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-10 text-left',
12702 click : function() {
12703 this.downloadCard(data.id)
12709 xns : Roo.bootstrap,
12711 style: 'max-height: 28px; ',
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' });
12751 this.getCardFooter().addClass('p-1');
12758 // dont' really need ot update items.
12759 // this.items.push(cn);
12760 this.fileCollection.add(cn);
12762 if (!data.srcfile) {
12763 this.updateInput();
12768 var reader = new FileReader();
12769 reader.addEventListener("load", function() {
12770 data.srcdata = reader.result;
12773 reader.readAsDataURL(data.srcfile);
12778 removeCard : function(id)
12781 var card = this.fileCollection.get(id);
12782 card.data.is_deleted = 1;
12783 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12784 //this.fileCollection.remove(card);
12785 //this.items = this.items.filter(function(e) { return e != card });
12786 // dont' really need ot update items.
12787 card.el.dom.parentNode.removeChild(card.el.dom);
12788 this.updateInput();
12794 this.fileCollection.each(function(card) {
12795 if (card.el.dom && card.el.dom.parentNode) {
12796 card.el.dom.parentNode.removeChild(card.el.dom);
12799 this.fileCollection.clear();
12800 this.updateInput();
12803 updateInput : function()
12806 this.fileCollection.each(function(e) {
12810 this.inputEl().dom.value = JSON.stringify(data);
12820 Roo.bootstrap.CardUploader.ID = -1;/*
12822 * Ext JS Library 1.1.1
12823 * Copyright(c) 2006-2007, Ext JS, LLC.
12825 * Originally Released Under LGPL - original licence link has changed is not relivant.
12828 * <script type="text/javascript">
12833 * @class Roo.data.SortTypes
12835 * Defines the default sorting (casting?) comparison functions used when sorting data.
12837 Roo.data.SortTypes = {
12839 * Default sort that does nothing
12840 * @param {Mixed} s The value being converted
12841 * @return {Mixed} The comparison value
12843 none : function(s){
12848 * The regular expression used to strip tags
12852 stripTagsRE : /<\/?[^>]+>/gi,
12855 * Strips all HTML tags to sort on text only
12856 * @param {Mixed} s The value being converted
12857 * @return {String} The comparison value
12859 asText : function(s){
12860 return String(s).replace(this.stripTagsRE, "");
12864 * Strips all HTML tags to sort on text only - Case insensitive
12865 * @param {Mixed} s The value being converted
12866 * @return {String} The comparison value
12868 asUCText : function(s){
12869 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12873 * Case insensitive string
12874 * @param {Mixed} s The value being converted
12875 * @return {String} The comparison value
12877 asUCString : function(s) {
12878 return String(s).toUpperCase();
12883 * @param {Mixed} s The value being converted
12884 * @return {Number} The comparison value
12886 asDate : function(s) {
12890 if(s instanceof Date){
12891 return s.getTime();
12893 return Date.parse(String(s));
12898 * @param {Mixed} s The value being converted
12899 * @return {Float} The comparison value
12901 asFloat : function(s) {
12902 var val = parseFloat(String(s).replace(/,/g, ""));
12911 * @param {Mixed} s The value being converted
12912 * @return {Number} The comparison value
12914 asInt : function(s) {
12915 var val = parseInt(String(s).replace(/,/g, ""));
12923 * Ext JS Library 1.1.1
12924 * Copyright(c) 2006-2007, Ext JS, LLC.
12926 * Originally Released Under LGPL - original licence link has changed is not relivant.
12929 * <script type="text/javascript">
12933 * @class Roo.data.Record
12934 * Instances of this class encapsulate both record <em>definition</em> information, and record
12935 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12936 * to access Records cached in an {@link Roo.data.Store} object.<br>
12938 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12939 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12942 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12944 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12945 * {@link #create}. The parameters are the same.
12946 * @param {Array} data An associative Array of data values keyed by the field name.
12947 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12948 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12949 * not specified an integer id is generated.
12951 Roo.data.Record = function(data, id){
12952 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12957 * Generate a constructor for a specific record layout.
12958 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12959 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12960 * Each field definition object may contain the following properties: <ul>
12961 * <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,
12962 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12963 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12964 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12965 * is being used, then this is a string containing the javascript expression to reference the data relative to
12966 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12967 * to the data item relative to the record element. If the mapping expression is the same as the field name,
12968 * this may be omitted.</p></li>
12969 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12970 * <ul><li>auto (Default, implies no conversion)</li>
12975 * <li>date</li></ul></p></li>
12976 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12977 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12978 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12979 * by the Reader into an object that will be stored in the Record. It is passed the
12980 * following parameters:<ul>
12981 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12983 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12985 * <br>usage:<br><pre><code>
12986 var TopicRecord = Roo.data.Record.create(
12987 {name: 'title', mapping: 'topic_title'},
12988 {name: 'author', mapping: 'username'},
12989 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12990 {name: 'lastPost', mapping: 'post_time', type: 'date'},
12991 {name: 'lastPoster', mapping: 'user2'},
12992 {name: 'excerpt', mapping: 'post_text'}
12995 var myNewRecord = new TopicRecord({
12996 title: 'Do my job please',
12999 lastPost: new Date(),
13000 lastPoster: 'Animal',
13001 excerpt: 'No way dude!'
13003 myStore.add(myNewRecord);
13008 Roo.data.Record.create = function(o){
13009 var f = function(){
13010 f.superclass.constructor.apply(this, arguments);
13012 Roo.extend(f, Roo.data.Record);
13013 var p = f.prototype;
13014 p.fields = new Roo.util.MixedCollection(false, function(field){
13017 for(var i = 0, len = o.length; i < len; i++){
13018 p.fields.add(new Roo.data.Field(o[i]));
13020 f.getField = function(name){
13021 return p.fields.get(name);
13026 Roo.data.Record.AUTO_ID = 1000;
13027 Roo.data.Record.EDIT = 'edit';
13028 Roo.data.Record.REJECT = 'reject';
13029 Roo.data.Record.COMMIT = 'commit';
13031 Roo.data.Record.prototype = {
13033 * Readonly flag - true if this record has been modified.
13042 join : function(store){
13043 this.store = store;
13047 * Set the named field to the specified value.
13048 * @param {String} name The name of the field to set.
13049 * @param {Object} value The value to set the field to.
13051 set : function(name, value){
13052 if(this.data[name] == value){
13056 if(!this.modified){
13057 this.modified = {};
13059 if(typeof this.modified[name] == 'undefined'){
13060 this.modified[name] = this.data[name];
13062 this.data[name] = value;
13063 if(!this.editing && this.store){
13064 this.store.afterEdit(this);
13069 * Get the value of the named field.
13070 * @param {String} name The name of the field to get the value of.
13071 * @return {Object} The value of the field.
13073 get : function(name){
13074 return this.data[name];
13078 beginEdit : function(){
13079 this.editing = true;
13080 this.modified = {};
13084 cancelEdit : function(){
13085 this.editing = false;
13086 delete this.modified;
13090 endEdit : function(){
13091 this.editing = false;
13092 if(this.dirty && this.store){
13093 this.store.afterEdit(this);
13098 * Usually called by the {@link Roo.data.Store} which owns the Record.
13099 * Rejects all changes made to the Record since either creation, or the last commit operation.
13100 * Modified fields are reverted to their original values.
13102 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13103 * of reject operations.
13105 reject : function(){
13106 var m = this.modified;
13108 if(typeof m[n] != "function"){
13109 this.data[n] = m[n];
13112 this.dirty = false;
13113 delete this.modified;
13114 this.editing = false;
13116 this.store.afterReject(this);
13121 * Usually called by the {@link Roo.data.Store} which owns the Record.
13122 * Commits all changes made to the Record since either creation, or the last commit operation.
13124 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13125 * of commit operations.
13127 commit : function(){
13128 this.dirty = false;
13129 delete this.modified;
13130 this.editing = false;
13132 this.store.afterCommit(this);
13137 hasError : function(){
13138 return this.error != null;
13142 clearError : function(){
13147 * Creates a copy of this record.
13148 * @param {String} id (optional) A new record id if you don't want to use this record's id
13151 copy : function(newId) {
13152 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13156 * Ext JS Library 1.1.1
13157 * Copyright(c) 2006-2007, Ext JS, LLC.
13159 * Originally Released Under LGPL - original licence link has changed is not relivant.
13162 * <script type="text/javascript">
13168 * @class Roo.data.Store
13169 * @extends Roo.util.Observable
13170 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13171 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13173 * 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
13174 * has no knowledge of the format of the data returned by the Proxy.<br>
13176 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13177 * instances from the data object. These records are cached and made available through accessor functions.
13179 * Creates a new Store.
13180 * @param {Object} config A config object containing the objects needed for the Store to access data,
13181 * and read the data into Records.
13183 Roo.data.Store = function(config){
13184 this.data = new Roo.util.MixedCollection(false);
13185 this.data.getKey = function(o){
13188 this.baseParams = {};
13190 this.paramNames = {
13195 "multisort" : "_multisort"
13198 if(config && config.data){
13199 this.inlineData = config.data;
13200 delete config.data;
13203 Roo.apply(this, config);
13205 if(this.reader){ // reader passed
13206 this.reader = Roo.factory(this.reader, Roo.data);
13207 this.reader.xmodule = this.xmodule || false;
13208 if(!this.recordType){
13209 this.recordType = this.reader.recordType;
13211 if(this.reader.onMetaChange){
13212 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13216 if(this.recordType){
13217 this.fields = this.recordType.prototype.fields;
13219 this.modified = [];
13223 * @event datachanged
13224 * Fires when the data cache has changed, and a widget which is using this Store
13225 * as a Record cache should refresh its view.
13226 * @param {Store} this
13228 datachanged : true,
13230 * @event metachange
13231 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13232 * @param {Store} this
13233 * @param {Object} meta The JSON metadata
13238 * Fires when Records have been added to the Store
13239 * @param {Store} this
13240 * @param {Roo.data.Record[]} records The array of Records added
13241 * @param {Number} index The index at which the record(s) were added
13246 * Fires when a Record has been removed from the Store
13247 * @param {Store} this
13248 * @param {Roo.data.Record} record The Record that was removed
13249 * @param {Number} index The index at which the record was removed
13254 * Fires when a Record has been updated
13255 * @param {Store} this
13256 * @param {Roo.data.Record} record The Record that was updated
13257 * @param {String} operation The update operation being performed. Value may be one of:
13259 Roo.data.Record.EDIT
13260 Roo.data.Record.REJECT
13261 Roo.data.Record.COMMIT
13267 * Fires when the data cache has been cleared.
13268 * @param {Store} this
13272 * @event beforeload
13273 * Fires before a request is made for a new data object. If the beforeload handler returns false
13274 * the load action will be canceled.
13275 * @param {Store} this
13276 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13280 * @event beforeloadadd
13281 * Fires after a new set of Records has been loaded.
13282 * @param {Store} this
13283 * @param {Roo.data.Record[]} records The Records that were loaded
13284 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13286 beforeloadadd : true,
13289 * Fires after a new set of Records has been loaded, before they are added to the store.
13290 * @param {Store} this
13291 * @param {Roo.data.Record[]} records The Records that were loaded
13292 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13293 * @params {Object} return from reader
13297 * @event loadexception
13298 * Fires if an exception occurs in the Proxy during loading.
13299 * Called with the signature of the Proxy's "loadexception" event.
13300 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13303 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13304 * @param {Object} load options
13305 * @param {Object} jsonData from your request (normally this contains the Exception)
13307 loadexception : true
13311 this.proxy = Roo.factory(this.proxy, Roo.data);
13312 this.proxy.xmodule = this.xmodule || false;
13313 this.relayEvents(this.proxy, ["loadexception"]);
13315 this.sortToggle = {};
13316 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13318 Roo.data.Store.superclass.constructor.call(this);
13320 if(this.inlineData){
13321 this.loadData(this.inlineData);
13322 delete this.inlineData;
13326 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13328 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13329 * without a remote query - used by combo/forms at present.
13333 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13336 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13339 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13340 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13343 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13344 * on any HTTP request
13347 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13350 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13354 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13355 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13357 remoteSort : false,
13360 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13361 * loaded or when a record is removed. (defaults to false).
13363 pruneModifiedRecords : false,
13366 lastOptions : null,
13369 * Add Records to the Store and fires the add event.
13370 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13372 add : function(records){
13373 records = [].concat(records);
13374 for(var i = 0, len = records.length; i < len; i++){
13375 records[i].join(this);
13377 var index = this.data.length;
13378 this.data.addAll(records);
13379 this.fireEvent("add", this, records, index);
13383 * Remove a Record from the Store and fires the remove event.
13384 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13386 remove : function(record){
13387 var index = this.data.indexOf(record);
13388 this.data.removeAt(index);
13390 if(this.pruneModifiedRecords){
13391 this.modified.remove(record);
13393 this.fireEvent("remove", this, record, index);
13397 * Remove all Records from the Store and fires the clear event.
13399 removeAll : function(){
13401 if(this.pruneModifiedRecords){
13402 this.modified = [];
13404 this.fireEvent("clear", this);
13408 * Inserts Records to the Store at the given index and fires the add event.
13409 * @param {Number} index The start index at which to insert the passed Records.
13410 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13412 insert : function(index, records){
13413 records = [].concat(records);
13414 for(var i = 0, len = records.length; i < len; i++){
13415 this.data.insert(index, records[i]);
13416 records[i].join(this);
13418 this.fireEvent("add", this, records, index);
13422 * Get the index within the cache of the passed Record.
13423 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13424 * @return {Number} The index of the passed Record. Returns -1 if not found.
13426 indexOf : function(record){
13427 return this.data.indexOf(record);
13431 * Get the index within the cache of the Record with the passed id.
13432 * @param {String} id The id of the Record to find.
13433 * @return {Number} The index of the Record. Returns -1 if not found.
13435 indexOfId : function(id){
13436 return this.data.indexOfKey(id);
13440 * Get the Record with the specified id.
13441 * @param {String} id The id of the Record to find.
13442 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13444 getById : function(id){
13445 return this.data.key(id);
13449 * Get the Record at the specified index.
13450 * @param {Number} index The index of the Record to find.
13451 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13453 getAt : function(index){
13454 return this.data.itemAt(index);
13458 * Returns a range of Records between specified indices.
13459 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13460 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13461 * @return {Roo.data.Record[]} An array of Records
13463 getRange : function(start, end){
13464 return this.data.getRange(start, end);
13468 storeOptions : function(o){
13469 o = Roo.apply({}, o);
13472 this.lastOptions = o;
13476 * Loads the Record cache from the configured Proxy using the configured Reader.
13478 * If using remote paging, then the first load call must specify the <em>start</em>
13479 * and <em>limit</em> properties in the options.params property to establish the initial
13480 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13482 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13483 * and this call will return before the new data has been loaded. Perform any post-processing
13484 * in a callback function, or in a "load" event handler.</strong>
13486 * @param {Object} options An object containing properties which control loading options:<ul>
13487 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13488 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13489 * passed the following arguments:<ul>
13490 * <li>r : Roo.data.Record[]</li>
13491 * <li>options: Options object from the load call</li>
13492 * <li>success: Boolean success indicator</li></ul></li>
13493 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13494 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13497 load : function(options){
13498 options = options || {};
13499 if(this.fireEvent("beforeload", this, options) !== false){
13500 this.storeOptions(options);
13501 var p = Roo.apply(options.params || {}, this.baseParams);
13502 // if meta was not loaded from remote source.. try requesting it.
13503 if (!this.reader.metaFromRemote) {
13504 p._requestMeta = 1;
13506 if(this.sortInfo && this.remoteSort){
13507 var pn = this.paramNames;
13508 p[pn["sort"]] = this.sortInfo.field;
13509 p[pn["dir"]] = this.sortInfo.direction;
13511 if (this.multiSort) {
13512 var pn = this.paramNames;
13513 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13516 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13521 * Reloads the Record cache from the configured Proxy using the configured Reader and
13522 * the options from the last load operation performed.
13523 * @param {Object} options (optional) An object containing properties which may override the options
13524 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13525 * the most recently used options are reused).
13527 reload : function(options){
13528 this.load(Roo.applyIf(options||{}, this.lastOptions));
13532 // Called as a callback by the Reader during a load operation.
13533 loadRecords : function(o, options, success){
13534 if(!o || success === false){
13535 if(success !== false){
13536 this.fireEvent("load", this, [], options, o);
13538 if(options.callback){
13539 options.callback.call(options.scope || this, [], options, false);
13543 // if data returned failure - throw an exception.
13544 if (o.success === false) {
13545 // show a message if no listener is registered.
13546 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13547 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13549 // loadmask wil be hooked into this..
13550 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13553 var r = o.records, t = o.totalRecords || r.length;
13555 this.fireEvent("beforeloadadd", this, r, options, o);
13557 if(!options || options.add !== true){
13558 if(this.pruneModifiedRecords){
13559 this.modified = [];
13561 for(var i = 0, len = r.length; i < len; i++){
13565 this.data = this.snapshot;
13566 delete this.snapshot;
13569 this.data.addAll(r);
13570 this.totalLength = t;
13572 this.fireEvent("datachanged", this);
13574 this.totalLength = Math.max(t, this.data.length+r.length);
13578 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13580 var e = new Roo.data.Record({});
13582 e.set(this.parent.displayField, this.parent.emptyTitle);
13583 e.set(this.parent.valueField, '');
13588 this.fireEvent("load", this, r, options, o);
13589 if(options.callback){
13590 options.callback.call(options.scope || this, r, options, true);
13596 * Loads data from a passed data block. A Reader which understands the format of the data
13597 * must have been configured in the constructor.
13598 * @param {Object} data The data block from which to read the Records. The format of the data expected
13599 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13600 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13602 loadData : function(o, append){
13603 var r = this.reader.readRecords(o);
13604 this.loadRecords(r, {add: append}, true);
13608 * using 'cn' the nested child reader read the child array into it's child stores.
13609 * @param {Object} rec The record with a 'children array
13611 loadDataFromChildren : function(rec)
13613 this.loadData(this.reader.toLoadData(rec));
13618 * Gets the number of cached records.
13620 * <em>If using paging, this may not be the total size of the dataset. If the data object
13621 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13622 * the data set size</em>
13624 getCount : function(){
13625 return this.data.length || 0;
13629 * Gets the total number of records in the dataset as returned by the server.
13631 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13632 * the dataset size</em>
13634 getTotalCount : function(){
13635 return this.totalLength || 0;
13639 * Returns the sort state of the Store as an object with two properties:
13641 field {String} The name of the field by which the Records are sorted
13642 direction {String} The sort order, "ASC" or "DESC"
13645 getSortState : function(){
13646 return this.sortInfo;
13650 applySort : function(){
13651 if(this.sortInfo && !this.remoteSort){
13652 var s = this.sortInfo, f = s.field;
13653 var st = this.fields.get(f).sortType;
13654 var fn = function(r1, r2){
13655 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13656 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13658 this.data.sort(s.direction, fn);
13659 if(this.snapshot && this.snapshot != this.data){
13660 this.snapshot.sort(s.direction, fn);
13666 * Sets the default sort column and order to be used by the next load operation.
13667 * @param {String} fieldName The name of the field to sort by.
13668 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13670 setDefaultSort : function(field, dir){
13671 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13675 * Sort the Records.
13676 * If remote sorting is used, the sort is performed on the server, and the cache is
13677 * reloaded. If local sorting is used, the cache is sorted internally.
13678 * @param {String} fieldName The name of the field to sort by.
13679 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13681 sort : function(fieldName, dir){
13682 var f = this.fields.get(fieldName);
13684 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13686 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13687 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13692 this.sortToggle[f.name] = dir;
13693 this.sortInfo = {field: f.name, direction: dir};
13694 if(!this.remoteSort){
13696 this.fireEvent("datachanged", this);
13698 this.load(this.lastOptions);
13703 * Calls the specified function for each of the Records in the cache.
13704 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13705 * Returning <em>false</em> aborts and exits the iteration.
13706 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13708 each : function(fn, scope){
13709 this.data.each(fn, scope);
13713 * Gets all records modified since the last commit. Modified records are persisted across load operations
13714 * (e.g., during paging).
13715 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13717 getModifiedRecords : function(){
13718 return this.modified;
13722 createFilterFn : function(property, value, anyMatch){
13723 if(!value.exec){ // not a regex
13724 value = String(value);
13725 if(value.length == 0){
13728 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13730 return function(r){
13731 return value.test(r.data[property]);
13736 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13737 * @param {String} property A field on your records
13738 * @param {Number} start The record index to start at (defaults to 0)
13739 * @param {Number} end The last record index to include (defaults to length - 1)
13740 * @return {Number} The sum
13742 sum : function(property, start, end){
13743 var rs = this.data.items, v = 0;
13744 start = start || 0;
13745 end = (end || end === 0) ? end : rs.length-1;
13747 for(var i = start; i <= end; i++){
13748 v += (rs[i].data[property] || 0);
13754 * Filter the records by a specified property.
13755 * @param {String} field A field on your records
13756 * @param {String/RegExp} value Either a string that the field
13757 * should start with or a RegExp to test against the field
13758 * @param {Boolean} anyMatch True to match any part not just the beginning
13760 filter : function(property, value, anyMatch){
13761 var fn = this.createFilterFn(property, value, anyMatch);
13762 return fn ? this.filterBy(fn) : this.clearFilter();
13766 * Filter by a function. The specified function will be called with each
13767 * record in this data source. If the function returns true the record is included,
13768 * otherwise it is filtered.
13769 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13770 * @param {Object} scope (optional) The scope of the function (defaults to this)
13772 filterBy : function(fn, scope){
13773 this.snapshot = this.snapshot || this.data;
13774 this.data = this.queryBy(fn, scope||this);
13775 this.fireEvent("datachanged", this);
13779 * Query the records by a specified property.
13780 * @param {String} field A field on your records
13781 * @param {String/RegExp} value Either a string that the field
13782 * should start with or a RegExp to test against the field
13783 * @param {Boolean} anyMatch True to match any part not just the beginning
13784 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13786 query : function(property, value, anyMatch){
13787 var fn = this.createFilterFn(property, value, anyMatch);
13788 return fn ? this.queryBy(fn) : this.data.clone();
13792 * Query by a function. The specified function will be called with each
13793 * record in this data source. If the function returns true the record is included
13795 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13796 * @param {Object} scope (optional) The scope of the function (defaults to this)
13797 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13799 queryBy : function(fn, scope){
13800 var data = this.snapshot || this.data;
13801 return data.filterBy(fn, scope||this);
13805 * Collects unique values for a particular dataIndex from this store.
13806 * @param {String} dataIndex The property to collect
13807 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13808 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13809 * @return {Array} An array of the unique values
13811 collect : function(dataIndex, allowNull, bypassFilter){
13812 var d = (bypassFilter === true && this.snapshot) ?
13813 this.snapshot.items : this.data.items;
13814 var v, sv, r = [], l = {};
13815 for(var i = 0, len = d.length; i < len; i++){
13816 v = d[i].data[dataIndex];
13818 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13827 * Revert to a view of the Record cache with no filtering applied.
13828 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13830 clearFilter : function(suppressEvent){
13831 if(this.snapshot && this.snapshot != this.data){
13832 this.data = this.snapshot;
13833 delete this.snapshot;
13834 if(suppressEvent !== true){
13835 this.fireEvent("datachanged", this);
13841 afterEdit : function(record){
13842 if(this.modified.indexOf(record) == -1){
13843 this.modified.push(record);
13845 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13849 afterReject : function(record){
13850 this.modified.remove(record);
13851 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13855 afterCommit : function(record){
13856 this.modified.remove(record);
13857 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13861 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13862 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13864 commitChanges : function(){
13865 var m = this.modified.slice(0);
13866 this.modified = [];
13867 for(var i = 0, len = m.length; i < len; i++){
13873 * Cancel outstanding changes on all changed records.
13875 rejectChanges : function(){
13876 var m = this.modified.slice(0);
13877 this.modified = [];
13878 for(var i = 0, len = m.length; i < len; i++){
13883 onMetaChange : function(meta, rtype, o){
13884 this.recordType = rtype;
13885 this.fields = rtype.prototype.fields;
13886 delete this.snapshot;
13887 this.sortInfo = meta.sortInfo || this.sortInfo;
13888 this.modified = [];
13889 this.fireEvent('metachange', this, this.reader.meta);
13892 moveIndex : function(data, type)
13894 var index = this.indexOf(data);
13896 var newIndex = index + type;
13900 this.insert(newIndex, data);
13905 * Ext JS Library 1.1.1
13906 * Copyright(c) 2006-2007, Ext JS, LLC.
13908 * Originally Released Under LGPL - original licence link has changed is not relivant.
13911 * <script type="text/javascript">
13915 * @class Roo.data.SimpleStore
13916 * @extends Roo.data.Store
13917 * Small helper class to make creating Stores from Array data easier.
13918 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13919 * @cfg {Array} fields An array of field definition objects, or field name strings.
13920 * @cfg {Object} an existing reader (eg. copied from another store)
13921 * @cfg {Array} data The multi-dimensional array of data
13923 * @param {Object} config
13925 Roo.data.SimpleStore = function(config)
13927 Roo.data.SimpleStore.superclass.constructor.call(this, {
13929 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13932 Roo.data.Record.create(config.fields)
13934 proxy : new Roo.data.MemoryProxy(config.data)
13938 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13940 * Ext JS Library 1.1.1
13941 * Copyright(c) 2006-2007, Ext JS, LLC.
13943 * Originally Released Under LGPL - original licence link has changed is not relivant.
13946 * <script type="text/javascript">
13951 * @extends Roo.data.Store
13952 * @class Roo.data.JsonStore
13953 * Small helper class to make creating Stores for JSON data easier. <br/>
13955 var store = new Roo.data.JsonStore({
13956 url: 'get-images.php',
13958 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13961 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13962 * JsonReader and HttpProxy (unless inline data is provided).</b>
13963 * @cfg {Array} fields An array of field definition objects, or field name strings.
13965 * @param {Object} config
13967 Roo.data.JsonStore = function(c){
13968 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13969 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13970 reader: new Roo.data.JsonReader(c, c.fields)
13973 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13975 * Ext JS Library 1.1.1
13976 * Copyright(c) 2006-2007, Ext JS, LLC.
13978 * Originally Released Under LGPL - original licence link has changed is not relivant.
13981 * <script type="text/javascript">
13985 Roo.data.Field = function(config){
13986 if(typeof config == "string"){
13987 config = {name: config};
13989 Roo.apply(this, config);
13992 this.type = "auto";
13995 var st = Roo.data.SortTypes;
13996 // named sortTypes are supported, here we look them up
13997 if(typeof this.sortType == "string"){
13998 this.sortType = st[this.sortType];
14001 // set default sortType for strings and dates
14002 if(!this.sortType){
14005 this.sortType = st.asUCString;
14008 this.sortType = st.asDate;
14011 this.sortType = st.none;
14016 var stripRe = /[\$,%]/g;
14018 // prebuilt conversion function for this field, instead of
14019 // switching every time we're reading a value
14021 var cv, dateFormat = this.dateFormat;
14026 cv = function(v){ return v; };
14029 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14033 return v !== undefined && v !== null && v !== '' ?
14034 parseInt(String(v).replace(stripRe, ""), 10) : '';
14039 return v !== undefined && v !== null && v !== '' ?
14040 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14045 cv = function(v){ return v === true || v === "true" || v == 1; };
14052 if(v instanceof Date){
14056 if(dateFormat == "timestamp"){
14057 return new Date(v*1000);
14059 return Date.parseDate(v, dateFormat);
14061 var parsed = Date.parse(v);
14062 return parsed ? new Date(parsed) : null;
14071 Roo.data.Field.prototype = {
14079 * Ext JS Library 1.1.1
14080 * Copyright(c) 2006-2007, Ext JS, LLC.
14082 * Originally Released Under LGPL - original licence link has changed is not relivant.
14085 * <script type="text/javascript">
14088 // Base class for reading structured data from a data source. This class is intended to be
14089 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14092 * @class Roo.data.DataReader
14093 * Base class for reading structured data from a data source. This class is intended to be
14094 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14097 Roo.data.DataReader = function(meta, recordType){
14101 this.recordType = recordType instanceof Array ?
14102 Roo.data.Record.create(recordType) : recordType;
14105 Roo.data.DataReader.prototype = {
14108 readerType : 'Data',
14110 * Create an empty record
14111 * @param {Object} data (optional) - overlay some values
14112 * @return {Roo.data.Record} record created.
14114 newRow : function(d) {
14116 this.recordType.prototype.fields.each(function(c) {
14118 case 'int' : da[c.name] = 0; break;
14119 case 'date' : da[c.name] = new Date(); break;
14120 case 'float' : da[c.name] = 0.0; break;
14121 case 'boolean' : da[c.name] = false; break;
14122 default : da[c.name] = ""; break;
14126 return new this.recordType(Roo.apply(da, d));
14132 * Ext JS Library 1.1.1
14133 * Copyright(c) 2006-2007, Ext JS, LLC.
14135 * Originally Released Under LGPL - original licence link has changed is not relivant.
14138 * <script type="text/javascript">
14142 * @class Roo.data.DataProxy
14143 * @extends Roo.data.Observable
14144 * This class is an abstract base class for implementations which provide retrieval of
14145 * unformatted data objects.<br>
14147 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14148 * (of the appropriate type which knows how to parse the data object) to provide a block of
14149 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14151 * Custom implementations must implement the load method as described in
14152 * {@link Roo.data.HttpProxy#load}.
14154 Roo.data.DataProxy = function(){
14157 * @event beforeload
14158 * Fires before a network request is made to retrieve a data object.
14159 * @param {Object} This DataProxy object.
14160 * @param {Object} params The params parameter to the load function.
14165 * Fires before the load method's callback is called.
14166 * @param {Object} This DataProxy object.
14167 * @param {Object} o The data object.
14168 * @param {Object} arg The callback argument object passed to the load function.
14172 * @event loadexception
14173 * Fires if an Exception occurs during data retrieval.
14174 * @param {Object} This DataProxy object.
14175 * @param {Object} o The data object.
14176 * @param {Object} arg The callback argument object passed to the load function.
14177 * @param {Object} e The Exception.
14179 loadexception : true
14181 Roo.data.DataProxy.superclass.constructor.call(this);
14184 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14187 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14191 * Ext JS Library 1.1.1
14192 * Copyright(c) 2006-2007, Ext JS, LLC.
14194 * Originally Released Under LGPL - original licence link has changed is not relivant.
14197 * <script type="text/javascript">
14200 * @class Roo.data.MemoryProxy
14201 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14202 * to the Reader when its load method is called.
14204 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14206 Roo.data.MemoryProxy = function(data){
14210 Roo.data.MemoryProxy.superclass.constructor.call(this);
14214 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14217 * Load data from the requested source (in this case an in-memory
14218 * data object passed to the constructor), read the data object into
14219 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14220 * process that block using the passed callback.
14221 * @param {Object} params This parameter is not used by the MemoryProxy class.
14222 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14223 * object into a block of Roo.data.Records.
14224 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14225 * The function must be passed <ul>
14226 * <li>The Record block object</li>
14227 * <li>The "arg" argument from the load function</li>
14228 * <li>A boolean success indicator</li>
14230 * @param {Object} scope The scope in which to call the callback
14231 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14233 load : function(params, reader, callback, scope, arg){
14234 params = params || {};
14237 result = reader.readRecords(params.data ? params.data :this.data);
14239 this.fireEvent("loadexception", this, arg, null, e);
14240 callback.call(scope, null, arg, false);
14243 callback.call(scope, result, arg, true);
14247 update : function(params, records){
14252 * Ext JS Library 1.1.1
14253 * Copyright(c) 2006-2007, Ext JS, LLC.
14255 * Originally Released Under LGPL - original licence link has changed is not relivant.
14258 * <script type="text/javascript">
14261 * @class Roo.data.HttpProxy
14262 * @extends Roo.data.DataProxy
14263 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14264 * configured to reference a certain URL.<br><br>
14266 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14267 * from which the running page was served.<br><br>
14269 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14271 * Be aware that to enable the browser to parse an XML document, the server must set
14272 * the Content-Type header in the HTTP response to "text/xml".
14274 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14275 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14276 * will be used to make the request.
14278 Roo.data.HttpProxy = function(conn){
14279 Roo.data.HttpProxy.superclass.constructor.call(this);
14280 // is conn a conn config or a real conn?
14282 this.useAjax = !conn || !conn.events;
14286 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14287 // thse are take from connection...
14290 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14293 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14294 * extra parameters to each request made by this object. (defaults to undefined)
14297 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14298 * to each request made by this object. (defaults to undefined)
14301 * @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)
14304 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14307 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14313 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14317 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14318 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14319 * a finer-grained basis than the DataProxy events.
14321 getConnection : function(){
14322 return this.useAjax ? Roo.Ajax : this.conn;
14326 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14327 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14328 * process that block using the passed callback.
14329 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14330 * for the request to the remote server.
14331 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14332 * object into a block of Roo.data.Records.
14333 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14334 * The function must be passed <ul>
14335 * <li>The Record block object</li>
14336 * <li>The "arg" argument from the load function</li>
14337 * <li>A boolean success indicator</li>
14339 * @param {Object} scope The scope in which to call the callback
14340 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14342 load : function(params, reader, callback, scope, arg){
14343 if(this.fireEvent("beforeload", this, params) !== false){
14345 params : params || {},
14347 callback : callback,
14352 callback : this.loadResponse,
14356 Roo.applyIf(o, this.conn);
14357 if(this.activeRequest){
14358 Roo.Ajax.abort(this.activeRequest);
14360 this.activeRequest = Roo.Ajax.request(o);
14362 this.conn.request(o);
14365 callback.call(scope||this, null, arg, false);
14370 loadResponse : function(o, success, response){
14371 delete this.activeRequest;
14373 this.fireEvent("loadexception", this, o, response);
14374 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14379 result = o.reader.read(response);
14381 this.fireEvent("loadexception", this, o, response, e);
14382 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14386 this.fireEvent("load", this, o, o.request.arg);
14387 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14391 update : function(dataSet){
14396 updateResponse : function(dataSet){
14401 * Ext JS Library 1.1.1
14402 * Copyright(c) 2006-2007, Ext JS, LLC.
14404 * Originally Released Under LGPL - original licence link has changed is not relivant.
14407 * <script type="text/javascript">
14411 * @class Roo.data.ScriptTagProxy
14412 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14413 * other than the originating domain of the running page.<br><br>
14415 * <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
14416 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14418 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14419 * source code that is used as the source inside a <script> tag.<br><br>
14421 * In order for the browser to process the returned data, the server must wrap the data object
14422 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14423 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14424 * depending on whether the callback name was passed:
14427 boolean scriptTag = false;
14428 String cb = request.getParameter("callback");
14431 response.setContentType("text/javascript");
14433 response.setContentType("application/x-json");
14435 Writer out = response.getWriter();
14437 out.write(cb + "(");
14439 out.print(dataBlock.toJsonString());
14446 * @param {Object} config A configuration object.
14448 Roo.data.ScriptTagProxy = function(config){
14449 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14450 Roo.apply(this, config);
14451 this.head = document.getElementsByTagName("head")[0];
14454 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14456 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14458 * @cfg {String} url The URL from which to request the data object.
14461 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14465 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14466 * the server the name of the callback function set up by the load call to process the returned data object.
14467 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14468 * javascript output which calls this named function passing the data object as its only parameter.
14470 callbackParam : "callback",
14472 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14473 * name to the request.
14478 * Load data from the configured URL, read the data object into
14479 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14480 * process that block using the passed callback.
14481 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14482 * for the request to the remote server.
14483 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14484 * object into a block of Roo.data.Records.
14485 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14486 * The function must be passed <ul>
14487 * <li>The Record block object</li>
14488 * <li>The "arg" argument from the load function</li>
14489 * <li>A boolean success indicator</li>
14491 * @param {Object} scope The scope in which to call the callback
14492 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14494 load : function(params, reader, callback, scope, arg){
14495 if(this.fireEvent("beforeload", this, params) !== false){
14497 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14499 var url = this.url;
14500 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14502 url += "&_dc=" + (new Date().getTime());
14504 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14507 cb : "stcCallback"+transId,
14508 scriptId : "stcScript"+transId,
14512 callback : callback,
14518 window[trans.cb] = function(o){
14519 conn.handleResponse(o, trans);
14522 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14524 if(this.autoAbort !== false){
14528 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14530 var script = document.createElement("script");
14531 script.setAttribute("src", url);
14532 script.setAttribute("type", "text/javascript");
14533 script.setAttribute("id", trans.scriptId);
14534 this.head.appendChild(script);
14536 this.trans = trans;
14538 callback.call(scope||this, null, arg, false);
14543 isLoading : function(){
14544 return this.trans ? true : false;
14548 * Abort the current server request.
14550 abort : function(){
14551 if(this.isLoading()){
14552 this.destroyTrans(this.trans);
14557 destroyTrans : function(trans, isLoaded){
14558 this.head.removeChild(document.getElementById(trans.scriptId));
14559 clearTimeout(trans.timeoutId);
14561 window[trans.cb] = undefined;
14563 delete window[trans.cb];
14566 // if hasn't been loaded, wait for load to remove it to prevent script error
14567 window[trans.cb] = function(){
14568 window[trans.cb] = undefined;
14570 delete window[trans.cb];
14577 handleResponse : function(o, trans){
14578 this.trans = false;
14579 this.destroyTrans(trans, true);
14582 result = trans.reader.readRecords(o);
14584 this.fireEvent("loadexception", this, o, trans.arg, e);
14585 trans.callback.call(trans.scope||window, null, trans.arg, false);
14588 this.fireEvent("load", this, o, trans.arg);
14589 trans.callback.call(trans.scope||window, result, trans.arg, true);
14593 handleFailure : function(trans){
14594 this.trans = false;
14595 this.destroyTrans(trans, false);
14596 this.fireEvent("loadexception", this, null, trans.arg);
14597 trans.callback.call(trans.scope||window, null, trans.arg, false);
14601 * Ext JS Library 1.1.1
14602 * Copyright(c) 2006-2007, Ext JS, LLC.
14604 * Originally Released Under LGPL - original licence link has changed is not relivant.
14607 * <script type="text/javascript">
14611 * @class Roo.data.JsonReader
14612 * @extends Roo.data.DataReader
14613 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14614 * based on mappings in a provided Roo.data.Record constructor.
14616 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14617 * in the reply previously.
14622 var RecordDef = Roo.data.Record.create([
14623 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14624 {name: 'occupation'} // This field will use "occupation" as the mapping.
14626 var myReader = new Roo.data.JsonReader({
14627 totalProperty: "results", // The property which contains the total dataset size (optional)
14628 root: "rows", // The property which contains an Array of row objects
14629 id: "id" // The property within each row object that provides an ID for the record (optional)
14633 * This would consume a JSON file like this:
14635 { 'results': 2, 'rows': [
14636 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14637 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14640 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14641 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14642 * paged from the remote server.
14643 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14644 * @cfg {String} root name of the property which contains the Array of row objects.
14645 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14646 * @cfg {Array} fields Array of field definition objects
14648 * Create a new JsonReader
14649 * @param {Object} meta Metadata configuration options
14650 * @param {Object} recordType Either an Array of field definition objects,
14651 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14653 Roo.data.JsonReader = function(meta, recordType){
14656 // set some defaults:
14657 Roo.applyIf(meta, {
14658 totalProperty: 'total',
14659 successProperty : 'success',
14664 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14666 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14668 readerType : 'Json',
14671 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14672 * Used by Store query builder to append _requestMeta to params.
14675 metaFromRemote : false,
14677 * This method is only used by a DataProxy which has retrieved data from a remote server.
14678 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14679 * @return {Object} data A data block which is used by an Roo.data.Store object as
14680 * a cache of Roo.data.Records.
14682 read : function(response){
14683 var json = response.responseText;
14685 var o = /* eval:var:o */ eval("("+json+")");
14687 throw {message: "JsonReader.read: Json object not found"};
14693 this.metaFromRemote = true;
14694 this.meta = o.metaData;
14695 this.recordType = Roo.data.Record.create(o.metaData.fields);
14696 this.onMetaChange(this.meta, this.recordType, o);
14698 return this.readRecords(o);
14701 // private function a store will implement
14702 onMetaChange : function(meta, recordType, o){
14709 simpleAccess: function(obj, subsc) {
14716 getJsonAccessor: function(){
14718 return function(expr) {
14720 return(re.test(expr))
14721 ? new Function("obj", "return obj." + expr)
14726 return Roo.emptyFn;
14731 * Create a data block containing Roo.data.Records from an XML document.
14732 * @param {Object} o An object which contains an Array of row objects in the property specified
14733 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14734 * which contains the total size of the dataset.
14735 * @return {Object} data A data block which is used by an Roo.data.Store object as
14736 * a cache of Roo.data.Records.
14738 readRecords : function(o){
14740 * After any data loads, the raw JSON data is available for further custom processing.
14744 var s = this.meta, Record = this.recordType,
14745 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14747 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14749 if(s.totalProperty) {
14750 this.getTotal = this.getJsonAccessor(s.totalProperty);
14752 if(s.successProperty) {
14753 this.getSuccess = this.getJsonAccessor(s.successProperty);
14755 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14757 var g = this.getJsonAccessor(s.id);
14758 this.getId = function(rec) {
14760 return (r === undefined || r === "") ? null : r;
14763 this.getId = function(){return null;};
14766 for(var jj = 0; jj < fl; jj++){
14768 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14769 this.ef[jj] = this.getJsonAccessor(map);
14773 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14774 if(s.totalProperty){
14775 var vt = parseInt(this.getTotal(o), 10);
14780 if(s.successProperty){
14781 var vs = this.getSuccess(o);
14782 if(vs === false || vs === 'false'){
14787 for(var i = 0; i < c; i++){
14790 var id = this.getId(n);
14791 for(var j = 0; j < fl; j++){
14793 var v = this.ef[j](n);
14795 Roo.log('missing convert for ' + f.name);
14799 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14801 var record = new Record(values, id);
14803 records[i] = record;
14809 totalRecords : totalRecords
14812 // used when loading children.. @see loadDataFromChildren
14813 toLoadData: function(rec)
14815 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14816 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14817 return { data : data, total : data.length };
14822 * Ext JS Library 1.1.1
14823 * Copyright(c) 2006-2007, Ext JS, LLC.
14825 * Originally Released Under LGPL - original licence link has changed is not relivant.
14828 * <script type="text/javascript">
14832 * @class Roo.data.ArrayReader
14833 * @extends Roo.data.DataReader
14834 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14835 * Each element of that Array represents a row of data fields. The
14836 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14837 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14841 var RecordDef = Roo.data.Record.create([
14842 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14843 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14845 var myReader = new Roo.data.ArrayReader({
14846 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14850 * This would consume an Array like this:
14852 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14856 * Create a new JsonReader
14857 * @param {Object} meta Metadata configuration options.
14858 * @param {Object|Array} recordType Either an Array of field definition objects
14860 * @cfg {Array} fields Array of field definition objects
14861 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14862 * as specified to {@link Roo.data.Record#create},
14863 * or an {@link Roo.data.Record} object
14866 * created using {@link Roo.data.Record#create}.
14868 Roo.data.ArrayReader = function(meta, recordType)
14870 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14873 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14876 * Create a data block containing Roo.data.Records from an XML document.
14877 * @param {Object} o An Array of row objects which represents the dataset.
14878 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14879 * a cache of Roo.data.Records.
14881 readRecords : function(o)
14883 var sid = this.meta ? this.meta.id : null;
14884 var recordType = this.recordType, fields = recordType.prototype.fields;
14887 for(var i = 0; i < root.length; i++){
14890 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14891 for(var j = 0, jlen = fields.length; j < jlen; j++){
14892 var f = fields.items[j];
14893 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14894 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14896 values[f.name] = v;
14898 var record = new recordType(values, id);
14900 records[records.length] = record;
14904 totalRecords : records.length
14907 // used when loading children.. @see loadDataFromChildren
14908 toLoadData: function(rec)
14910 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14911 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14922 * @class Roo.bootstrap.ComboBox
14923 * @extends Roo.bootstrap.TriggerField
14924 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14925 * @cfg {Boolean} append (true|false) default false
14926 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14927 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14928 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14929 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14930 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14931 * @cfg {Boolean} animate default true
14932 * @cfg {Boolean} emptyResultText only for touch device
14933 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14934 * @cfg {String} emptyTitle default ''
14935 * @cfg {Number} width fixed with? experimental
14937 * Create a new ComboBox.
14938 * @param {Object} config Configuration options
14940 Roo.bootstrap.ComboBox = function(config){
14941 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14945 * Fires when the dropdown list is expanded
14946 * @param {Roo.bootstrap.ComboBox} combo This combo box
14951 * Fires when the dropdown list is collapsed
14952 * @param {Roo.bootstrap.ComboBox} combo This combo box
14956 * @event beforeselect
14957 * Fires before a list item is selected. Return false to cancel the selection.
14958 * @param {Roo.bootstrap.ComboBox} combo This combo box
14959 * @param {Roo.data.Record} record The data record returned from the underlying store
14960 * @param {Number} index The index of the selected item in the dropdown list
14962 'beforeselect' : true,
14965 * Fires when a list item is selected
14966 * @param {Roo.bootstrap.ComboBox} combo This combo box
14967 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14968 * @param {Number} index The index of the selected item in the dropdown list
14972 * @event beforequery
14973 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14974 * The event object passed has these properties:
14975 * @param {Roo.bootstrap.ComboBox} combo This combo box
14976 * @param {String} query The query
14977 * @param {Boolean} forceAll true to force "all" query
14978 * @param {Boolean} cancel true to cancel the query
14979 * @param {Object} e The query event object
14981 'beforequery': true,
14984 * Fires when the 'add' icon is pressed (add a listener to enable add button)
14985 * @param {Roo.bootstrap.ComboBox} combo This combo box
14990 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14991 * @param {Roo.bootstrap.ComboBox} combo This combo box
14992 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14997 * Fires when the remove value from the combobox array
14998 * @param {Roo.bootstrap.ComboBox} combo This combo box
15002 * @event afterremove
15003 * Fires when the remove value from the combobox array
15004 * @param {Roo.bootstrap.ComboBox} combo This combo box
15006 'afterremove' : true,
15008 * @event specialfilter
15009 * Fires when specialfilter
15010 * @param {Roo.bootstrap.ComboBox} combo This combo box
15012 'specialfilter' : true,
15015 * Fires when tick the element
15016 * @param {Roo.bootstrap.ComboBox} combo This combo box
15020 * @event touchviewdisplay
15021 * Fires when touch view require special display (default is using displayField)
15022 * @param {Roo.bootstrap.ComboBox} combo This combo box
15023 * @param {Object} cfg set html .
15025 'touchviewdisplay' : true
15030 this.tickItems = [];
15032 this.selectedIndex = -1;
15033 if(this.mode == 'local'){
15034 if(config.queryDelay === undefined){
15035 this.queryDelay = 10;
15037 if(config.minChars === undefined){
15043 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15046 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15047 * rendering into an Roo.Editor, defaults to false)
15050 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15051 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15054 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15057 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15058 * the dropdown list (defaults to undefined, with no header element)
15062 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15066 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15068 listWidth: undefined,
15070 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15071 * mode = 'remote' or 'text' if mode = 'local')
15073 displayField: undefined,
15076 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15077 * mode = 'remote' or 'value' if mode = 'local').
15078 * Note: use of a valueField requires the user make a selection
15079 * in order for a value to be mapped.
15081 valueField: undefined,
15083 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15088 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15089 * field's data value (defaults to the underlying DOM element's name)
15091 hiddenName: undefined,
15093 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15097 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15099 selectedClass: 'active',
15102 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15106 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15107 * anchor positions (defaults to 'tl-bl')
15109 listAlign: 'tl-bl?',
15111 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15115 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15116 * query specified by the allQuery config option (defaults to 'query')
15118 triggerAction: 'query',
15120 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15121 * (defaults to 4, does not apply if editable = false)
15125 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15126 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15130 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15131 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15135 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15136 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15140 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15141 * when editable = true (defaults to false)
15143 selectOnFocus:false,
15145 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15147 queryParam: 'query',
15149 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15150 * when mode = 'remote' (defaults to 'Loading...')
15152 loadingText: 'Loading...',
15154 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15158 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15162 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15163 * traditional select (defaults to true)
15167 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15171 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15175 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15176 * listWidth has a higher value)
15180 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15181 * allow the user to set arbitrary text into the field (defaults to false)
15183 forceSelection:false,
15185 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15186 * if typeAhead = true (defaults to 250)
15188 typeAheadDelay : 250,
15190 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15191 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15193 valueNotFoundText : undefined,
15195 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15197 blockFocus : false,
15200 * @cfg {Boolean} disableClear Disable showing of clear button.
15202 disableClear : false,
15204 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15206 alwaysQuery : false,
15209 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15214 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15216 invalidClass : "has-warning",
15219 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15221 validClass : "has-success",
15224 * @cfg {Boolean} specialFilter (true|false) special filter default false
15226 specialFilter : false,
15229 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15231 mobileTouchView : true,
15234 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15236 useNativeIOS : false,
15239 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15241 mobile_restrict_height : false,
15243 ios_options : false,
15255 btnPosition : 'right',
15256 triggerList : true,
15257 showToggleBtn : true,
15259 emptyResultText: 'Empty',
15260 triggerText : 'Select',
15264 // element that contains real text value.. (when hidden is used..)
15266 getAutoCreate : function()
15271 * Render classic select for iso
15274 if(Roo.isIOS && this.useNativeIOS){
15275 cfg = this.getAutoCreateNativeIOS();
15283 if(Roo.isTouch && this.mobileTouchView){
15284 cfg = this.getAutoCreateTouchView();
15291 if(!this.tickable){
15292 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15297 * ComboBox with tickable selections
15300 var align = this.labelAlign || this.parentLabelAlign();
15303 cls : 'form-group roo-combobox-tickable' //input-group
15306 var btn_text_select = '';
15307 var btn_text_done = '';
15308 var btn_text_cancel = '';
15310 if (this.btn_text_show) {
15311 btn_text_select = 'Select';
15312 btn_text_done = 'Done';
15313 btn_text_cancel = 'Cancel';
15318 cls : 'tickable-buttons',
15323 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15324 //html : this.triggerText
15325 html: btn_text_select
15331 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15333 html: btn_text_done
15339 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15341 html: btn_text_cancel
15347 buttons.cn.unshift({
15349 cls: 'roo-select2-search-field-input'
15355 Roo.each(buttons.cn, function(c){
15357 c.cls += ' btn-' + _this.size;
15360 if (_this.disabled) {
15367 style : 'display: contents',
15372 cls: 'form-hidden-field'
15376 cls: 'roo-select2-choices',
15380 cls: 'roo-select2-search-field',
15391 cls: 'roo-select2-container input-group roo-select2-container-multi',
15397 // cls: 'typeahead typeahead-long dropdown-menu',
15398 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15403 if(this.hasFeedback && !this.allowBlank){
15407 cls: 'glyphicon form-control-feedback'
15410 combobox.cn.push(feedback);
15417 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15418 tooltip : 'This field is required'
15420 if (Roo.bootstrap.version == 4) {
15423 style : 'display:none'
15426 if (align ==='left' && this.fieldLabel.length) {
15428 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15435 cls : 'control-label col-form-label',
15436 html : this.fieldLabel
15448 var labelCfg = cfg.cn[1];
15449 var contentCfg = cfg.cn[2];
15452 if(this.indicatorpos == 'right'){
15458 cls : 'control-label col-form-label',
15462 html : this.fieldLabel
15478 labelCfg = cfg.cn[0];
15479 contentCfg = cfg.cn[1];
15483 if(this.labelWidth > 12){
15484 labelCfg.style = "width: " + this.labelWidth + 'px';
15486 if(this.width * 1 > 0){
15487 contentCfg.style = "width: " + this.width + 'px';
15489 if(this.labelWidth < 13 && this.labelmd == 0){
15490 this.labelmd = this.labelWidth;
15493 if(this.labellg > 0){
15494 labelCfg.cls += ' col-lg-' + this.labellg;
15495 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15498 if(this.labelmd > 0){
15499 labelCfg.cls += ' col-md-' + this.labelmd;
15500 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15503 if(this.labelsm > 0){
15504 labelCfg.cls += ' col-sm-' + this.labelsm;
15505 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15508 if(this.labelxs > 0){
15509 labelCfg.cls += ' col-xs-' + this.labelxs;
15510 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15514 } else if ( this.fieldLabel.length) {
15515 // Roo.log(" label");
15520 //cls : 'input-group-addon',
15521 html : this.fieldLabel
15526 if(this.indicatorpos == 'right'){
15530 //cls : 'input-group-addon',
15531 html : this.fieldLabel
15541 // Roo.log(" no label && no align");
15548 ['xs','sm','md','lg'].map(function(size){
15549 if (settings[size]) {
15550 cfg.cls += ' col-' + size + '-' + settings[size];
15558 _initEventsCalled : false,
15561 initEvents: function()
15563 if (this._initEventsCalled) { // as we call render... prevent looping...
15566 this._initEventsCalled = true;
15569 throw "can not find store for combo";
15572 this.indicator = this.indicatorEl();
15574 this.store = Roo.factory(this.store, Roo.data);
15575 this.store.parent = this;
15577 // if we are building from html. then this element is so complex, that we can not really
15578 // use the rendered HTML.
15579 // so we have to trash and replace the previous code.
15580 if (Roo.XComponent.build_from_html) {
15581 // remove this element....
15582 var e = this.el.dom, k=0;
15583 while (e ) { e = e.previousSibling; ++k;}
15588 this.rendered = false;
15590 this.render(this.parent().getChildContainer(true), k);
15593 if(Roo.isIOS && this.useNativeIOS){
15594 this.initIOSView();
15602 if(Roo.isTouch && this.mobileTouchView){
15603 this.initTouchView();
15608 this.initTickableEvents();
15612 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15614 if(this.hiddenName){
15616 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15618 this.hiddenField.dom.value =
15619 this.hiddenValue !== undefined ? this.hiddenValue :
15620 this.value !== undefined ? this.value : '';
15622 // prevent input submission
15623 this.el.dom.removeAttribute('name');
15624 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15629 // this.el.dom.setAttribute('autocomplete', 'off');
15632 var cls = 'x-combo-list';
15634 //this.list = new Roo.Layer({
15635 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15641 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15642 _this.list.setWidth(lw);
15645 this.list.on('mouseover', this.onViewOver, this);
15646 this.list.on('mousemove', this.onViewMove, this);
15647 this.list.on('scroll', this.onViewScroll, this);
15650 this.list.swallowEvent('mousewheel');
15651 this.assetHeight = 0;
15654 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15655 this.assetHeight += this.header.getHeight();
15658 this.innerList = this.list.createChild({cls:cls+'-inner'});
15659 this.innerList.on('mouseover', this.onViewOver, this);
15660 this.innerList.on('mousemove', this.onViewMove, this);
15661 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15663 if(this.allowBlank && !this.pageSize && !this.disableClear){
15664 this.footer = this.list.createChild({cls:cls+'-ft'});
15665 this.pageTb = new Roo.Toolbar(this.footer);
15669 this.footer = this.list.createChild({cls:cls+'-ft'});
15670 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15671 {pageSize: this.pageSize});
15675 if (this.pageTb && this.allowBlank && !this.disableClear) {
15677 this.pageTb.add(new Roo.Toolbar.Fill(), {
15678 cls: 'x-btn-icon x-btn-clear',
15680 handler: function()
15683 _this.clearValue();
15684 _this.onSelect(false, -1);
15689 this.assetHeight += this.footer.getHeight();
15694 this.tpl = Roo.bootstrap.version == 4 ?
15695 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15696 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15699 this.view = new Roo.View(this.list, this.tpl, {
15700 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15702 //this.view.wrapEl.setDisplayed(false);
15703 this.view.on('click', this.onViewClick, this);
15706 this.store.on('beforeload', this.onBeforeLoad, this);
15707 this.store.on('load', this.onLoad, this);
15708 this.store.on('loadexception', this.onLoadException, this);
15710 if(this.resizable){
15711 this.resizer = new Roo.Resizable(this.list, {
15712 pinned:true, handles:'se'
15714 this.resizer.on('resize', function(r, w, h){
15715 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15716 this.listWidth = w;
15717 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15718 this.restrictHeight();
15720 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15723 if(!this.editable){
15724 this.editable = true;
15725 this.setEditable(false);
15730 if (typeof(this.events.add.listeners) != 'undefined') {
15732 this.addicon = this.wrap.createChild(
15733 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15735 this.addicon.on('click', function(e) {
15736 this.fireEvent('add', this);
15739 if (typeof(this.events.edit.listeners) != 'undefined') {
15741 this.editicon = this.wrap.createChild(
15742 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15743 if (this.addicon) {
15744 this.editicon.setStyle('margin-left', '40px');
15746 this.editicon.on('click', function(e) {
15748 // we fire even if inothing is selected..
15749 this.fireEvent('edit', this, this.lastData );
15755 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15756 "up" : function(e){
15757 this.inKeyMode = true;
15761 "down" : function(e){
15762 if(!this.isExpanded()){
15763 this.onTriggerClick();
15765 this.inKeyMode = true;
15770 "enter" : function(e){
15771 // this.onViewClick();
15775 if(this.fireEvent("specialkey", this, e)){
15776 this.onViewClick(false);
15782 "esc" : function(e){
15786 "tab" : function(e){
15789 if(this.fireEvent("specialkey", this, e)){
15790 this.onViewClick(false);
15798 doRelay : function(foo, bar, hname){
15799 if(hname == 'down' || this.scope.isExpanded()){
15800 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15809 this.queryDelay = Math.max(this.queryDelay || 10,
15810 this.mode == 'local' ? 10 : 250);
15813 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15815 if(this.typeAhead){
15816 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15818 if(this.editable !== false){
15819 this.inputEl().on("keyup", this.onKeyUp, this);
15821 if(this.forceSelection){
15822 this.inputEl().on('blur', this.doForce, this);
15826 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15827 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15831 initTickableEvents: function()
15835 if(this.hiddenName){
15837 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15839 this.hiddenField.dom.value =
15840 this.hiddenValue !== undefined ? this.hiddenValue :
15841 this.value !== undefined ? this.value : '';
15843 // prevent input submission
15844 this.el.dom.removeAttribute('name');
15845 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15850 // this.list = this.el.select('ul.dropdown-menu',true).first();
15852 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15853 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15854 if(this.triggerList){
15855 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15858 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15859 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15861 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15862 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15864 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15865 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15867 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15868 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15869 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15872 this.cancelBtn.hide();
15877 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15878 _this.list.setWidth(lw);
15881 this.list.on('mouseover', this.onViewOver, this);
15882 this.list.on('mousemove', this.onViewMove, this);
15884 this.list.on('scroll', this.onViewScroll, this);
15887 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15888 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15891 this.view = new Roo.View(this.list, this.tpl, {
15896 selectedClass: this.selectedClass
15899 //this.view.wrapEl.setDisplayed(false);
15900 this.view.on('click', this.onViewClick, this);
15904 this.store.on('beforeload', this.onBeforeLoad, this);
15905 this.store.on('load', this.onLoad, this);
15906 this.store.on('loadexception', this.onLoadException, this);
15909 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15910 "up" : function(e){
15911 this.inKeyMode = true;
15915 "down" : function(e){
15916 this.inKeyMode = true;
15920 "enter" : function(e){
15921 if(this.fireEvent("specialkey", this, e)){
15922 this.onViewClick(false);
15928 "esc" : function(e){
15929 this.onTickableFooterButtonClick(e, false, false);
15932 "tab" : function(e){
15933 this.fireEvent("specialkey", this, e);
15935 this.onTickableFooterButtonClick(e, false, false);
15942 doRelay : function(e, fn, key){
15943 if(this.scope.isExpanded()){
15944 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15953 this.queryDelay = Math.max(this.queryDelay || 10,
15954 this.mode == 'local' ? 10 : 250);
15957 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15959 if(this.typeAhead){
15960 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15963 if(this.editable !== false){
15964 this.tickableInputEl().on("keyup", this.onKeyUp, this);
15967 this.indicator = this.indicatorEl();
15969 if(this.indicator){
15970 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15971 this.indicator.hide();
15976 onDestroy : function(){
15978 this.view.setStore(null);
15979 this.view.el.removeAllListeners();
15980 this.view.el.remove();
15981 this.view.purgeListeners();
15984 this.list.dom.innerHTML = '';
15988 this.store.un('beforeload', this.onBeforeLoad, this);
15989 this.store.un('load', this.onLoad, this);
15990 this.store.un('loadexception', this.onLoadException, this);
15992 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15996 fireKey : function(e){
15997 if(e.isNavKeyPress() && !this.list.isVisible()){
15998 this.fireEvent("specialkey", this, e);
16003 onResize: function(w, h)
16007 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16009 // if(typeof w != 'number'){
16010 // // we do not handle it!?!?
16013 // var tw = this.trigger.getWidth();
16014 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16015 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16017 // this.inputEl().setWidth( this.adjustWidth('input', x));
16019 // //this.trigger.setStyle('left', x+'px');
16021 // if(this.list && this.listWidth === undefined){
16022 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16023 // this.list.setWidth(lw);
16024 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16032 * Allow or prevent the user from directly editing the field text. If false is passed,
16033 * the user will only be able to select from the items defined in the dropdown list. This method
16034 * is the runtime equivalent of setting the 'editable' config option at config time.
16035 * @param {Boolean} value True to allow the user to directly edit the field text
16037 setEditable : function(value){
16038 if(value == this.editable){
16041 this.editable = value;
16043 this.inputEl().dom.setAttribute('readOnly', true);
16044 this.inputEl().on('mousedown', this.onTriggerClick, this);
16045 this.inputEl().addClass('x-combo-noedit');
16047 this.inputEl().dom.setAttribute('readOnly', false);
16048 this.inputEl().un('mousedown', this.onTriggerClick, this);
16049 this.inputEl().removeClass('x-combo-noedit');
16055 onBeforeLoad : function(combo,opts){
16056 if(!this.hasFocus){
16060 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16062 this.restrictHeight();
16063 this.selectedIndex = -1;
16067 onLoad : function(){
16069 this.hasQuery = false;
16071 if(!this.hasFocus){
16075 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16076 this.loading.hide();
16079 if(this.store.getCount() > 0){
16082 this.restrictHeight();
16083 if(this.lastQuery == this.allQuery){
16084 if(this.editable && !this.tickable){
16085 this.inputEl().dom.select();
16089 !this.selectByValue(this.value, true) &&
16092 !this.store.lastOptions ||
16093 typeof(this.store.lastOptions.add) == 'undefined' ||
16094 this.store.lastOptions.add != true
16097 this.select(0, true);
16100 if(this.autoFocus){
16103 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16104 this.taTask.delay(this.typeAheadDelay);
16108 this.onEmptyResults();
16114 onLoadException : function()
16116 this.hasQuery = false;
16118 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16119 this.loading.hide();
16122 if(this.tickable && this.editable){
16127 // only causes errors at present
16128 //Roo.log(this.store.reader.jsonData);
16129 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16131 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16137 onTypeAhead : function(){
16138 if(this.store.getCount() > 0){
16139 var r = this.store.getAt(0);
16140 var newValue = r.data[this.displayField];
16141 var len = newValue.length;
16142 var selStart = this.getRawValue().length;
16144 if(selStart != len){
16145 this.setRawValue(newValue);
16146 this.selectText(selStart, newValue.length);
16152 onSelect : function(record, index){
16154 if(this.fireEvent('beforeselect', this, record, index) !== false){
16156 this.setFromData(index > -1 ? record.data : false);
16159 this.fireEvent('select', this, record, index);
16164 * Returns the currently selected field value or empty string if no value is set.
16165 * @return {String} value The selected value
16167 getValue : function()
16169 if(Roo.isIOS && this.useNativeIOS){
16170 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16174 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16177 if(this.valueField){
16178 return typeof this.value != 'undefined' ? this.value : '';
16180 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16184 getRawValue : function()
16186 if(Roo.isIOS && this.useNativeIOS){
16187 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16190 var v = this.inputEl().getValue();
16196 * Clears any text/value currently set in the field
16198 clearValue : function(){
16200 if(this.hiddenField){
16201 this.hiddenField.dom.value = '';
16204 this.setRawValue('');
16205 this.lastSelectionText = '';
16206 this.lastData = false;
16208 var close = this.closeTriggerEl();
16219 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16220 * will be displayed in the field. If the value does not match the data value of an existing item,
16221 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16222 * Otherwise the field will be blank (although the value will still be set).
16223 * @param {String} value The value to match
16225 setValue : function(v)
16227 if(Roo.isIOS && this.useNativeIOS){
16228 this.setIOSValue(v);
16238 if(this.valueField){
16239 var r = this.findRecord(this.valueField, v);
16241 text = r.data[this.displayField];
16242 }else if(this.valueNotFoundText !== undefined){
16243 text = this.valueNotFoundText;
16246 this.lastSelectionText = text;
16247 if(this.hiddenField){
16248 this.hiddenField.dom.value = v;
16250 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16253 var close = this.closeTriggerEl();
16256 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16262 * @property {Object} the last set data for the element
16267 * Sets the value of the field based on a object which is related to the record format for the store.
16268 * @param {Object} value the value to set as. or false on reset?
16270 setFromData : function(o){
16277 var dv = ''; // display value
16278 var vv = ''; // value value..
16280 if (this.displayField) {
16281 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16283 // this is an error condition!!!
16284 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16287 if(this.valueField){
16288 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16291 var close = this.closeTriggerEl();
16294 if(dv.length || vv * 1 > 0){
16296 this.blockFocus=true;
16302 if(this.hiddenField){
16303 this.hiddenField.dom.value = vv;
16305 this.lastSelectionText = dv;
16306 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16310 // no hidden field.. - we store the value in 'value', but still display
16311 // display field!!!!
16312 this.lastSelectionText = dv;
16313 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16320 reset : function(){
16321 // overridden so that last data is reset..
16328 this.setValue(this.originalValue);
16329 //this.clearInvalid();
16330 this.lastData = false;
16332 this.view.clearSelections();
16338 findRecord : function(prop, value){
16340 if(this.store.getCount() > 0){
16341 this.store.each(function(r){
16342 if(r.data[prop] == value){
16352 getName: function()
16354 // returns hidden if it's set..
16355 if (!this.rendered) {return ''};
16356 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16360 onViewMove : function(e, t){
16361 this.inKeyMode = false;
16365 onViewOver : function(e, t){
16366 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16369 var item = this.view.findItemFromChild(t);
16372 var index = this.view.indexOf(item);
16373 this.select(index, false);
16378 onViewClick : function(view, doFocus, el, e)
16380 var index = this.view.getSelectedIndexes()[0];
16382 var r = this.store.getAt(index);
16386 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16393 Roo.each(this.tickItems, function(v,k){
16395 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16397 _this.tickItems.splice(k, 1);
16399 if(typeof(e) == 'undefined' && view == false){
16400 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16412 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16413 this.tickItems.push(r.data);
16416 if(typeof(e) == 'undefined' && view == false){
16417 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16424 this.onSelect(r, index);
16426 if(doFocus !== false && !this.blockFocus){
16427 this.inputEl().focus();
16432 restrictHeight : function(){
16433 //this.innerList.dom.style.height = '';
16434 //var inner = this.innerList.dom;
16435 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16436 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16437 //this.list.beginUpdate();
16438 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16439 this.list.alignTo(this.inputEl(), this.listAlign);
16440 this.list.alignTo(this.inputEl(), this.listAlign);
16441 //this.list.endUpdate();
16445 onEmptyResults : function(){
16447 if(this.tickable && this.editable){
16448 this.hasFocus = false;
16449 this.restrictHeight();
16457 * Returns true if the dropdown list is expanded, else false.
16459 isExpanded : function(){
16460 return this.list.isVisible();
16464 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16465 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16466 * @param {String} value The data value of the item to select
16467 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16468 * selected item if it is not currently in view (defaults to true)
16469 * @return {Boolean} True if the value matched an item in the list, else false
16471 selectByValue : function(v, scrollIntoView){
16472 if(v !== undefined && v !== null){
16473 var r = this.findRecord(this.valueField || this.displayField, v);
16475 this.select(this.store.indexOf(r), scrollIntoView);
16483 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16484 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16485 * @param {Number} index The zero-based index of the list item to select
16486 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16487 * selected item if it is not currently in view (defaults to true)
16489 select : function(index, scrollIntoView){
16490 this.selectedIndex = index;
16491 this.view.select(index);
16492 if(scrollIntoView !== false){
16493 var el = this.view.getNode(index);
16495 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16498 this.list.scrollChildIntoView(el, false);
16504 selectNext : function(){
16505 var ct = this.store.getCount();
16507 if(this.selectedIndex == -1){
16509 }else if(this.selectedIndex < ct-1){
16510 this.select(this.selectedIndex+1);
16516 selectPrev : function(){
16517 var ct = this.store.getCount();
16519 if(this.selectedIndex == -1){
16521 }else if(this.selectedIndex != 0){
16522 this.select(this.selectedIndex-1);
16528 onKeyUp : function(e){
16529 if(this.editable !== false && !e.isSpecialKey()){
16530 this.lastKey = e.getKey();
16531 this.dqTask.delay(this.queryDelay);
16536 validateBlur : function(){
16537 return !this.list || !this.list.isVisible();
16541 initQuery : function(){
16543 var v = this.getRawValue();
16545 if(this.tickable && this.editable){
16546 v = this.tickableInputEl().getValue();
16553 doForce : function(){
16554 if(this.inputEl().dom.value.length > 0){
16555 this.inputEl().dom.value =
16556 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16562 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16563 * query allowing the query action to be canceled if needed.
16564 * @param {String} query The SQL query to execute
16565 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16566 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16567 * saved in the current store (defaults to false)
16569 doQuery : function(q, forceAll){
16571 if(q === undefined || q === null){
16576 forceAll: forceAll,
16580 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16585 forceAll = qe.forceAll;
16586 if(forceAll === true || (q.length >= this.minChars)){
16588 this.hasQuery = true;
16590 if(this.lastQuery != q || this.alwaysQuery){
16591 this.lastQuery = q;
16592 if(this.mode == 'local'){
16593 this.selectedIndex = -1;
16595 this.store.clearFilter();
16598 if(this.specialFilter){
16599 this.fireEvent('specialfilter', this);
16604 this.store.filter(this.displayField, q);
16607 this.store.fireEvent("datachanged", this.store);
16614 this.store.baseParams[this.queryParam] = q;
16616 var options = {params : this.getParams(q)};
16619 options.add = true;
16620 options.params.start = this.page * this.pageSize;
16623 this.store.load(options);
16626 * this code will make the page width larger, at the beginning, the list not align correctly,
16627 * we should expand the list on onLoad
16628 * so command out it
16633 this.selectedIndex = -1;
16638 this.loadNext = false;
16642 getParams : function(q){
16644 //p[this.queryParam] = q;
16648 p.limit = this.pageSize;
16654 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16656 collapse : function(){
16657 if(!this.isExpanded()){
16663 this.hasFocus = false;
16667 this.cancelBtn.hide();
16668 this.trigger.show();
16671 this.tickableInputEl().dom.value = '';
16672 this.tickableInputEl().blur();
16677 Roo.get(document).un('mousedown', this.collapseIf, this);
16678 Roo.get(document).un('mousewheel', this.collapseIf, this);
16679 if (!this.editable) {
16680 Roo.get(document).un('keydown', this.listKeyPress, this);
16682 this.fireEvent('collapse', this);
16688 collapseIf : function(e){
16689 var in_combo = e.within(this.el);
16690 var in_list = e.within(this.list);
16691 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16693 if (in_combo || in_list || is_list) {
16694 //e.stopPropagation();
16699 this.onTickableFooterButtonClick(e, false, false);
16707 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16709 expand : function(){
16711 if(this.isExpanded() || !this.hasFocus){
16715 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16716 this.list.setWidth(lw);
16722 this.restrictHeight();
16726 this.tickItems = Roo.apply([], this.item);
16729 this.cancelBtn.show();
16730 this.trigger.hide();
16733 this.tickableInputEl().focus();
16738 Roo.get(document).on('mousedown', this.collapseIf, this);
16739 Roo.get(document).on('mousewheel', this.collapseIf, this);
16740 if (!this.editable) {
16741 Roo.get(document).on('keydown', this.listKeyPress, this);
16744 this.fireEvent('expand', this);
16748 // Implements the default empty TriggerField.onTriggerClick function
16749 onTriggerClick : function(e)
16751 Roo.log('trigger click');
16753 if(this.disabled || !this.triggerList){
16758 this.loadNext = false;
16760 if(this.isExpanded()){
16762 if (!this.blockFocus) {
16763 this.inputEl().focus();
16767 this.hasFocus = true;
16768 if(this.triggerAction == 'all') {
16769 this.doQuery(this.allQuery, true);
16771 this.doQuery(this.getRawValue());
16773 if (!this.blockFocus) {
16774 this.inputEl().focus();
16779 onTickableTriggerClick : function(e)
16786 this.loadNext = false;
16787 this.hasFocus = true;
16789 if(this.triggerAction == 'all') {
16790 this.doQuery(this.allQuery, true);
16792 this.doQuery(this.getRawValue());
16796 onSearchFieldClick : function(e)
16798 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16799 this.onTickableFooterButtonClick(e, false, false);
16803 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16808 this.loadNext = false;
16809 this.hasFocus = true;
16811 if(this.triggerAction == 'all') {
16812 this.doQuery(this.allQuery, true);
16814 this.doQuery(this.getRawValue());
16818 listKeyPress : function(e)
16820 //Roo.log('listkeypress');
16821 // scroll to first matching element based on key pres..
16822 if (e.isSpecialKey()) {
16825 var k = String.fromCharCode(e.getKey()).toUpperCase();
16828 var csel = this.view.getSelectedNodes();
16829 var cselitem = false;
16831 var ix = this.view.indexOf(csel[0]);
16832 cselitem = this.store.getAt(ix);
16833 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16839 this.store.each(function(v) {
16841 // start at existing selection.
16842 if (cselitem.id == v.id) {
16848 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16849 match = this.store.indexOf(v);
16855 if (match === false) {
16856 return true; // no more action?
16859 this.view.select(match);
16860 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16861 sn.scrollIntoView(sn.dom.parentNode, false);
16864 onViewScroll : function(e, t){
16866 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){
16870 this.hasQuery = true;
16872 this.loading = this.list.select('.loading', true).first();
16874 if(this.loading === null){
16875 this.list.createChild({
16877 cls: 'loading roo-select2-more-results roo-select2-active',
16878 html: 'Loading more results...'
16881 this.loading = this.list.select('.loading', true).first();
16883 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16885 this.loading.hide();
16888 this.loading.show();
16893 this.loadNext = true;
16895 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16900 addItem : function(o)
16902 var dv = ''; // display value
16904 if (this.displayField) {
16905 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16907 // this is an error condition!!!
16908 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16915 var choice = this.choices.createChild({
16917 cls: 'roo-select2-search-choice',
16926 cls: 'roo-select2-search-choice-close fa fa-times',
16931 }, this.searchField);
16933 var close = choice.select('a.roo-select2-search-choice-close', true).first();
16935 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16943 this.inputEl().dom.value = '';
16948 onRemoveItem : function(e, _self, o)
16950 e.preventDefault();
16952 this.lastItem = Roo.apply([], this.item);
16954 var index = this.item.indexOf(o.data) * 1;
16957 Roo.log('not this item?!');
16961 this.item.splice(index, 1);
16966 this.fireEvent('remove', this, e);
16972 syncValue : function()
16974 if(!this.item.length){
16981 Roo.each(this.item, function(i){
16982 if(_this.valueField){
16983 value.push(i[_this.valueField]);
16990 this.value = value.join(',');
16992 if(this.hiddenField){
16993 this.hiddenField.dom.value = this.value;
16996 this.store.fireEvent("datachanged", this.store);
17001 clearItem : function()
17003 if(!this.multiple){
17009 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17017 if(this.tickable && !Roo.isTouch){
17018 this.view.refresh();
17022 inputEl: function ()
17024 if(Roo.isIOS && this.useNativeIOS){
17025 return this.el.select('select.roo-ios-select', true).first();
17028 if(Roo.isTouch && this.mobileTouchView){
17029 return this.el.select('input.form-control',true).first();
17033 return this.searchField;
17036 return this.el.select('input.form-control',true).first();
17039 onTickableFooterButtonClick : function(e, btn, el)
17041 e.preventDefault();
17043 this.lastItem = Roo.apply([], this.item);
17045 if(btn && btn.name == 'cancel'){
17046 this.tickItems = Roo.apply([], this.item);
17055 Roo.each(this.tickItems, function(o){
17063 validate : function()
17065 if(this.getVisibilityEl().hasClass('hidden')){
17069 var v = this.getRawValue();
17072 v = this.getValue();
17075 if(this.disabled || this.allowBlank || v.length){
17080 this.markInvalid();
17084 tickableInputEl : function()
17086 if(!this.tickable || !this.editable){
17087 return this.inputEl();
17090 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17094 getAutoCreateTouchView : function()
17099 cls: 'form-group' //input-group
17105 type : this.inputType,
17106 cls : 'form-control x-combo-noedit',
17107 autocomplete: 'new-password',
17108 placeholder : this.placeholder || '',
17113 input.name = this.name;
17117 input.cls += ' input-' + this.size;
17120 if (this.disabled) {
17121 input.disabled = true;
17125 cls : 'roo-combobox-wrap',
17132 inputblock.cls += ' input-group';
17134 inputblock.cn.unshift({
17136 cls : 'input-group-addon input-group-prepend input-group-text',
17141 if(this.removable && !this.multiple){
17142 inputblock.cls += ' roo-removable';
17144 inputblock.cn.push({
17147 cls : 'roo-combo-removable-btn close'
17151 if(this.hasFeedback && !this.allowBlank){
17153 inputblock.cls += ' has-feedback';
17155 inputblock.cn.push({
17157 cls: 'glyphicon form-control-feedback'
17164 inputblock.cls += (this.before) ? '' : ' input-group';
17166 inputblock.cn.push({
17168 cls : 'input-group-addon input-group-append input-group-text',
17174 var ibwrap = inputblock;
17179 cls: 'roo-select2-choices',
17183 cls: 'roo-select2-search-field',
17196 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17201 cls: 'form-hidden-field'
17207 if(!this.multiple && this.showToggleBtn){
17213 if (this.caret != false) {
17216 cls: 'fa fa-' + this.caret
17223 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17225 Roo.bootstrap.version == 3 ? caret : '',
17228 cls: 'combobox-clear',
17242 combobox.cls += ' roo-select2-container-multi';
17245 var align = this.labelAlign || this.parentLabelAlign();
17247 if (align ==='left' && this.fieldLabel.length) {
17252 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17253 tooltip : 'This field is required'
17257 cls : 'control-label col-form-label',
17258 html : this.fieldLabel
17262 cls : 'roo-combobox-wrap ',
17269 var labelCfg = cfg.cn[1];
17270 var contentCfg = cfg.cn[2];
17273 if(this.indicatorpos == 'right'){
17278 cls : 'control-label col-form-label',
17282 html : this.fieldLabel
17286 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17287 tooltip : 'This field is required'
17292 cls : "roo-combobox-wrap ",
17300 labelCfg = cfg.cn[0];
17301 contentCfg = cfg.cn[1];
17306 if(this.labelWidth > 12){
17307 labelCfg.style = "width: " + this.labelWidth + 'px';
17310 if(this.labelWidth < 13 && this.labelmd == 0){
17311 this.labelmd = this.labelWidth;
17314 if(this.labellg > 0){
17315 labelCfg.cls += ' col-lg-' + this.labellg;
17316 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17319 if(this.labelmd > 0){
17320 labelCfg.cls += ' col-md-' + this.labelmd;
17321 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17324 if(this.labelsm > 0){
17325 labelCfg.cls += ' col-sm-' + this.labelsm;
17326 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17329 if(this.labelxs > 0){
17330 labelCfg.cls += ' col-xs-' + this.labelxs;
17331 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17335 } else if ( this.fieldLabel.length) {
17339 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17340 tooltip : 'This field is required'
17344 cls : 'control-label',
17345 html : this.fieldLabel
17356 if(this.indicatorpos == 'right'){
17360 cls : 'control-label',
17361 html : this.fieldLabel,
17365 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17366 tooltip : 'This field is required'
17383 var settings = this;
17385 ['xs','sm','md','lg'].map(function(size){
17386 if (settings[size]) {
17387 cfg.cls += ' col-' + size + '-' + settings[size];
17394 initTouchView : function()
17396 this.renderTouchView();
17398 this.touchViewEl.on('scroll', function(){
17399 this.el.dom.scrollTop = 0;
17402 this.originalValue = this.getValue();
17404 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17406 this.inputEl().on("click", this.showTouchView, this);
17407 if (this.triggerEl) {
17408 this.triggerEl.on("click", this.showTouchView, this);
17412 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17413 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17415 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17417 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17418 this.store.on('load', this.onTouchViewLoad, this);
17419 this.store.on('loadexception', this.onTouchViewLoadException, this);
17421 if(this.hiddenName){
17423 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17425 this.hiddenField.dom.value =
17426 this.hiddenValue !== undefined ? this.hiddenValue :
17427 this.value !== undefined ? this.value : '';
17429 this.el.dom.removeAttribute('name');
17430 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17434 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17435 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17438 if(this.removable && !this.multiple){
17439 var close = this.closeTriggerEl();
17441 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17442 close.on('click', this.removeBtnClick, this, close);
17446 * fix the bug in Safari iOS8
17448 this.inputEl().on("focus", function(e){
17449 document.activeElement.blur();
17452 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17459 renderTouchView : function()
17461 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17462 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17464 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17465 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17467 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17468 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17469 this.touchViewBodyEl.setStyle('overflow', 'auto');
17471 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17472 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17474 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17475 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17479 showTouchView : function()
17485 this.touchViewHeaderEl.hide();
17487 if(this.modalTitle.length){
17488 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17489 this.touchViewHeaderEl.show();
17492 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17493 this.touchViewEl.show();
17495 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17497 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17498 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17500 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17502 if(this.modalTitle.length){
17503 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17506 this.touchViewBodyEl.setHeight(bodyHeight);
17510 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17512 this.touchViewEl.addClass(['in','show']);
17515 if(this._touchViewMask){
17516 Roo.get(document.body).addClass("x-body-masked");
17517 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17518 this._touchViewMask.setStyle('z-index', 10000);
17519 this._touchViewMask.addClass('show');
17522 this.doTouchViewQuery();
17526 hideTouchView : function()
17528 this.touchViewEl.removeClass(['in','show']);
17532 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17534 this.touchViewEl.setStyle('display', 'none');
17537 if(this._touchViewMask){
17538 this._touchViewMask.removeClass('show');
17539 Roo.get(document.body).removeClass("x-body-masked");
17543 setTouchViewValue : function()
17550 Roo.each(this.tickItems, function(o){
17555 this.hideTouchView();
17558 doTouchViewQuery : function()
17567 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17571 if(!this.alwaysQuery || this.mode == 'local'){
17572 this.onTouchViewLoad();
17579 onTouchViewBeforeLoad : function(combo,opts)
17585 onTouchViewLoad : function()
17587 if(this.store.getCount() < 1){
17588 this.onTouchViewEmptyResults();
17592 this.clearTouchView();
17594 var rawValue = this.getRawValue();
17596 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17598 this.tickItems = [];
17600 this.store.data.each(function(d, rowIndex){
17601 var row = this.touchViewListGroup.createChild(template);
17603 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17604 row.addClass(d.data.cls);
17607 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17610 html : d.data[this.displayField]
17613 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17614 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17617 row.removeClass('selected');
17618 if(!this.multiple && this.valueField &&
17619 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17622 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17623 row.addClass('selected');
17626 if(this.multiple && this.valueField &&
17627 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17631 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17632 this.tickItems.push(d.data);
17635 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17639 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17641 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17643 if(this.modalTitle.length){
17644 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17647 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17649 if(this.mobile_restrict_height && listHeight < bodyHeight){
17650 this.touchViewBodyEl.setHeight(listHeight);
17655 if(firstChecked && listHeight > bodyHeight){
17656 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17661 onTouchViewLoadException : function()
17663 this.hideTouchView();
17666 onTouchViewEmptyResults : function()
17668 this.clearTouchView();
17670 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17672 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17676 clearTouchView : function()
17678 this.touchViewListGroup.dom.innerHTML = '';
17681 onTouchViewClick : function(e, el, o)
17683 e.preventDefault();
17686 var rowIndex = o.rowIndex;
17688 var r = this.store.getAt(rowIndex);
17690 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17692 if(!this.multiple){
17693 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17694 c.dom.removeAttribute('checked');
17697 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17699 this.setFromData(r.data);
17701 var close = this.closeTriggerEl();
17707 this.hideTouchView();
17709 this.fireEvent('select', this, r, rowIndex);
17714 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17715 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17716 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17720 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17721 this.addItem(r.data);
17722 this.tickItems.push(r.data);
17726 getAutoCreateNativeIOS : function()
17729 cls: 'form-group' //input-group,
17734 cls : 'roo-ios-select'
17738 combobox.name = this.name;
17741 if (this.disabled) {
17742 combobox.disabled = true;
17745 var settings = this;
17747 ['xs','sm','md','lg'].map(function(size){
17748 if (settings[size]) {
17749 cfg.cls += ' col-' + size + '-' + settings[size];
17759 initIOSView : function()
17761 this.store.on('load', this.onIOSViewLoad, this);
17766 onIOSViewLoad : function()
17768 if(this.store.getCount() < 1){
17772 this.clearIOSView();
17774 if(this.allowBlank) {
17776 var default_text = '-- SELECT --';
17778 if(this.placeholder.length){
17779 default_text = this.placeholder;
17782 if(this.emptyTitle.length){
17783 default_text += ' - ' + this.emptyTitle + ' -';
17786 var opt = this.inputEl().createChild({
17789 html : default_text
17793 o[this.valueField] = 0;
17794 o[this.displayField] = default_text;
17796 this.ios_options.push({
17803 this.store.data.each(function(d, rowIndex){
17807 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17808 html = d.data[this.displayField];
17813 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17814 value = d.data[this.valueField];
17823 if(this.value == d.data[this.valueField]){
17824 option['selected'] = true;
17827 var opt = this.inputEl().createChild(option);
17829 this.ios_options.push({
17836 this.inputEl().on('change', function(){
17837 this.fireEvent('select', this);
17842 clearIOSView: function()
17844 this.inputEl().dom.innerHTML = '';
17846 this.ios_options = [];
17849 setIOSValue: function(v)
17853 if(!this.ios_options){
17857 Roo.each(this.ios_options, function(opts){
17859 opts.el.dom.removeAttribute('selected');
17861 if(opts.data[this.valueField] != v){
17865 opts.el.dom.setAttribute('selected', true);
17871 * @cfg {Boolean} grow
17875 * @cfg {Number} growMin
17879 * @cfg {Number} growMax
17888 Roo.apply(Roo.bootstrap.ComboBox, {
17892 cls: 'modal-header',
17914 cls: 'list-group-item',
17918 cls: 'roo-combobox-list-group-item-value'
17922 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17936 listItemCheckbox : {
17938 cls: 'list-group-item',
17942 cls: 'roo-combobox-list-group-item-value'
17946 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17962 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17967 cls: 'modal-footer',
17975 cls: 'col-xs-6 text-left',
17978 cls: 'btn btn-danger roo-touch-view-cancel',
17984 cls: 'col-xs-6 text-right',
17987 cls: 'btn btn-success roo-touch-view-ok',
17998 Roo.apply(Roo.bootstrap.ComboBox, {
18000 touchViewTemplate : {
18002 cls: 'modal fade roo-combobox-touch-view',
18006 cls: 'modal-dialog',
18007 style : 'position:fixed', // we have to fix position....
18011 cls: 'modal-content',
18013 Roo.bootstrap.ComboBox.header,
18014 Roo.bootstrap.ComboBox.body,
18015 Roo.bootstrap.ComboBox.footer
18024 * Ext JS Library 1.1.1
18025 * Copyright(c) 2006-2007, Ext JS, LLC.
18027 * Originally Released Under LGPL - original licence link has changed is not relivant.
18030 * <script type="text/javascript">
18035 * @extends Roo.util.Observable
18036 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18037 * This class also supports single and multi selection modes. <br>
18038 * Create a data model bound view:
18040 var store = new Roo.data.Store(...);
18042 var view = new Roo.View({
18044 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18046 singleSelect: true,
18047 selectedClass: "ydataview-selected",
18051 // listen for node click?
18052 view.on("click", function(vw, index, node, e){
18053 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18057 dataModel.load("foobar.xml");
18059 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18061 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18062 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18064 * Note: old style constructor is still suported (container, template, config)
18067 * Create a new View
18068 * @param {Object} config The config object
18071 Roo.View = function(config, depreciated_tpl, depreciated_config){
18073 this.parent = false;
18075 if (typeof(depreciated_tpl) == 'undefined') {
18076 // new way.. - universal constructor.
18077 Roo.apply(this, config);
18078 this.el = Roo.get(this.el);
18081 this.el = Roo.get(config);
18082 this.tpl = depreciated_tpl;
18083 Roo.apply(this, depreciated_config);
18085 this.wrapEl = this.el.wrap().wrap();
18086 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18089 if(typeof(this.tpl) == "string"){
18090 this.tpl = new Roo.Template(this.tpl);
18092 // support xtype ctors..
18093 this.tpl = new Roo.factory(this.tpl, Roo);
18097 this.tpl.compile();
18102 * @event beforeclick
18103 * Fires before a click is processed. Returns false to cancel the default action.
18104 * @param {Roo.View} this
18105 * @param {Number} index The index of the target node
18106 * @param {HTMLElement} node The target node
18107 * @param {Roo.EventObject} e The raw event object
18109 "beforeclick" : true,
18112 * Fires when a template node is clicked.
18113 * @param {Roo.View} this
18114 * @param {Number} index The index of the target node
18115 * @param {HTMLElement} node The target node
18116 * @param {Roo.EventObject} e The raw event object
18121 * Fires when a template node is double clicked.
18122 * @param {Roo.View} this
18123 * @param {Number} index The index of the target node
18124 * @param {HTMLElement} node The target node
18125 * @param {Roo.EventObject} e The raw event object
18129 * @event contextmenu
18130 * Fires when a template node is right clicked.
18131 * @param {Roo.View} this
18132 * @param {Number} index The index of the target node
18133 * @param {HTMLElement} node The target node
18134 * @param {Roo.EventObject} e The raw event object
18136 "contextmenu" : true,
18138 * @event selectionchange
18139 * Fires when the selected nodes change.
18140 * @param {Roo.View} this
18141 * @param {Array} selections Array of the selected nodes
18143 "selectionchange" : true,
18146 * @event beforeselect
18147 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18148 * @param {Roo.View} this
18149 * @param {HTMLElement} node The node to be selected
18150 * @param {Array} selections Array of currently selected nodes
18152 "beforeselect" : true,
18154 * @event preparedata
18155 * Fires on every row to render, to allow you to change the data.
18156 * @param {Roo.View} this
18157 * @param {Object} data to be rendered (change this)
18159 "preparedata" : true
18167 "click": this.onClick,
18168 "dblclick": this.onDblClick,
18169 "contextmenu": this.onContextMenu,
18173 this.selections = [];
18175 this.cmp = new Roo.CompositeElementLite([]);
18177 this.store = Roo.factory(this.store, Roo.data);
18178 this.setStore(this.store, true);
18181 if ( this.footer && this.footer.xtype) {
18183 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18185 this.footer.dataSource = this.store;
18186 this.footer.container = fctr;
18187 this.footer = Roo.factory(this.footer, Roo);
18188 fctr.insertFirst(this.el);
18190 // this is a bit insane - as the paging toolbar seems to detach the el..
18191 // dom.parentNode.parentNode.parentNode
18192 // they get detached?
18196 Roo.View.superclass.constructor.call(this);
18201 Roo.extend(Roo.View, Roo.util.Observable, {
18204 * @cfg {Roo.data.Store} store Data store to load data from.
18209 * @cfg {String|Roo.Element} el The container element.
18214 * @cfg {String|Roo.Template} tpl The template used by this View
18218 * @cfg {String} dataName the named area of the template to use as the data area
18219 * Works with domtemplates roo-name="name"
18223 * @cfg {String} selectedClass The css class to add to selected nodes
18225 selectedClass : "x-view-selected",
18227 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18232 * @cfg {String} text to display on mask (default Loading)
18236 * @cfg {Boolean} multiSelect Allow multiple selection
18238 multiSelect : false,
18240 * @cfg {Boolean} singleSelect Allow single selection
18242 singleSelect: false,
18245 * @cfg {Boolean} toggleSelect - selecting
18247 toggleSelect : false,
18250 * @cfg {Boolean} tickable - selecting
18255 * Returns the element this view is bound to.
18256 * @return {Roo.Element}
18258 getEl : function(){
18259 return this.wrapEl;
18265 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18267 refresh : function(){
18268 //Roo.log('refresh');
18271 // if we are using something like 'domtemplate', then
18272 // the what gets used is:
18273 // t.applySubtemplate(NAME, data, wrapping data..)
18274 // the outer template then get' applied with
18275 // the store 'extra data'
18276 // and the body get's added to the
18277 // roo-name="data" node?
18278 // <span class='roo-tpl-{name}'></span> ?????
18282 this.clearSelections();
18283 this.el.update("");
18285 var records = this.store.getRange();
18286 if(records.length < 1) {
18288 // is this valid?? = should it render a template??
18290 this.el.update(this.emptyText);
18294 if (this.dataName) {
18295 this.el.update(t.apply(this.store.meta)); //????
18296 el = this.el.child('.roo-tpl-' + this.dataName);
18299 for(var i = 0, len = records.length; i < len; i++){
18300 var data = this.prepareData(records[i].data, i, records[i]);
18301 this.fireEvent("preparedata", this, data, i, records[i]);
18303 var d = Roo.apply({}, data);
18306 Roo.apply(d, {'roo-id' : Roo.id()});
18310 Roo.each(this.parent.item, function(item){
18311 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18314 Roo.apply(d, {'roo-data-checked' : 'checked'});
18318 html[html.length] = Roo.util.Format.trim(
18320 t.applySubtemplate(this.dataName, d, this.store.meta) :
18327 el.update(html.join(""));
18328 this.nodes = el.dom.childNodes;
18329 this.updateIndexes(0);
18334 * Function to override to reformat the data that is sent to
18335 * the template for each node.
18336 * DEPRICATED - use the preparedata event handler.
18337 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18338 * a JSON object for an UpdateManager bound view).
18340 prepareData : function(data, index, record)
18342 this.fireEvent("preparedata", this, data, index, record);
18346 onUpdate : function(ds, record){
18347 // Roo.log('on update');
18348 this.clearSelections();
18349 var index = this.store.indexOf(record);
18350 var n = this.nodes[index];
18351 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18352 n.parentNode.removeChild(n);
18353 this.updateIndexes(index, index);
18359 onAdd : function(ds, records, index)
18361 //Roo.log(['on Add', ds, records, index] );
18362 this.clearSelections();
18363 if(this.nodes.length == 0){
18367 var n = this.nodes[index];
18368 for(var i = 0, len = records.length; i < len; i++){
18369 var d = this.prepareData(records[i].data, i, records[i]);
18371 this.tpl.insertBefore(n, d);
18374 this.tpl.append(this.el, d);
18377 this.updateIndexes(index);
18380 onRemove : function(ds, record, index){
18381 // Roo.log('onRemove');
18382 this.clearSelections();
18383 var el = this.dataName ?
18384 this.el.child('.roo-tpl-' + this.dataName) :
18387 el.dom.removeChild(this.nodes[index]);
18388 this.updateIndexes(index);
18392 * Refresh an individual node.
18393 * @param {Number} index
18395 refreshNode : function(index){
18396 this.onUpdate(this.store, this.store.getAt(index));
18399 updateIndexes : function(startIndex, endIndex){
18400 var ns = this.nodes;
18401 startIndex = startIndex || 0;
18402 endIndex = endIndex || ns.length - 1;
18403 for(var i = startIndex; i <= endIndex; i++){
18404 ns[i].nodeIndex = i;
18409 * Changes the data store this view uses and refresh the view.
18410 * @param {Store} store
18412 setStore : function(store, initial){
18413 if(!initial && this.store){
18414 this.store.un("datachanged", this.refresh);
18415 this.store.un("add", this.onAdd);
18416 this.store.un("remove", this.onRemove);
18417 this.store.un("update", this.onUpdate);
18418 this.store.un("clear", this.refresh);
18419 this.store.un("beforeload", this.onBeforeLoad);
18420 this.store.un("load", this.onLoad);
18421 this.store.un("loadexception", this.onLoad);
18425 store.on("datachanged", this.refresh, this);
18426 store.on("add", this.onAdd, this);
18427 store.on("remove", this.onRemove, this);
18428 store.on("update", this.onUpdate, this);
18429 store.on("clear", this.refresh, this);
18430 store.on("beforeload", this.onBeforeLoad, this);
18431 store.on("load", this.onLoad, this);
18432 store.on("loadexception", this.onLoad, this);
18440 * onbeforeLoad - masks the loading area.
18443 onBeforeLoad : function(store,opts)
18445 //Roo.log('onBeforeLoad');
18447 this.el.update("");
18449 this.el.mask(this.mask ? this.mask : "Loading" );
18451 onLoad : function ()
18458 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18459 * @param {HTMLElement} node
18460 * @return {HTMLElement} The template node
18462 findItemFromChild : function(node){
18463 var el = this.dataName ?
18464 this.el.child('.roo-tpl-' + this.dataName,true) :
18467 if(!node || node.parentNode == el){
18470 var p = node.parentNode;
18471 while(p && p != el){
18472 if(p.parentNode == el){
18481 onClick : function(e){
18482 var item = this.findItemFromChild(e.getTarget());
18484 var index = this.indexOf(item);
18485 if(this.onItemClick(item, index, e) !== false){
18486 this.fireEvent("click", this, index, item, e);
18489 this.clearSelections();
18494 onContextMenu : function(e){
18495 var item = this.findItemFromChild(e.getTarget());
18497 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18502 onDblClick : function(e){
18503 var item = this.findItemFromChild(e.getTarget());
18505 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18509 onItemClick : function(item, index, e)
18511 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18514 if (this.toggleSelect) {
18515 var m = this.isSelected(item) ? 'unselect' : 'select';
18518 _t[m](item, true, false);
18521 if(this.multiSelect || this.singleSelect){
18522 if(this.multiSelect && e.shiftKey && this.lastSelection){
18523 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18525 this.select(item, this.multiSelect && e.ctrlKey);
18526 this.lastSelection = item;
18529 if(!this.tickable){
18530 e.preventDefault();
18538 * Get the number of selected nodes.
18541 getSelectionCount : function(){
18542 return this.selections.length;
18546 * Get the currently selected nodes.
18547 * @return {Array} An array of HTMLElements
18549 getSelectedNodes : function(){
18550 return this.selections;
18554 * Get the indexes of the selected nodes.
18557 getSelectedIndexes : function(){
18558 var indexes = [], s = this.selections;
18559 for(var i = 0, len = s.length; i < len; i++){
18560 indexes.push(s[i].nodeIndex);
18566 * Clear all selections
18567 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18569 clearSelections : function(suppressEvent){
18570 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18571 this.cmp.elements = this.selections;
18572 this.cmp.removeClass(this.selectedClass);
18573 this.selections = [];
18574 if(!suppressEvent){
18575 this.fireEvent("selectionchange", this, this.selections);
18581 * Returns true if the passed node is selected
18582 * @param {HTMLElement/Number} node The node or node index
18583 * @return {Boolean}
18585 isSelected : function(node){
18586 var s = this.selections;
18590 node = this.getNode(node);
18591 return s.indexOf(node) !== -1;
18596 * @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
18597 * @param {Boolean} keepExisting (optional) true to keep existing selections
18598 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18600 select : function(nodeInfo, keepExisting, suppressEvent){
18601 if(nodeInfo instanceof Array){
18603 this.clearSelections(true);
18605 for(var i = 0, len = nodeInfo.length; i < len; i++){
18606 this.select(nodeInfo[i], true, true);
18610 var node = this.getNode(nodeInfo);
18611 if(!node || this.isSelected(node)){
18612 return; // already selected.
18615 this.clearSelections(true);
18618 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18619 Roo.fly(node).addClass(this.selectedClass);
18620 this.selections.push(node);
18621 if(!suppressEvent){
18622 this.fireEvent("selectionchange", this, this.selections);
18630 * @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
18631 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18632 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18634 unselect : function(nodeInfo, keepExisting, suppressEvent)
18636 if(nodeInfo instanceof Array){
18637 Roo.each(this.selections, function(s) {
18638 this.unselect(s, nodeInfo);
18642 var node = this.getNode(nodeInfo);
18643 if(!node || !this.isSelected(node)){
18644 //Roo.log("not selected");
18645 return; // not selected.
18649 Roo.each(this.selections, function(s) {
18651 Roo.fly(node).removeClass(this.selectedClass);
18658 this.selections= ns;
18659 this.fireEvent("selectionchange", this, this.selections);
18663 * Gets a template node.
18664 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18665 * @return {HTMLElement} The node or null if it wasn't found
18667 getNode : function(nodeInfo){
18668 if(typeof nodeInfo == "string"){
18669 return document.getElementById(nodeInfo);
18670 }else if(typeof nodeInfo == "number"){
18671 return this.nodes[nodeInfo];
18677 * Gets a range template nodes.
18678 * @param {Number} startIndex
18679 * @param {Number} endIndex
18680 * @return {Array} An array of nodes
18682 getNodes : function(start, end){
18683 var ns = this.nodes;
18684 start = start || 0;
18685 end = typeof end == "undefined" ? ns.length - 1 : end;
18688 for(var i = start; i <= end; i++){
18692 for(var i = start; i >= end; i--){
18700 * Finds the index of the passed node
18701 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18702 * @return {Number} The index of the node or -1
18704 indexOf : function(node){
18705 node = this.getNode(node);
18706 if(typeof node.nodeIndex == "number"){
18707 return node.nodeIndex;
18709 var ns = this.nodes;
18710 for(var i = 0, len = ns.length; i < len; i++){
18721 * based on jquery fullcalendar
18725 Roo.bootstrap = Roo.bootstrap || {};
18727 * @class Roo.bootstrap.Calendar
18728 * @extends Roo.bootstrap.Component
18729 * Bootstrap Calendar class
18730 * @cfg {Boolean} loadMask (true|false) default false
18731 * @cfg {Object} header generate the user specific header of the calendar, default false
18734 * Create a new Container
18735 * @param {Object} config The config object
18740 Roo.bootstrap.Calendar = function(config){
18741 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18745 * Fires when a date is selected
18746 * @param {DatePicker} this
18747 * @param {Date} date The selected date
18751 * @event monthchange
18752 * Fires when the displayed month changes
18753 * @param {DatePicker} this
18754 * @param {Date} date The selected month
18756 'monthchange': true,
18758 * @event evententer
18759 * Fires when mouse over an event
18760 * @param {Calendar} this
18761 * @param {event} Event
18763 'evententer': true,
18765 * @event eventleave
18766 * Fires when the mouse leaves an
18767 * @param {Calendar} this
18770 'eventleave': true,
18772 * @event eventclick
18773 * Fires when the mouse click an
18774 * @param {Calendar} this
18783 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18786 * @cfg {Number} startDay
18787 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18795 getAutoCreate : function(){
18798 var fc_button = function(name, corner, style, content ) {
18799 return Roo.apply({},{
18801 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18803 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18806 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18817 style : 'width:100%',
18824 cls : 'fc-header-left',
18826 fc_button('prev', 'left', 'arrow', '‹' ),
18827 fc_button('next', 'right', 'arrow', '›' ),
18828 { tag: 'span', cls: 'fc-header-space' },
18829 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18837 cls : 'fc-header-center',
18841 cls: 'fc-header-title',
18844 html : 'month / year'
18852 cls : 'fc-header-right',
18854 /* fc_button('month', 'left', '', 'month' ),
18855 fc_button('week', '', '', 'week' ),
18856 fc_button('day', 'right', '', 'day' )
18868 header = this.header;
18871 var cal_heads = function() {
18873 // fixme - handle this.
18875 for (var i =0; i < Date.dayNames.length; i++) {
18876 var d = Date.dayNames[i];
18879 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18880 html : d.substring(0,3)
18884 ret[0].cls += ' fc-first';
18885 ret[6].cls += ' fc-last';
18888 var cal_cell = function(n) {
18891 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18896 cls: 'fc-day-number',
18900 cls: 'fc-day-content',
18904 style: 'position: relative;' // height: 17px;
18916 var cal_rows = function() {
18919 for (var r = 0; r < 6; r++) {
18926 for (var i =0; i < Date.dayNames.length; i++) {
18927 var d = Date.dayNames[i];
18928 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18931 row.cn[0].cls+=' fc-first';
18932 row.cn[0].cn[0].style = 'min-height:90px';
18933 row.cn[6].cls+=' fc-last';
18937 ret[0].cls += ' fc-first';
18938 ret[4].cls += ' fc-prev-last';
18939 ret[5].cls += ' fc-last';
18946 cls: 'fc-border-separate',
18947 style : 'width:100%',
18955 cls : 'fc-first fc-last',
18973 cls : 'fc-content',
18974 style : "position: relative;",
18977 cls : 'fc-view fc-view-month fc-grid',
18978 style : 'position: relative',
18979 unselectable : 'on',
18982 cls : 'fc-event-container',
18983 style : 'position:absolute;z-index:8;top:0;left:0;'
19001 initEvents : function()
19004 throw "can not find store for calendar";
19010 style: "text-align:center",
19014 style: "background-color:white;width:50%;margin:250 auto",
19018 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19029 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19031 var size = this.el.select('.fc-content', true).first().getSize();
19032 this.maskEl.setSize(size.width, size.height);
19033 this.maskEl.enableDisplayMode("block");
19034 if(!this.loadMask){
19035 this.maskEl.hide();
19038 this.store = Roo.factory(this.store, Roo.data);
19039 this.store.on('load', this.onLoad, this);
19040 this.store.on('beforeload', this.onBeforeLoad, this);
19044 this.cells = this.el.select('.fc-day',true);
19045 //Roo.log(this.cells);
19046 this.textNodes = this.el.query('.fc-day-number');
19047 this.cells.addClassOnOver('fc-state-hover');
19049 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19050 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19051 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19052 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19054 this.on('monthchange', this.onMonthChange, this);
19056 this.update(new Date().clearTime());
19059 resize : function() {
19060 var sz = this.el.getSize();
19062 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19063 this.el.select('.fc-day-content div',true).setHeight(34);
19068 showPrevMonth : function(e){
19069 this.update(this.activeDate.add("mo", -1));
19071 showToday : function(e){
19072 this.update(new Date().clearTime());
19075 showNextMonth : function(e){
19076 this.update(this.activeDate.add("mo", 1));
19080 showPrevYear : function(){
19081 this.update(this.activeDate.add("y", -1));
19085 showNextYear : function(){
19086 this.update(this.activeDate.add("y", 1));
19091 update : function(date)
19093 var vd = this.activeDate;
19094 this.activeDate = date;
19095 // if(vd && this.el){
19096 // var t = date.getTime();
19097 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19098 // Roo.log('using add remove');
19100 // this.fireEvent('monthchange', this, date);
19102 // this.cells.removeClass("fc-state-highlight");
19103 // this.cells.each(function(c){
19104 // if(c.dateValue == t){
19105 // c.addClass("fc-state-highlight");
19106 // setTimeout(function(){
19107 // try{c.dom.firstChild.focus();}catch(e){}
19117 var days = date.getDaysInMonth();
19119 var firstOfMonth = date.getFirstDateOfMonth();
19120 var startingPos = firstOfMonth.getDay()-this.startDay;
19122 if(startingPos < this.startDay){
19126 var pm = date.add(Date.MONTH, -1);
19127 var prevStart = pm.getDaysInMonth()-startingPos;
19129 this.cells = this.el.select('.fc-day',true);
19130 this.textNodes = this.el.query('.fc-day-number');
19131 this.cells.addClassOnOver('fc-state-hover');
19133 var cells = this.cells.elements;
19134 var textEls = this.textNodes;
19136 Roo.each(cells, function(cell){
19137 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19140 days += startingPos;
19142 // convert everything to numbers so it's fast
19143 var day = 86400000;
19144 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19147 //Roo.log(prevStart);
19149 var today = new Date().clearTime().getTime();
19150 var sel = date.clearTime().getTime();
19151 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19152 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19153 var ddMatch = this.disabledDatesRE;
19154 var ddText = this.disabledDatesText;
19155 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19156 var ddaysText = this.disabledDaysText;
19157 var format = this.format;
19159 var setCellClass = function(cal, cell){
19163 //Roo.log('set Cell Class');
19165 var t = d.getTime();
19169 cell.dateValue = t;
19171 cell.className += " fc-today";
19172 cell.className += " fc-state-highlight";
19173 cell.title = cal.todayText;
19176 // disable highlight in other month..
19177 //cell.className += " fc-state-highlight";
19182 cell.className = " fc-state-disabled";
19183 cell.title = cal.minText;
19187 cell.className = " fc-state-disabled";
19188 cell.title = cal.maxText;
19192 if(ddays.indexOf(d.getDay()) != -1){
19193 cell.title = ddaysText;
19194 cell.className = " fc-state-disabled";
19197 if(ddMatch && format){
19198 var fvalue = d.dateFormat(format);
19199 if(ddMatch.test(fvalue)){
19200 cell.title = ddText.replace("%0", fvalue);
19201 cell.className = " fc-state-disabled";
19205 if (!cell.initialClassName) {
19206 cell.initialClassName = cell.dom.className;
19209 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19214 for(; i < startingPos; i++) {
19215 textEls[i].innerHTML = (++prevStart);
19216 d.setDate(d.getDate()+1);
19218 cells[i].className = "fc-past fc-other-month";
19219 setCellClass(this, cells[i]);
19224 for(; i < days; i++){
19225 intDay = i - startingPos + 1;
19226 textEls[i].innerHTML = (intDay);
19227 d.setDate(d.getDate()+1);
19229 cells[i].className = ''; // "x-date-active";
19230 setCellClass(this, cells[i]);
19234 for(; i < 42; i++) {
19235 textEls[i].innerHTML = (++extraDays);
19236 d.setDate(d.getDate()+1);
19238 cells[i].className = "fc-future fc-other-month";
19239 setCellClass(this, cells[i]);
19242 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19244 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19246 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19247 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19249 if(totalRows != 6){
19250 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19251 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19254 this.fireEvent('monthchange', this, date);
19258 if(!this.internalRender){
19259 var main = this.el.dom.firstChild;
19260 var w = main.offsetWidth;
19261 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19262 Roo.fly(main).setWidth(w);
19263 this.internalRender = true;
19264 // opera does not respect the auto grow header center column
19265 // then, after it gets a width opera refuses to recalculate
19266 // without a second pass
19267 if(Roo.isOpera && !this.secondPass){
19268 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19269 this.secondPass = true;
19270 this.update.defer(10, this, [date]);
19277 findCell : function(dt) {
19278 dt = dt.clearTime().getTime();
19280 this.cells.each(function(c){
19281 //Roo.log("check " +c.dateValue + '?=' + dt);
19282 if(c.dateValue == dt){
19292 findCells : function(ev) {
19293 var s = ev.start.clone().clearTime().getTime();
19295 var e= ev.end.clone().clearTime().getTime();
19298 this.cells.each(function(c){
19299 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19301 if(c.dateValue > e){
19304 if(c.dateValue < s){
19313 // findBestRow: function(cells)
19317 // for (var i =0 ; i < cells.length;i++) {
19318 // ret = Math.max(cells[i].rows || 0,ret);
19325 addItem : function(ev)
19327 // look for vertical location slot in
19328 var cells = this.findCells(ev);
19330 // ev.row = this.findBestRow(cells);
19332 // work out the location.
19336 for(var i =0; i < cells.length; i++) {
19338 cells[i].row = cells[0].row;
19341 cells[i].row = cells[i].row + 1;
19351 if (crow.start.getY() == cells[i].getY()) {
19353 crow.end = cells[i];
19370 cells[0].events.push(ev);
19372 this.calevents.push(ev);
19375 clearEvents: function() {
19377 if(!this.calevents){
19381 Roo.each(this.cells.elements, function(c){
19387 Roo.each(this.calevents, function(e) {
19388 Roo.each(e.els, function(el) {
19389 el.un('mouseenter' ,this.onEventEnter, this);
19390 el.un('mouseleave' ,this.onEventLeave, this);
19395 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19401 renderEvents: function()
19405 this.cells.each(function(c) {
19414 if(c.row != c.events.length){
19415 r = 4 - (4 - (c.row - c.events.length));
19418 c.events = ev.slice(0, r);
19419 c.more = ev.slice(r);
19421 if(c.more.length && c.more.length == 1){
19422 c.events.push(c.more.pop());
19425 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19429 this.cells.each(function(c) {
19431 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19434 for (var e = 0; e < c.events.length; e++){
19435 var ev = c.events[e];
19436 var rows = ev.rows;
19438 for(var i = 0; i < rows.length; i++) {
19440 // how many rows should it span..
19443 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19444 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19446 unselectable : "on",
19449 cls: 'fc-event-inner',
19453 // cls: 'fc-event-time',
19454 // html : cells.length > 1 ? '' : ev.time
19458 cls: 'fc-event-title',
19459 html : String.format('{0}', ev.title)
19466 cls: 'ui-resizable-handle ui-resizable-e',
19467 html : '  '
19474 cfg.cls += ' fc-event-start';
19476 if ((i+1) == rows.length) {
19477 cfg.cls += ' fc-event-end';
19480 var ctr = _this.el.select('.fc-event-container',true).first();
19481 var cg = ctr.createChild(cfg);
19483 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19484 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19486 var r = (c.more.length) ? 1 : 0;
19487 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19488 cg.setWidth(ebox.right - sbox.x -2);
19490 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19491 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19492 cg.on('click', _this.onEventClick, _this, ev);
19503 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19504 style : 'position: absolute',
19505 unselectable : "on",
19508 cls: 'fc-event-inner',
19512 cls: 'fc-event-title',
19520 cls: 'ui-resizable-handle ui-resizable-e',
19521 html : '  '
19527 var ctr = _this.el.select('.fc-event-container',true).first();
19528 var cg = ctr.createChild(cfg);
19530 var sbox = c.select('.fc-day-content',true).first().getBox();
19531 var ebox = c.select('.fc-day-content',true).first().getBox();
19533 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19534 cg.setWidth(ebox.right - sbox.x -2);
19536 cg.on('click', _this.onMoreEventClick, _this, c.more);
19546 onEventEnter: function (e, el,event,d) {
19547 this.fireEvent('evententer', this, el, event);
19550 onEventLeave: function (e, el,event,d) {
19551 this.fireEvent('eventleave', this, el, event);
19554 onEventClick: function (e, el,event,d) {
19555 this.fireEvent('eventclick', this, el, event);
19558 onMonthChange: function () {
19562 onMoreEventClick: function(e, el, more)
19566 this.calpopover.placement = 'right';
19567 this.calpopover.setTitle('More');
19569 this.calpopover.setContent('');
19571 var ctr = this.calpopover.el.select('.popover-content', true).first();
19573 Roo.each(more, function(m){
19575 cls : 'fc-event-hori fc-event-draggable',
19578 var cg = ctr.createChild(cfg);
19580 cg.on('click', _this.onEventClick, _this, m);
19583 this.calpopover.show(el);
19588 onLoad: function ()
19590 this.calevents = [];
19593 if(this.store.getCount() > 0){
19594 this.store.data.each(function(d){
19597 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19598 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19599 time : d.data.start_time,
19600 title : d.data.title,
19601 description : d.data.description,
19602 venue : d.data.venue
19607 this.renderEvents();
19609 if(this.calevents.length && this.loadMask){
19610 this.maskEl.hide();
19614 onBeforeLoad: function()
19616 this.clearEvents();
19618 this.maskEl.show();
19632 * @class Roo.bootstrap.Popover
19633 * @extends Roo.bootstrap.Component
19634 * Bootstrap Popover class
19635 * @cfg {String} html contents of the popover (or false to use children..)
19636 * @cfg {String} title of popover (or false to hide)
19637 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19638 * @cfg {String} trigger click || hover (or false to trigger manually)
19639 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19640 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19641 * - if false and it has a 'parent' then it will be automatically added to that element
19642 * - if string - Roo.get will be called
19643 * @cfg {Number} delay - delay before showing
19646 * Create a new Popover
19647 * @param {Object} config The config object
19650 Roo.bootstrap.Popover = function(config){
19651 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19657 * After the popover show
19659 * @param {Roo.bootstrap.Popover} this
19664 * After the popover hide
19666 * @param {Roo.bootstrap.Popover} this
19672 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19677 placement : 'right',
19678 trigger : 'hover', // hover
19684 can_build_overlaid : false,
19686 maskEl : false, // the mask element
19689 alignEl : false, // when show is called with an element - this get's stored.
19691 getChildContainer : function()
19693 return this.contentEl;
19696 getPopoverHeader : function()
19698 this.title = true; // flag not to hide it..
19699 this.headerEl.addClass('p-0');
19700 return this.headerEl
19704 getAutoCreate : function(){
19707 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19708 style: 'display:block',
19714 cls : 'popover-inner ',
19718 cls: 'popover-title popover-header',
19719 html : this.title === false ? '' : this.title
19722 cls : 'popover-content popover-body ' + (this.cls || ''),
19723 html : this.html || ''
19734 * @param {string} the title
19736 setTitle: function(str)
19740 this.headerEl.dom.innerHTML = str;
19745 * @param {string} the body content
19747 setContent: function(str)
19750 if (this.contentEl) {
19751 this.contentEl.dom.innerHTML = str;
19755 // as it get's added to the bottom of the page.
19756 onRender : function(ct, position)
19758 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19763 var cfg = Roo.apply({}, this.getAutoCreate());
19767 cfg.cls += ' ' + this.cls;
19770 cfg.style = this.style;
19772 //Roo.log("adding to ");
19773 this.el = Roo.get(document.body).createChild(cfg, position);
19774 // Roo.log(this.el);
19777 this.contentEl = this.el.select('.popover-content',true).first();
19778 this.headerEl = this.el.select('.popover-title',true).first();
19781 if(typeof(this.items) != 'undefined'){
19782 var items = this.items;
19785 for(var i =0;i < items.length;i++) {
19786 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19790 this.items = nitems;
19792 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19793 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19800 resizeMask : function()
19802 this.maskEl.setSize(
19803 Roo.lib.Dom.getViewWidth(true),
19804 Roo.lib.Dom.getViewHeight(true)
19808 initEvents : function()
19812 Roo.bootstrap.Popover.register(this);
19815 this.arrowEl = this.el.select('.arrow',true).first();
19816 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19817 this.el.enableDisplayMode('block');
19821 if (this.over === false && !this.parent()) {
19824 if (this.triggers === false) {
19829 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19830 var triggers = this.trigger ? this.trigger.split(' ') : [];
19831 Roo.each(triggers, function(trigger) {
19833 if (trigger == 'click') {
19834 on_el.on('click', this.toggle, this);
19835 } else if (trigger != 'manual') {
19836 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19837 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19839 on_el.on(eventIn ,this.enter, this);
19840 on_el.on(eventOut, this.leave, this);
19850 toggle : function () {
19851 this.hoverState == 'in' ? this.leave() : this.enter();
19854 enter : function () {
19856 clearTimeout(this.timeout);
19858 this.hoverState = 'in';
19860 if (!this.delay || !this.delay.show) {
19865 this.timeout = setTimeout(function () {
19866 if (_t.hoverState == 'in') {
19869 }, this.delay.show)
19872 leave : function() {
19873 clearTimeout(this.timeout);
19875 this.hoverState = 'out';
19877 if (!this.delay || !this.delay.hide) {
19882 this.timeout = setTimeout(function () {
19883 if (_t.hoverState == 'out') {
19886 }, this.delay.hide)
19890 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19891 * @param {string} (left|right|top|bottom) position
19893 show : function (on_el, placement)
19895 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19896 on_el = on_el || false; // default to false
19899 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19900 on_el = this.parent().el;
19901 } else if (this.over) {
19902 Roo.get(this.over);
19907 this.alignEl = Roo.get( on_el );
19910 this.render(document.body);
19916 if (this.title === false) {
19917 this.headerEl.hide();
19922 this.el.dom.style.display = 'block';
19925 if (this.alignEl) {
19926 this.updatePosition(this.placement, true);
19929 // this is usually just done by the builder = to show the popoup in the middle of the scren.
19930 var es = this.el.getSize();
19931 var x = Roo.lib.Dom.getViewWidth()/2;
19932 var y = Roo.lib.Dom.getViewHeight()/2;
19933 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
19938 //var arrow = this.el.select('.arrow',true).first();
19939 //arrow.set(align[2],
19941 this.el.addClass('in');
19945 this.hoverState = 'in';
19948 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19949 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19950 this.maskEl.dom.style.display = 'block';
19951 this.maskEl.addClass('show');
19953 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19955 this.fireEvent('show', this);
19959 * fire this manually after loading a grid in the table for example
19960 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
19961 * @param {Boolean} try and move it if we cant get right position.
19963 updatePosition : function(placement, try_move)
19965 // allow for calling with no parameters
19966 placement = placement ? placement : this.placement;
19967 try_move = typeof(try_move) == 'undefined' ? true : try_move;
19969 this.el.removeClass([
19970 'fade','top','bottom', 'left', 'right','in',
19971 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19973 this.el.addClass(placement + ' bs-popover-' + placement);
19975 if (!this.alignEl ) {
19979 switch (placement) {
19981 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19982 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19983 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19984 //normal display... or moved up/down.
19985 this.el.setXY(offset);
19986 var xy = this.alignEl.getAnchorXY('tr', false);
19988 this.arrowEl.setXY(xy);
19991 // continue through...
19992 return this.updatePosition('left', false);
19996 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19997 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
19998 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19999 //normal display... or moved up/down.
20000 this.el.setXY(offset);
20001 var xy = this.alignEl.getAnchorXY('tl', false);
20002 xy[0]-=10;xy[1]+=5; // << fix me
20003 this.arrowEl.setXY(xy);
20007 return this.updatePosition('right', false);
20010 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20011 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20012 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20013 //normal display... or moved up/down.
20014 this.el.setXY(offset);
20015 var xy = this.alignEl.getAnchorXY('t', false);
20016 xy[1]-=10; // << fix me
20017 this.arrowEl.setXY(xy);
20021 return this.updatePosition('bottom', false);
20024 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20025 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20026 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20027 //normal display... or moved up/down.
20028 this.el.setXY(offset);
20029 var xy = this.alignEl.getAnchorXY('b', false);
20030 xy[1]+=2; // << fix me
20031 this.arrowEl.setXY(xy);
20035 return this.updatePosition('top', false);
20046 this.el.setXY([0,0]);
20047 this.el.removeClass('in');
20049 this.hoverState = null;
20050 this.maskEl.hide(); // always..
20051 this.fireEvent('hide', this);
20057 Roo.apply(Roo.bootstrap.Popover, {
20060 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20061 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20062 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20063 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20068 clickHander : false,
20071 onMouseDown : function(e)
20073 if (!e.getTarget(".roo-popover")) {
20081 register : function(popup)
20083 if (!Roo.bootstrap.Popover.clickHandler) {
20084 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20086 // hide other popups.
20088 this.popups.push(popup);
20090 hideAll : function()
20092 this.popups.forEach(function(p) {
20100 * Card header - holder for the card header elements.
20105 * @class Roo.bootstrap.PopoverNav
20106 * @extends Roo.bootstrap.NavGroup
20107 * Bootstrap Popover header navigation class
20109 * Create a new Popover Header Navigation
20110 * @param {Object} config The config object
20113 Roo.bootstrap.PopoverNav = function(config){
20114 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20117 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20120 container_method : 'getPopoverHeader'
20138 * @class Roo.bootstrap.Progress
20139 * @extends Roo.bootstrap.Component
20140 * Bootstrap Progress class
20141 * @cfg {Boolean} striped striped of the progress bar
20142 * @cfg {Boolean} active animated of the progress bar
20146 * Create a new Progress
20147 * @param {Object} config The config object
20150 Roo.bootstrap.Progress = function(config){
20151 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20154 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20159 getAutoCreate : function(){
20167 cfg.cls += ' progress-striped';
20171 cfg.cls += ' active';
20190 * @class Roo.bootstrap.ProgressBar
20191 * @extends Roo.bootstrap.Component
20192 * Bootstrap ProgressBar class
20193 * @cfg {Number} aria_valuenow aria-value now
20194 * @cfg {Number} aria_valuemin aria-value min
20195 * @cfg {Number} aria_valuemax aria-value max
20196 * @cfg {String} label label for the progress bar
20197 * @cfg {String} panel (success | info | warning | danger )
20198 * @cfg {String} role role of the progress bar
20199 * @cfg {String} sr_only text
20203 * Create a new ProgressBar
20204 * @param {Object} config The config object
20207 Roo.bootstrap.ProgressBar = function(config){
20208 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20211 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20215 aria_valuemax : 100,
20221 getAutoCreate : function()
20226 cls: 'progress-bar',
20227 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20239 cfg.role = this.role;
20242 if(this.aria_valuenow){
20243 cfg['aria-valuenow'] = this.aria_valuenow;
20246 if(this.aria_valuemin){
20247 cfg['aria-valuemin'] = this.aria_valuemin;
20250 if(this.aria_valuemax){
20251 cfg['aria-valuemax'] = this.aria_valuemax;
20254 if(this.label && !this.sr_only){
20255 cfg.html = this.label;
20259 cfg.cls += ' progress-bar-' + this.panel;
20265 update : function(aria_valuenow)
20267 this.aria_valuenow = aria_valuenow;
20269 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20284 * @class Roo.bootstrap.TabGroup
20285 * @extends Roo.bootstrap.Column
20286 * Bootstrap Column class
20287 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20288 * @cfg {Boolean} carousel true to make the group behave like a carousel
20289 * @cfg {Boolean} bullets show bullets for the panels
20290 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20291 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20292 * @cfg {Boolean} showarrow (true|false) show arrow default true
20295 * Create a new TabGroup
20296 * @param {Object} config The config object
20299 Roo.bootstrap.TabGroup = function(config){
20300 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20302 this.navId = Roo.id();
20305 Roo.bootstrap.TabGroup.register(this);
20309 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20312 transition : false,
20317 slideOnTouch : false,
20320 getAutoCreate : function()
20322 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20324 cfg.cls += ' tab-content';
20326 if (this.carousel) {
20327 cfg.cls += ' carousel slide';
20330 cls : 'carousel-inner',
20334 if(this.bullets && !Roo.isTouch){
20337 cls : 'carousel-bullets',
20341 if(this.bullets_cls){
20342 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20349 cfg.cn[0].cn.push(bullets);
20352 if(this.showarrow){
20353 cfg.cn[0].cn.push({
20355 class : 'carousel-arrow',
20359 class : 'carousel-prev',
20363 class : 'fa fa-chevron-left'
20369 class : 'carousel-next',
20373 class : 'fa fa-chevron-right'
20386 initEvents: function()
20388 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20389 // this.el.on("touchstart", this.onTouchStart, this);
20392 if(this.autoslide){
20395 this.slideFn = window.setInterval(function() {
20396 _this.showPanelNext();
20400 if(this.showarrow){
20401 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20402 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20408 // onTouchStart : function(e, el, o)
20410 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20414 // this.showPanelNext();
20418 getChildContainer : function()
20420 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20424 * register a Navigation item
20425 * @param {Roo.bootstrap.NavItem} the navitem to add
20427 register : function(item)
20429 this.tabs.push( item);
20430 item.navId = this.navId; // not really needed..
20435 getActivePanel : function()
20438 Roo.each(this.tabs, function(t) {
20448 getPanelByName : function(n)
20451 Roo.each(this.tabs, function(t) {
20452 if (t.tabId == n) {
20460 indexOfPanel : function(p)
20463 Roo.each(this.tabs, function(t,i) {
20464 if (t.tabId == p.tabId) {
20473 * show a specific panel
20474 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20475 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20477 showPanel : function (pan)
20479 if(this.transition || typeof(pan) == 'undefined'){
20480 Roo.log("waiting for the transitionend");
20484 if (typeof(pan) == 'number') {
20485 pan = this.tabs[pan];
20488 if (typeof(pan) == 'string') {
20489 pan = this.getPanelByName(pan);
20492 var cur = this.getActivePanel();
20495 Roo.log('pan or acitve pan is undefined');
20499 if (pan.tabId == this.getActivePanel().tabId) {
20503 if (false === cur.fireEvent('beforedeactivate')) {
20507 if(this.bullets > 0 && !Roo.isTouch){
20508 this.setActiveBullet(this.indexOfPanel(pan));
20511 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20513 //class="carousel-item carousel-item-next carousel-item-left"
20515 this.transition = true;
20516 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20517 var lr = dir == 'next' ? 'left' : 'right';
20518 pan.el.addClass(dir); // or prev
20519 pan.el.addClass('carousel-item-' + dir); // or prev
20520 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20521 cur.el.addClass(lr); // or right
20522 pan.el.addClass(lr);
20523 cur.el.addClass('carousel-item-' +lr); // or right
20524 pan.el.addClass('carousel-item-' +lr);
20528 cur.el.on('transitionend', function() {
20529 Roo.log("trans end?");
20531 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20532 pan.setActive(true);
20534 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20535 cur.setActive(false);
20537 _this.transition = false;
20539 }, this, { single: true } );
20544 cur.setActive(false);
20545 pan.setActive(true);
20550 showPanelNext : function()
20552 var i = this.indexOfPanel(this.getActivePanel());
20554 if (i >= this.tabs.length - 1 && !this.autoslide) {
20558 if (i >= this.tabs.length - 1 && this.autoslide) {
20562 this.showPanel(this.tabs[i+1]);
20565 showPanelPrev : function()
20567 var i = this.indexOfPanel(this.getActivePanel());
20569 if (i < 1 && !this.autoslide) {
20573 if (i < 1 && this.autoslide) {
20574 i = this.tabs.length;
20577 this.showPanel(this.tabs[i-1]);
20581 addBullet: function()
20583 if(!this.bullets || Roo.isTouch){
20586 var ctr = this.el.select('.carousel-bullets',true).first();
20587 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20588 var bullet = ctr.createChild({
20589 cls : 'bullet bullet-' + i
20590 },ctr.dom.lastChild);
20595 bullet.on('click', (function(e, el, o, ii, t){
20597 e.preventDefault();
20599 this.showPanel(ii);
20601 if(this.autoslide && this.slideFn){
20602 clearInterval(this.slideFn);
20603 this.slideFn = window.setInterval(function() {
20604 _this.showPanelNext();
20608 }).createDelegate(this, [i, bullet], true));
20613 setActiveBullet : function(i)
20619 Roo.each(this.el.select('.bullet', true).elements, function(el){
20620 el.removeClass('selected');
20623 var bullet = this.el.select('.bullet-' + i, true).first();
20629 bullet.addClass('selected');
20640 Roo.apply(Roo.bootstrap.TabGroup, {
20644 * register a Navigation Group
20645 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20647 register : function(navgrp)
20649 this.groups[navgrp.navId] = navgrp;
20653 * fetch a Navigation Group based on the navigation ID
20654 * if one does not exist , it will get created.
20655 * @param {string} the navgroup to add
20656 * @returns {Roo.bootstrap.NavGroup} the navgroup
20658 get: function(navId) {
20659 if (typeof(this.groups[navId]) == 'undefined') {
20660 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20662 return this.groups[navId] ;
20677 * @class Roo.bootstrap.TabPanel
20678 * @extends Roo.bootstrap.Component
20679 * Bootstrap TabPanel class
20680 * @cfg {Boolean} active panel active
20681 * @cfg {String} html panel content
20682 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20683 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20684 * @cfg {String} href click to link..
20685 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20689 * Create a new TabPanel
20690 * @param {Object} config The config object
20693 Roo.bootstrap.TabPanel = function(config){
20694 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20698 * Fires when the active status changes
20699 * @param {Roo.bootstrap.TabPanel} this
20700 * @param {Boolean} state the new state
20705 * @event beforedeactivate
20706 * Fires before a tab is de-activated - can be used to do validation on a form.
20707 * @param {Roo.bootstrap.TabPanel} this
20708 * @return {Boolean} false if there is an error
20711 'beforedeactivate': true
20714 this.tabId = this.tabId || Roo.id();
20718 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20725 touchSlide : false,
20726 getAutoCreate : function(){
20731 // item is needed for carousel - not sure if it has any effect otherwise
20732 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20733 html: this.html || ''
20737 cfg.cls += ' active';
20741 cfg.tabId = this.tabId;
20749 initEvents: function()
20751 var p = this.parent();
20753 this.navId = this.navId || p.navId;
20755 if (typeof(this.navId) != 'undefined') {
20756 // not really needed.. but just in case.. parent should be a NavGroup.
20757 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20761 var i = tg.tabs.length - 1;
20763 if(this.active && tg.bullets > 0 && i < tg.bullets){
20764 tg.setActiveBullet(i);
20768 this.el.on('click', this.onClick, this);
20770 if(Roo.isTouch && this.touchSlide){
20771 this.el.on("touchstart", this.onTouchStart, this);
20772 this.el.on("touchmove", this.onTouchMove, this);
20773 this.el.on("touchend", this.onTouchEnd, this);
20778 onRender : function(ct, position)
20780 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20783 setActive : function(state)
20785 Roo.log("panel - set active " + this.tabId + "=" + state);
20787 this.active = state;
20789 this.el.removeClass('active');
20791 } else if (!this.el.hasClass('active')) {
20792 this.el.addClass('active');
20795 this.fireEvent('changed', this, state);
20798 onClick : function(e)
20800 e.preventDefault();
20802 if(!this.href.length){
20806 window.location.href = this.href;
20815 onTouchStart : function(e)
20817 this.swiping = false;
20819 this.startX = e.browserEvent.touches[0].clientX;
20820 this.startY = e.browserEvent.touches[0].clientY;
20823 onTouchMove : function(e)
20825 this.swiping = true;
20827 this.endX = e.browserEvent.touches[0].clientX;
20828 this.endY = e.browserEvent.touches[0].clientY;
20831 onTouchEnd : function(e)
20838 var tabGroup = this.parent();
20840 if(this.endX > this.startX){ // swiping right
20841 tabGroup.showPanelPrev();
20845 if(this.startX > this.endX){ // swiping left
20846 tabGroup.showPanelNext();
20865 * @class Roo.bootstrap.DateField
20866 * @extends Roo.bootstrap.Input
20867 * Bootstrap DateField class
20868 * @cfg {Number} weekStart default 0
20869 * @cfg {String} viewMode default empty, (months|years)
20870 * @cfg {String} minViewMode default empty, (months|years)
20871 * @cfg {Number} startDate default -Infinity
20872 * @cfg {Number} endDate default Infinity
20873 * @cfg {Boolean} todayHighlight default false
20874 * @cfg {Boolean} todayBtn default false
20875 * @cfg {Boolean} calendarWeeks default false
20876 * @cfg {Object} daysOfWeekDisabled default empty
20877 * @cfg {Boolean} singleMode default false (true | false)
20879 * @cfg {Boolean} keyboardNavigation default true
20880 * @cfg {String} language default en
20883 * Create a new DateField
20884 * @param {Object} config The config object
20887 Roo.bootstrap.DateField = function(config){
20888 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20892 * Fires when this field show.
20893 * @param {Roo.bootstrap.DateField} this
20894 * @param {Mixed} date The date value
20899 * Fires when this field hide.
20900 * @param {Roo.bootstrap.DateField} this
20901 * @param {Mixed} date The date value
20906 * Fires when select a date.
20907 * @param {Roo.bootstrap.DateField} this
20908 * @param {Mixed} date The date value
20912 * @event beforeselect
20913 * Fires when before select a date.
20914 * @param {Roo.bootstrap.DateField} this
20915 * @param {Mixed} date The date value
20917 beforeselect : true
20921 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
20924 * @cfg {String} format
20925 * The default date format string which can be overriden for localization support. The format must be
20926 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20930 * @cfg {String} altFormats
20931 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20932 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20934 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20942 todayHighlight : false,
20948 keyboardNavigation: true,
20950 calendarWeeks: false,
20952 startDate: -Infinity,
20956 daysOfWeekDisabled: [],
20960 singleMode : false,
20962 UTCDate: function()
20964 return new Date(Date.UTC.apply(Date, arguments));
20967 UTCToday: function()
20969 var today = new Date();
20970 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20973 getDate: function() {
20974 var d = this.getUTCDate();
20975 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20978 getUTCDate: function() {
20982 setDate: function(d) {
20983 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20986 setUTCDate: function(d) {
20988 this.setValue(this.formatDate(this.date));
20991 onRender: function(ct, position)
20994 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20996 this.language = this.language || 'en';
20997 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20998 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21000 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21001 this.format = this.format || 'm/d/y';
21002 this.isInline = false;
21003 this.isInput = true;
21004 this.component = this.el.select('.add-on', true).first() || false;
21005 this.component = (this.component && this.component.length === 0) ? false : this.component;
21006 this.hasInput = this.component && this.inputEl().length;
21008 if (typeof(this.minViewMode === 'string')) {
21009 switch (this.minViewMode) {
21011 this.minViewMode = 1;
21014 this.minViewMode = 2;
21017 this.minViewMode = 0;
21022 if (typeof(this.viewMode === 'string')) {
21023 switch (this.viewMode) {
21036 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21038 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21040 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21042 this.picker().on('mousedown', this.onMousedown, this);
21043 this.picker().on('click', this.onClick, this);
21045 this.picker().addClass('datepicker-dropdown');
21047 this.startViewMode = this.viewMode;
21049 if(this.singleMode){
21050 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21051 v.setVisibilityMode(Roo.Element.DISPLAY);
21055 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21056 v.setStyle('width', '189px');
21060 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21061 if(!this.calendarWeeks){
21066 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21067 v.attr('colspan', function(i, val){
21068 return parseInt(val) + 1;
21073 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21075 this.setStartDate(this.startDate);
21076 this.setEndDate(this.endDate);
21078 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21085 if(this.isInline) {
21090 picker : function()
21092 return this.pickerEl;
21093 // return this.el.select('.datepicker', true).first();
21096 fillDow: function()
21098 var dowCnt = this.weekStart;
21107 if(this.calendarWeeks){
21115 while (dowCnt < this.weekStart + 7) {
21119 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21123 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21126 fillMonths: function()
21129 var months = this.picker().select('>.datepicker-months td', true).first();
21131 months.dom.innerHTML = '';
21137 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21140 months.createChild(month);
21147 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;
21149 if (this.date < this.startDate) {
21150 this.viewDate = new Date(this.startDate);
21151 } else if (this.date > this.endDate) {
21152 this.viewDate = new Date(this.endDate);
21154 this.viewDate = new Date(this.date);
21162 var d = new Date(this.viewDate),
21163 year = d.getUTCFullYear(),
21164 month = d.getUTCMonth(),
21165 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21166 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21167 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21168 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21169 currentDate = this.date && this.date.valueOf(),
21170 today = this.UTCToday();
21172 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21174 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21176 // this.picker.select('>tfoot th.today').
21177 // .text(dates[this.language].today)
21178 // .toggle(this.todayBtn !== false);
21180 this.updateNavArrows();
21183 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21185 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21187 prevMonth.setUTCDate(day);
21189 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21191 var nextMonth = new Date(prevMonth);
21193 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21195 nextMonth = nextMonth.valueOf();
21197 var fillMonths = false;
21199 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21201 while(prevMonth.valueOf() <= nextMonth) {
21204 if (prevMonth.getUTCDay() === this.weekStart) {
21206 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21214 if(this.calendarWeeks){
21215 // ISO 8601: First week contains first thursday.
21216 // ISO also states week starts on Monday, but we can be more abstract here.
21218 // Start of current week: based on weekstart/current date
21219 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21220 // Thursday of this week
21221 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21222 // First Thursday of year, year from thursday
21223 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21224 // Calendar week: ms between thursdays, div ms per day, div 7 days
21225 calWeek = (th - yth) / 864e5 / 7 + 1;
21227 fillMonths.cn.push({
21235 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21237 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21240 if (this.todayHighlight &&
21241 prevMonth.getUTCFullYear() == today.getFullYear() &&
21242 prevMonth.getUTCMonth() == today.getMonth() &&
21243 prevMonth.getUTCDate() == today.getDate()) {
21244 clsName += ' today';
21247 if (currentDate && prevMonth.valueOf() === currentDate) {
21248 clsName += ' active';
21251 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21252 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21253 clsName += ' disabled';
21256 fillMonths.cn.push({
21258 cls: 'day ' + clsName,
21259 html: prevMonth.getDate()
21262 prevMonth.setDate(prevMonth.getDate()+1);
21265 var currentYear = this.date && this.date.getUTCFullYear();
21266 var currentMonth = this.date && this.date.getUTCMonth();
21268 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21270 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21271 v.removeClass('active');
21273 if(currentYear === year && k === currentMonth){
21274 v.addClass('active');
21277 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21278 v.addClass('disabled');
21284 year = parseInt(year/10, 10) * 10;
21286 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21288 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21291 for (var i = -1; i < 11; i++) {
21292 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21294 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21302 showMode: function(dir)
21305 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21308 Roo.each(this.picker().select('>div',true).elements, function(v){
21309 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21312 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21317 if(this.isInline) {
21321 this.picker().removeClass(['bottom', 'top']);
21323 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21325 * place to the top of element!
21329 this.picker().addClass('top');
21330 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21335 this.picker().addClass('bottom');
21337 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21340 parseDate : function(value)
21342 if(!value || value instanceof Date){
21345 var v = Date.parseDate(value, this.format);
21346 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21347 v = Date.parseDate(value, 'Y-m-d');
21349 if(!v && this.altFormats){
21350 if(!this.altFormatsArray){
21351 this.altFormatsArray = this.altFormats.split("|");
21353 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21354 v = Date.parseDate(value, this.altFormatsArray[i]);
21360 formatDate : function(date, fmt)
21362 return (!date || !(date instanceof Date)) ?
21363 date : date.dateFormat(fmt || this.format);
21366 onFocus : function()
21368 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21372 onBlur : function()
21374 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21376 var d = this.inputEl().getValue();
21383 showPopup : function()
21385 this.picker().show();
21389 this.fireEvent('showpopup', this, this.date);
21392 hidePopup : function()
21394 if(this.isInline) {
21397 this.picker().hide();
21398 this.viewMode = this.startViewMode;
21401 this.fireEvent('hidepopup', this, this.date);
21405 onMousedown: function(e)
21407 e.stopPropagation();
21408 e.preventDefault();
21413 Roo.bootstrap.DateField.superclass.keyup.call(this);
21417 setValue: function(v)
21419 if(this.fireEvent('beforeselect', this, v) !== false){
21420 var d = new Date(this.parseDate(v) ).clearTime();
21422 if(isNaN(d.getTime())){
21423 this.date = this.viewDate = '';
21424 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21428 v = this.formatDate(d);
21430 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21432 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21436 this.fireEvent('select', this, this.date);
21440 getValue: function()
21442 return this.formatDate(this.date);
21445 fireKey: function(e)
21447 if (!this.picker().isVisible()){
21448 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21454 var dateChanged = false,
21456 newDate, newViewDate;
21461 e.preventDefault();
21465 if (!this.keyboardNavigation) {
21468 dir = e.keyCode == 37 ? -1 : 1;
21471 newDate = this.moveYear(this.date, dir);
21472 newViewDate = this.moveYear(this.viewDate, dir);
21473 } else if (e.shiftKey){
21474 newDate = this.moveMonth(this.date, dir);
21475 newViewDate = this.moveMonth(this.viewDate, dir);
21477 newDate = new Date(this.date);
21478 newDate.setUTCDate(this.date.getUTCDate() + dir);
21479 newViewDate = new Date(this.viewDate);
21480 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21482 if (this.dateWithinRange(newDate)){
21483 this.date = newDate;
21484 this.viewDate = newViewDate;
21485 this.setValue(this.formatDate(this.date));
21487 e.preventDefault();
21488 dateChanged = true;
21493 if (!this.keyboardNavigation) {
21496 dir = e.keyCode == 38 ? -1 : 1;
21498 newDate = this.moveYear(this.date, dir);
21499 newViewDate = this.moveYear(this.viewDate, dir);
21500 } else if (e.shiftKey){
21501 newDate = this.moveMonth(this.date, dir);
21502 newViewDate = this.moveMonth(this.viewDate, dir);
21504 newDate = new Date(this.date);
21505 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21506 newViewDate = new Date(this.viewDate);
21507 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21509 if (this.dateWithinRange(newDate)){
21510 this.date = newDate;
21511 this.viewDate = newViewDate;
21512 this.setValue(this.formatDate(this.date));
21514 e.preventDefault();
21515 dateChanged = true;
21519 this.setValue(this.formatDate(this.date));
21521 e.preventDefault();
21524 this.setValue(this.formatDate(this.date));
21538 onClick: function(e)
21540 e.stopPropagation();
21541 e.preventDefault();
21543 var target = e.getTarget();
21545 if(target.nodeName.toLowerCase() === 'i'){
21546 target = Roo.get(target).dom.parentNode;
21549 var nodeName = target.nodeName;
21550 var className = target.className;
21551 var html = target.innerHTML;
21552 //Roo.log(nodeName);
21554 switch(nodeName.toLowerCase()) {
21556 switch(className) {
21562 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21563 switch(this.viewMode){
21565 this.viewDate = this.moveMonth(this.viewDate, dir);
21569 this.viewDate = this.moveYear(this.viewDate, dir);
21575 var date = new Date();
21576 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21578 this.setValue(this.formatDate(this.date));
21585 if (className.indexOf('disabled') < 0) {
21586 this.viewDate.setUTCDate(1);
21587 if (className.indexOf('month') > -1) {
21588 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21590 var year = parseInt(html, 10) || 0;
21591 this.viewDate.setUTCFullYear(year);
21595 if(this.singleMode){
21596 this.setValue(this.formatDate(this.viewDate));
21607 //Roo.log(className);
21608 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21609 var day = parseInt(html, 10) || 1;
21610 var year = (this.viewDate || new Date()).getUTCFullYear(),
21611 month = (this.viewDate || new Date()).getUTCMonth();
21613 if (className.indexOf('old') > -1) {
21620 } else if (className.indexOf('new') > -1) {
21628 //Roo.log([year,month,day]);
21629 this.date = this.UTCDate(year, month, day,0,0,0,0);
21630 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21632 //Roo.log(this.formatDate(this.date));
21633 this.setValue(this.formatDate(this.date));
21640 setStartDate: function(startDate)
21642 this.startDate = startDate || -Infinity;
21643 if (this.startDate !== -Infinity) {
21644 this.startDate = this.parseDate(this.startDate);
21647 this.updateNavArrows();
21650 setEndDate: function(endDate)
21652 this.endDate = endDate || Infinity;
21653 if (this.endDate !== Infinity) {
21654 this.endDate = this.parseDate(this.endDate);
21657 this.updateNavArrows();
21660 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21662 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21663 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21664 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21666 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21667 return parseInt(d, 10);
21670 this.updateNavArrows();
21673 updateNavArrows: function()
21675 if(this.singleMode){
21679 var d = new Date(this.viewDate),
21680 year = d.getUTCFullYear(),
21681 month = d.getUTCMonth();
21683 Roo.each(this.picker().select('.prev', true).elements, function(v){
21685 switch (this.viewMode) {
21688 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21694 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21701 Roo.each(this.picker().select('.next', true).elements, function(v){
21703 switch (this.viewMode) {
21706 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21712 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21720 moveMonth: function(date, dir)
21725 var new_date = new Date(date.valueOf()),
21726 day = new_date.getUTCDate(),
21727 month = new_date.getUTCMonth(),
21728 mag = Math.abs(dir),
21730 dir = dir > 0 ? 1 : -1;
21733 // If going back one month, make sure month is not current month
21734 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21736 return new_date.getUTCMonth() == month;
21738 // If going forward one month, make sure month is as expected
21739 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21741 return new_date.getUTCMonth() != new_month;
21743 new_month = month + dir;
21744 new_date.setUTCMonth(new_month);
21745 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21746 if (new_month < 0 || new_month > 11) {
21747 new_month = (new_month + 12) % 12;
21750 // For magnitudes >1, move one month at a time...
21751 for (var i=0; i<mag; i++) {
21752 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21753 new_date = this.moveMonth(new_date, dir);
21755 // ...then reset the day, keeping it in the new month
21756 new_month = new_date.getUTCMonth();
21757 new_date.setUTCDate(day);
21759 return new_month != new_date.getUTCMonth();
21762 // Common date-resetting loop -- if date is beyond end of month, make it
21765 new_date.setUTCDate(--day);
21766 new_date.setUTCMonth(new_month);
21771 moveYear: function(date, dir)
21773 return this.moveMonth(date, dir*12);
21776 dateWithinRange: function(date)
21778 return date >= this.startDate && date <= this.endDate;
21784 this.picker().remove();
21787 validateValue : function(value)
21789 if(this.getVisibilityEl().hasClass('hidden')){
21793 if(value.length < 1) {
21794 if(this.allowBlank){
21800 if(value.length < this.minLength){
21803 if(value.length > this.maxLength){
21807 var vt = Roo.form.VTypes;
21808 if(!vt[this.vtype](value, this)){
21812 if(typeof this.validator == "function"){
21813 var msg = this.validator(value);
21819 if(this.regex && !this.regex.test(value)){
21823 if(typeof(this.parseDate(value)) == 'undefined'){
21827 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21831 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21841 this.date = this.viewDate = '';
21843 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21848 Roo.apply(Roo.bootstrap.DateField, {
21859 html: '<i class="fa fa-arrow-left"/>'
21869 html: '<i class="fa fa-arrow-right"/>'
21911 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21912 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21913 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21914 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21915 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21928 navFnc: 'FullYear',
21933 navFnc: 'FullYear',
21938 Roo.apply(Roo.bootstrap.DateField, {
21942 cls: 'datepicker dropdown-menu roo-dynamic shadow',
21946 cls: 'datepicker-days',
21950 cls: 'table-condensed',
21952 Roo.bootstrap.DateField.head,
21956 Roo.bootstrap.DateField.footer
21963 cls: 'datepicker-months',
21967 cls: 'table-condensed',
21969 Roo.bootstrap.DateField.head,
21970 Roo.bootstrap.DateField.content,
21971 Roo.bootstrap.DateField.footer
21978 cls: 'datepicker-years',
21982 cls: 'table-condensed',
21984 Roo.bootstrap.DateField.head,
21985 Roo.bootstrap.DateField.content,
21986 Roo.bootstrap.DateField.footer
22005 * @class Roo.bootstrap.TimeField
22006 * @extends Roo.bootstrap.Input
22007 * Bootstrap DateField class
22011 * Create a new TimeField
22012 * @param {Object} config The config object
22015 Roo.bootstrap.TimeField = function(config){
22016 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22020 * Fires when this field show.
22021 * @param {Roo.bootstrap.DateField} thisthis
22022 * @param {Mixed} date The date value
22027 * Fires when this field hide.
22028 * @param {Roo.bootstrap.DateField} this
22029 * @param {Mixed} date The date value
22034 * Fires when select a date.
22035 * @param {Roo.bootstrap.DateField} this
22036 * @param {Mixed} date The date value
22042 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22045 * @cfg {String} format
22046 * The default time format string which can be overriden for localization support. The format must be
22047 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22051 getAutoCreate : function()
22053 this.after = '<i class="fa far fa-clock"></i>';
22054 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22058 onRender: function(ct, position)
22061 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22063 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22065 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22067 this.pop = this.picker().select('>.datepicker-time',true).first();
22068 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22070 this.picker().on('mousedown', this.onMousedown, this);
22071 this.picker().on('click', this.onClick, this);
22073 this.picker().addClass('datepicker-dropdown');
22078 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22079 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22080 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22081 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22082 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22083 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22087 fireKey: function(e){
22088 if (!this.picker().isVisible()){
22089 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22095 e.preventDefault();
22103 this.onTogglePeriod();
22106 this.onIncrementMinutes();
22109 this.onDecrementMinutes();
22118 onClick: function(e) {
22119 e.stopPropagation();
22120 e.preventDefault();
22123 picker : function()
22125 return this.pickerEl;
22128 fillTime: function()
22130 var time = this.pop.select('tbody', true).first();
22132 time.dom.innerHTML = '';
22147 cls: 'hours-up fa fas fa-chevron-up'
22167 cls: 'minutes-up fa fas fa-chevron-up'
22188 cls: 'timepicker-hour',
22203 cls: 'timepicker-minute',
22218 cls: 'btn btn-primary period',
22240 cls: 'hours-down fa fas fa-chevron-down'
22260 cls: 'minutes-down fa fas fa-chevron-down'
22278 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22285 var hours = this.time.getHours();
22286 var minutes = this.time.getMinutes();
22299 hours = hours - 12;
22303 hours = '0' + hours;
22307 minutes = '0' + minutes;
22310 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22311 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22312 this.pop.select('button', true).first().dom.innerHTML = period;
22318 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22320 var cls = ['bottom'];
22322 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22329 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22333 //this.picker().setXY(20000,20000);
22334 this.picker().addClass(cls.join('-'));
22338 Roo.each(cls, function(c){
22343 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22344 //_this.picker().setTop(_this.inputEl().getHeight());
22348 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22350 //_this.picker().setTop(0 - _this.picker().getHeight());
22355 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22359 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22367 onFocus : function()
22369 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22373 onBlur : function()
22375 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22381 this.picker().show();
22386 this.fireEvent('show', this, this.date);
22391 this.picker().hide();
22394 this.fireEvent('hide', this, this.date);
22397 setTime : function()
22400 this.setValue(this.time.format(this.format));
22402 this.fireEvent('select', this, this.date);
22407 onMousedown: function(e){
22408 e.stopPropagation();
22409 e.preventDefault();
22412 onIncrementHours: function()
22414 Roo.log('onIncrementHours');
22415 this.time = this.time.add(Date.HOUR, 1);
22420 onDecrementHours: function()
22422 Roo.log('onDecrementHours');
22423 this.time = this.time.add(Date.HOUR, -1);
22427 onIncrementMinutes: function()
22429 Roo.log('onIncrementMinutes');
22430 this.time = this.time.add(Date.MINUTE, 1);
22434 onDecrementMinutes: function()
22436 Roo.log('onDecrementMinutes');
22437 this.time = this.time.add(Date.MINUTE, -1);
22441 onTogglePeriod: function()
22443 Roo.log('onTogglePeriod');
22444 this.time = this.time.add(Date.HOUR, 12);
22452 Roo.apply(Roo.bootstrap.TimeField, {
22456 cls: 'datepicker dropdown-menu',
22460 cls: 'datepicker-time',
22464 cls: 'table-condensed',
22493 cls: 'btn btn-info ok',
22521 * @class Roo.bootstrap.MonthField
22522 * @extends Roo.bootstrap.Input
22523 * Bootstrap MonthField class
22525 * @cfg {String} language default en
22528 * Create a new MonthField
22529 * @param {Object} config The config object
22532 Roo.bootstrap.MonthField = function(config){
22533 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22538 * Fires when this field show.
22539 * @param {Roo.bootstrap.MonthField} this
22540 * @param {Mixed} date The date value
22545 * Fires when this field hide.
22546 * @param {Roo.bootstrap.MonthField} this
22547 * @param {Mixed} date The date value
22552 * Fires when select a date.
22553 * @param {Roo.bootstrap.MonthField} this
22554 * @param {String} oldvalue The old value
22555 * @param {String} newvalue The new value
22561 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22563 onRender: function(ct, position)
22566 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22568 this.language = this.language || 'en';
22569 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22570 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22572 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22573 this.isInline = false;
22574 this.isInput = true;
22575 this.component = this.el.select('.add-on', true).first() || false;
22576 this.component = (this.component && this.component.length === 0) ? false : this.component;
22577 this.hasInput = this.component && this.inputEL().length;
22579 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22581 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22583 this.picker().on('mousedown', this.onMousedown, this);
22584 this.picker().on('click', this.onClick, this);
22586 this.picker().addClass('datepicker-dropdown');
22588 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22589 v.setStyle('width', '189px');
22596 if(this.isInline) {
22602 setValue: function(v, suppressEvent)
22604 var o = this.getValue();
22606 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22610 if(suppressEvent !== true){
22611 this.fireEvent('select', this, o, v);
22616 getValue: function()
22621 onClick: function(e)
22623 e.stopPropagation();
22624 e.preventDefault();
22626 var target = e.getTarget();
22628 if(target.nodeName.toLowerCase() === 'i'){
22629 target = Roo.get(target).dom.parentNode;
22632 var nodeName = target.nodeName;
22633 var className = target.className;
22634 var html = target.innerHTML;
22636 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22640 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22642 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22648 picker : function()
22650 return this.pickerEl;
22653 fillMonths: function()
22656 var months = this.picker().select('>.datepicker-months td', true).first();
22658 months.dom.innerHTML = '';
22664 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22667 months.createChild(month);
22676 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22677 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22680 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22681 e.removeClass('active');
22683 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22684 e.addClass('active');
22691 if(this.isInline) {
22695 this.picker().removeClass(['bottom', 'top']);
22697 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22699 * place to the top of element!
22703 this.picker().addClass('top');
22704 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22709 this.picker().addClass('bottom');
22711 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22714 onFocus : function()
22716 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22720 onBlur : function()
22722 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22724 var d = this.inputEl().getValue();
22733 this.picker().show();
22734 this.picker().select('>.datepicker-months', true).first().show();
22738 this.fireEvent('show', this, this.date);
22743 if(this.isInline) {
22746 this.picker().hide();
22747 this.fireEvent('hide', this, this.date);
22751 onMousedown: function(e)
22753 e.stopPropagation();
22754 e.preventDefault();
22759 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22763 fireKey: function(e)
22765 if (!this.picker().isVisible()){
22766 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22777 e.preventDefault();
22781 dir = e.keyCode == 37 ? -1 : 1;
22783 this.vIndex = this.vIndex + dir;
22785 if(this.vIndex < 0){
22789 if(this.vIndex > 11){
22793 if(isNaN(this.vIndex)){
22797 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22803 dir = e.keyCode == 38 ? -1 : 1;
22805 this.vIndex = this.vIndex + dir * 4;
22807 if(this.vIndex < 0){
22811 if(this.vIndex > 11){
22815 if(isNaN(this.vIndex)){
22819 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22824 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22825 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22829 e.preventDefault();
22832 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22833 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22849 this.picker().remove();
22854 Roo.apply(Roo.bootstrap.MonthField, {
22873 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22874 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22879 Roo.apply(Roo.bootstrap.MonthField, {
22883 cls: 'datepicker dropdown-menu roo-dynamic',
22887 cls: 'datepicker-months',
22891 cls: 'table-condensed',
22893 Roo.bootstrap.DateField.content
22913 * @class Roo.bootstrap.CheckBox
22914 * @extends Roo.bootstrap.Input
22915 * Bootstrap CheckBox class
22917 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22918 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22919 * @cfg {String} boxLabel The text that appears beside the checkbox
22920 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22921 * @cfg {Boolean} checked initnal the element
22922 * @cfg {Boolean} inline inline the element (default false)
22923 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22924 * @cfg {String} tooltip label tooltip
22927 * Create a new CheckBox
22928 * @param {Object} config The config object
22931 Roo.bootstrap.CheckBox = function(config){
22932 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22937 * Fires when the element is checked or unchecked.
22938 * @param {Roo.bootstrap.CheckBox} this This input
22939 * @param {Boolean} checked The new checked value
22944 * Fires when the element is click.
22945 * @param {Roo.bootstrap.CheckBox} this This input
22952 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
22954 inputType: 'checkbox',
22963 // checkbox success does not make any sense really..
22968 getAutoCreate : function()
22970 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22976 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22979 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
22985 type : this.inputType,
22986 value : this.inputValue,
22987 cls : 'roo-' + this.inputType, //'form-box',
22988 placeholder : this.placeholder || ''
22992 if(this.inputType != 'radio'){
22996 cls : 'roo-hidden-value',
22997 value : this.checked ? this.inputValue : this.valueOff
23002 if (this.weight) { // Validity check?
23003 cfg.cls += " " + this.inputType + "-" + this.weight;
23006 if (this.disabled) {
23007 input.disabled=true;
23011 input.checked = this.checked;
23016 input.name = this.name;
23018 if(this.inputType != 'radio'){
23019 hidden.name = this.name;
23020 input.name = '_hidden_' + this.name;
23025 input.cls += ' input-' + this.size;
23030 ['xs','sm','md','lg'].map(function(size){
23031 if (settings[size]) {
23032 cfg.cls += ' col-' + size + '-' + settings[size];
23036 var inputblock = input;
23038 if (this.before || this.after) {
23041 cls : 'input-group',
23046 inputblock.cn.push({
23048 cls : 'input-group-addon',
23053 inputblock.cn.push(input);
23055 if(this.inputType != 'radio'){
23056 inputblock.cn.push(hidden);
23060 inputblock.cn.push({
23062 cls : 'input-group-addon',
23068 var boxLabelCfg = false;
23074 //'for': id, // box label is handled by onclick - so no for...
23076 html: this.boxLabel
23079 boxLabelCfg.tooltip = this.tooltip;
23085 if (align ==='left' && this.fieldLabel.length) {
23086 // Roo.log("left and has label");
23091 cls : 'control-label',
23092 html : this.fieldLabel
23103 cfg.cn[1].cn.push(boxLabelCfg);
23106 if(this.labelWidth > 12){
23107 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23110 if(this.labelWidth < 13 && this.labelmd == 0){
23111 this.labelmd = this.labelWidth;
23114 if(this.labellg > 0){
23115 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23116 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23119 if(this.labelmd > 0){
23120 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23121 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23124 if(this.labelsm > 0){
23125 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23126 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23129 if(this.labelxs > 0){
23130 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23131 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23134 } else if ( this.fieldLabel.length) {
23135 // Roo.log(" label");
23139 tag: this.boxLabel ? 'span' : 'label',
23141 cls: 'control-label box-input-label',
23142 //cls : 'input-group-addon',
23143 html : this.fieldLabel
23150 cfg.cn.push(boxLabelCfg);
23155 // Roo.log(" no label && no align");
23156 cfg.cn = [ inputblock ] ;
23158 cfg.cn.push(boxLabelCfg);
23166 if(this.inputType != 'radio'){
23167 cfg.cn.push(hidden);
23175 * return the real input element.
23177 inputEl: function ()
23179 return this.el.select('input.roo-' + this.inputType,true).first();
23181 hiddenEl: function ()
23183 return this.el.select('input.roo-hidden-value',true).first();
23186 labelEl: function()
23188 return this.el.select('label.control-label',true).first();
23190 /* depricated... */
23194 return this.labelEl();
23197 boxLabelEl: function()
23199 return this.el.select('label.box-label',true).first();
23202 initEvents : function()
23204 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23206 this.inputEl().on('click', this.onClick, this);
23208 if (this.boxLabel) {
23209 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23212 this.startValue = this.getValue();
23215 Roo.bootstrap.CheckBox.register(this);
23219 onClick : function(e)
23221 if(this.fireEvent('click', this, e) !== false){
23222 this.setChecked(!this.checked);
23227 setChecked : function(state,suppressEvent)
23229 this.startValue = this.getValue();
23231 if(this.inputType == 'radio'){
23233 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23234 e.dom.checked = false;
23237 this.inputEl().dom.checked = true;
23239 this.inputEl().dom.value = this.inputValue;
23241 if(suppressEvent !== true){
23242 this.fireEvent('check', this, true);
23250 this.checked = state;
23252 this.inputEl().dom.checked = state;
23255 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23257 if(suppressEvent !== true){
23258 this.fireEvent('check', this, state);
23264 getValue : function()
23266 if(this.inputType == 'radio'){
23267 return this.getGroupValue();
23270 return this.hiddenEl().dom.value;
23274 getGroupValue : function()
23276 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23280 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23283 setValue : function(v,suppressEvent)
23285 if(this.inputType == 'radio'){
23286 this.setGroupValue(v, suppressEvent);
23290 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23295 setGroupValue : function(v, suppressEvent)
23297 this.startValue = this.getValue();
23299 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23300 e.dom.checked = false;
23302 if(e.dom.value == v){
23303 e.dom.checked = true;
23307 if(suppressEvent !== true){
23308 this.fireEvent('check', this, true);
23316 validate : function()
23318 if(this.getVisibilityEl().hasClass('hidden')){
23324 (this.inputType == 'radio' && this.validateRadio()) ||
23325 (this.inputType == 'checkbox' && this.validateCheckbox())
23331 this.markInvalid();
23335 validateRadio : function()
23337 if(this.getVisibilityEl().hasClass('hidden')){
23341 if(this.allowBlank){
23347 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23348 if(!e.dom.checked){
23360 validateCheckbox : function()
23363 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23364 //return (this.getValue() == this.inputValue) ? true : false;
23367 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23375 for(var i in group){
23376 if(group[i].el.isVisible(true)){
23384 for(var i in group){
23389 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23396 * Mark this field as valid
23398 markValid : function()
23402 this.fireEvent('valid', this);
23404 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23407 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23414 if(this.inputType == 'radio'){
23415 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23416 var fg = e.findParent('.form-group', false, true);
23417 if (Roo.bootstrap.version == 3) {
23418 fg.removeClass([_this.invalidClass, _this.validClass]);
23419 fg.addClass(_this.validClass);
23421 fg.removeClass(['is-valid', 'is-invalid']);
23422 fg.addClass('is-valid');
23430 var fg = this.el.findParent('.form-group', false, true);
23431 if (Roo.bootstrap.version == 3) {
23432 fg.removeClass([this.invalidClass, this.validClass]);
23433 fg.addClass(this.validClass);
23435 fg.removeClass(['is-valid', 'is-invalid']);
23436 fg.addClass('is-valid');
23441 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23447 for(var i in group){
23448 var fg = group[i].el.findParent('.form-group', false, true);
23449 if (Roo.bootstrap.version == 3) {
23450 fg.removeClass([this.invalidClass, this.validClass]);
23451 fg.addClass(this.validClass);
23453 fg.removeClass(['is-valid', 'is-invalid']);
23454 fg.addClass('is-valid');
23460 * Mark this field as invalid
23461 * @param {String} msg The validation message
23463 markInvalid : function(msg)
23465 if(this.allowBlank){
23471 this.fireEvent('invalid', this, msg);
23473 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23476 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23480 label.markInvalid();
23483 if(this.inputType == 'radio'){
23485 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23486 var fg = e.findParent('.form-group', false, true);
23487 if (Roo.bootstrap.version == 3) {
23488 fg.removeClass([_this.invalidClass, _this.validClass]);
23489 fg.addClass(_this.invalidClass);
23491 fg.removeClass(['is-invalid', 'is-valid']);
23492 fg.addClass('is-invalid');
23500 var fg = this.el.findParent('.form-group', false, true);
23501 if (Roo.bootstrap.version == 3) {
23502 fg.removeClass([_this.invalidClass, _this.validClass]);
23503 fg.addClass(_this.invalidClass);
23505 fg.removeClass(['is-invalid', 'is-valid']);
23506 fg.addClass('is-invalid');
23511 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23517 for(var i in group){
23518 var fg = group[i].el.findParent('.form-group', false, true);
23519 if (Roo.bootstrap.version == 3) {
23520 fg.removeClass([_this.invalidClass, _this.validClass]);
23521 fg.addClass(_this.invalidClass);
23523 fg.removeClass(['is-invalid', 'is-valid']);
23524 fg.addClass('is-invalid');
23530 clearInvalid : function()
23532 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23534 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23536 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23538 if (label && label.iconEl) {
23539 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23540 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23544 disable : function()
23546 if(this.inputType != 'radio'){
23547 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23554 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23555 _this.getActionEl().addClass(this.disabledClass);
23556 e.dom.disabled = true;
23560 this.disabled = true;
23561 this.fireEvent("disable", this);
23565 enable : function()
23567 if(this.inputType != 'radio'){
23568 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23575 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23576 _this.getActionEl().removeClass(this.disabledClass);
23577 e.dom.disabled = false;
23581 this.disabled = false;
23582 this.fireEvent("enable", this);
23586 setBoxLabel : function(v)
23591 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23597 Roo.apply(Roo.bootstrap.CheckBox, {
23602 * register a CheckBox Group
23603 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23605 register : function(checkbox)
23607 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23608 this.groups[checkbox.groupId] = {};
23611 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23615 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23619 * fetch a CheckBox Group based on the group ID
23620 * @param {string} the group ID
23621 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23623 get: function(groupId) {
23624 if (typeof(this.groups[groupId]) == 'undefined') {
23628 return this.groups[groupId] ;
23641 * @class Roo.bootstrap.Radio
23642 * @extends Roo.bootstrap.Component
23643 * Bootstrap Radio class
23644 * @cfg {String} boxLabel - the label associated
23645 * @cfg {String} value - the value of radio
23648 * Create a new Radio
23649 * @param {Object} config The config object
23651 Roo.bootstrap.Radio = function(config){
23652 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23656 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23662 getAutoCreate : function()
23666 cls : 'form-group radio',
23671 html : this.boxLabel
23679 initEvents : function()
23681 this.parent().register(this);
23683 this.el.on('click', this.onClick, this);
23687 onClick : function(e)
23689 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23690 this.setChecked(true);
23694 setChecked : function(state, suppressEvent)
23696 this.parent().setValue(this.value, suppressEvent);
23700 setBoxLabel : function(v)
23705 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23720 * @class Roo.bootstrap.SecurePass
23721 * @extends Roo.bootstrap.Input
23722 * Bootstrap SecurePass class
23726 * Create a new SecurePass
23727 * @param {Object} config The config object
23730 Roo.bootstrap.SecurePass = function (config) {
23731 // these go here, so the translation tool can replace them..
23733 PwdEmpty: "Please type a password, and then retype it to confirm.",
23734 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23735 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23736 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23737 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23738 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23739 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23740 TooWeak: "Your password is Too Weak."
23742 this.meterLabel = "Password strength:";
23743 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23744 this.meterClass = [
23745 "roo-password-meter-tooweak",
23746 "roo-password-meter-weak",
23747 "roo-password-meter-medium",
23748 "roo-password-meter-strong",
23749 "roo-password-meter-grey"
23754 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23757 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23759 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23761 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23762 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23763 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23764 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23765 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23766 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23767 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23777 * @cfg {String/Object} Label for the strength meter (defaults to
23778 * 'Password strength:')
23783 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23784 * ['Weak', 'Medium', 'Strong'])
23787 pwdStrengths: false,
23800 initEvents: function ()
23802 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23804 if (this.el.is('input[type=password]') && Roo.isSafari) {
23805 this.el.on('keydown', this.SafariOnKeyDown, this);
23808 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23811 onRender: function (ct, position)
23813 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23814 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23815 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23817 this.trigger.createChild({
23822 cls: 'roo-password-meter-grey col-xs-12',
23825 //width: this.meterWidth + 'px'
23829 cls: 'roo-password-meter-text'
23835 if (this.hideTrigger) {
23836 this.trigger.setDisplayed(false);
23838 this.setSize(this.width || '', this.height || '');
23841 onDestroy: function ()
23843 if (this.trigger) {
23844 this.trigger.removeAllListeners();
23845 this.trigger.remove();
23848 this.wrap.remove();
23850 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23853 checkStrength: function ()
23855 var pwd = this.inputEl().getValue();
23856 if (pwd == this._lastPwd) {
23861 if (this.ClientSideStrongPassword(pwd)) {
23863 } else if (this.ClientSideMediumPassword(pwd)) {
23865 } else if (this.ClientSideWeakPassword(pwd)) {
23871 Roo.log('strength1: ' + strength);
23873 //var pm = this.trigger.child('div/div/div').dom;
23874 var pm = this.trigger.child('div/div');
23875 pm.removeClass(this.meterClass);
23876 pm.addClass(this.meterClass[strength]);
23879 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23881 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23883 this._lastPwd = pwd;
23887 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23889 this._lastPwd = '';
23891 var pm = this.trigger.child('div/div');
23892 pm.removeClass(this.meterClass);
23893 pm.addClass('roo-password-meter-grey');
23896 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23899 this.inputEl().dom.type='password';
23902 validateValue: function (value)
23904 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23907 if (value.length == 0) {
23908 if (this.allowBlank) {
23909 this.clearInvalid();
23913 this.markInvalid(this.errors.PwdEmpty);
23914 this.errorMsg = this.errors.PwdEmpty;
23922 if (!value.match(/[\x21-\x7e]+/)) {
23923 this.markInvalid(this.errors.PwdBadChar);
23924 this.errorMsg = this.errors.PwdBadChar;
23927 if (value.length < 6) {
23928 this.markInvalid(this.errors.PwdShort);
23929 this.errorMsg = this.errors.PwdShort;
23932 if (value.length > 16) {
23933 this.markInvalid(this.errors.PwdLong);
23934 this.errorMsg = this.errors.PwdLong;
23938 if (this.ClientSideStrongPassword(value)) {
23940 } else if (this.ClientSideMediumPassword(value)) {
23942 } else if (this.ClientSideWeakPassword(value)) {
23949 if (strength < 2) {
23950 //this.markInvalid(this.errors.TooWeak);
23951 this.errorMsg = this.errors.TooWeak;
23956 console.log('strength2: ' + strength);
23958 //var pm = this.trigger.child('div/div/div').dom;
23960 var pm = this.trigger.child('div/div');
23961 pm.removeClass(this.meterClass);
23962 pm.addClass(this.meterClass[strength]);
23964 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23966 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23968 this.errorMsg = '';
23972 CharacterSetChecks: function (type)
23975 this.fResult = false;
23978 isctype: function (character, type)
23981 case this.kCapitalLetter:
23982 if (character >= 'A' && character <= 'Z') {
23987 case this.kSmallLetter:
23988 if (character >= 'a' && character <= 'z') {
23994 if (character >= '0' && character <= '9') {
23999 case this.kPunctuation:
24000 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24011 IsLongEnough: function (pwd, size)
24013 return !(pwd == null || isNaN(size) || pwd.length < size);
24016 SpansEnoughCharacterSets: function (word, nb)
24018 if (!this.IsLongEnough(word, nb))
24023 var characterSetChecks = new Array(
24024 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24025 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24028 for (var index = 0; index < word.length; ++index) {
24029 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24030 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24031 characterSetChecks[nCharSet].fResult = true;
24038 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24039 if (characterSetChecks[nCharSet].fResult) {
24044 if (nCharSets < nb) {
24050 ClientSideStrongPassword: function (pwd)
24052 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24055 ClientSideMediumPassword: function (pwd)
24057 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24060 ClientSideWeakPassword: function (pwd)
24062 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24065 })//<script type="text/javascript">
24068 * Based Ext JS Library 1.1.1
24069 * Copyright(c) 2006-2007, Ext JS, LLC.
24075 * @class Roo.HtmlEditorCore
24076 * @extends Roo.Component
24077 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24079 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24082 Roo.HtmlEditorCore = function(config){
24085 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24090 * @event initialize
24091 * Fires when the editor is fully initialized (including the iframe)
24092 * @param {Roo.HtmlEditorCore} this
24097 * Fires when the editor is first receives the focus. Any insertion must wait
24098 * until after this event.
24099 * @param {Roo.HtmlEditorCore} this
24103 * @event beforesync
24104 * Fires before the textarea is updated with content from the editor iframe. Return false
24105 * to cancel the sync.
24106 * @param {Roo.HtmlEditorCore} this
24107 * @param {String} html
24111 * @event beforepush
24112 * Fires before the iframe editor is updated with content from the textarea. Return false
24113 * to cancel the push.
24114 * @param {Roo.HtmlEditorCore} this
24115 * @param {String} html
24120 * Fires when the textarea is updated with content from the editor iframe.
24121 * @param {Roo.HtmlEditorCore} this
24122 * @param {String} html
24127 * Fires when the iframe editor is updated with content from the textarea.
24128 * @param {Roo.HtmlEditorCore} this
24129 * @param {String} html
24134 * @event editorevent
24135 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24136 * @param {Roo.HtmlEditorCore} this
24142 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24144 // defaults : white / black...
24145 this.applyBlacklists();
24152 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24156 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24162 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24167 * @cfg {Number} height (in pixels)
24171 * @cfg {Number} width (in pixels)
24176 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24179 stylesheets: false,
24184 // private properties
24185 validationEvent : false,
24187 initialized : false,
24189 sourceEditMode : false,
24190 onFocus : Roo.emptyFn,
24192 hideMode:'offsets',
24196 // blacklist + whitelisted elements..
24203 * Protected method that will not generally be called directly. It
24204 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24205 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24207 getDocMarkup : function(){
24211 // inherit styels from page...??
24212 if (this.stylesheets === false) {
24214 Roo.get(document.head).select('style').each(function(node) {
24215 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24218 Roo.get(document.head).select('link').each(function(node) {
24219 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24222 } else if (!this.stylesheets.length) {
24224 st = '<style type="text/css">' +
24225 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24228 for (var i in this.stylesheets) {
24229 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24234 st += '<style type="text/css">' +
24235 'IMG { cursor: pointer } ' +
24238 var cls = 'roo-htmleditor-body';
24240 if(this.bodyCls.length){
24241 cls += ' ' + this.bodyCls;
24244 return '<html><head>' + st +
24245 //<style type="text/css">' +
24246 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24248 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24252 onRender : function(ct, position)
24255 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24256 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24259 this.el.dom.style.border = '0 none';
24260 this.el.dom.setAttribute('tabIndex', -1);
24261 this.el.addClass('x-hidden hide');
24265 if(Roo.isIE){ // fix IE 1px bogus margin
24266 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24270 this.frameId = Roo.id();
24274 var iframe = this.owner.wrap.createChild({
24276 cls: 'form-control', // bootstrap..
24278 name: this.frameId,
24279 frameBorder : 'no',
24280 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24285 this.iframe = iframe.dom;
24287 this.assignDocWin();
24289 this.doc.designMode = 'on';
24292 this.doc.write(this.getDocMarkup());
24296 var task = { // must defer to wait for browser to be ready
24298 //console.log("run task?" + this.doc.readyState);
24299 this.assignDocWin();
24300 if(this.doc.body || this.doc.readyState == 'complete'){
24302 this.doc.designMode="on";
24306 Roo.TaskMgr.stop(task);
24307 this.initEditor.defer(10, this);
24314 Roo.TaskMgr.start(task);
24319 onResize : function(w, h)
24321 Roo.log('resize: ' +w + ',' + h );
24322 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24326 if(typeof w == 'number'){
24328 this.iframe.style.width = w + 'px';
24330 if(typeof h == 'number'){
24332 this.iframe.style.height = h + 'px';
24334 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24341 * Toggles the editor between standard and source edit mode.
24342 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24344 toggleSourceEdit : function(sourceEditMode){
24346 this.sourceEditMode = sourceEditMode === true;
24348 if(this.sourceEditMode){
24350 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24353 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24354 //this.iframe.className = '';
24357 //this.setSize(this.owner.wrap.getSize());
24358 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24365 * Protected method that will not generally be called directly. If you need/want
24366 * custom HTML cleanup, this is the method you should override.
24367 * @param {String} html The HTML to be cleaned
24368 * return {String} The cleaned HTML
24370 cleanHtml : function(html){
24371 html = String(html);
24372 if(html.length > 5){
24373 if(Roo.isSafari){ // strip safari nonsense
24374 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24377 if(html == ' '){
24384 * HTML Editor -> Textarea
24385 * Protected method that will not generally be called directly. Syncs the contents
24386 * of the editor iframe with the textarea.
24388 syncValue : function(){
24389 if(this.initialized){
24390 var bd = (this.doc.body || this.doc.documentElement);
24391 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24392 var html = bd.innerHTML;
24394 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24395 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24397 html = '<div style="'+m[0]+'">' + html + '</div>';
24400 html = this.cleanHtml(html);
24401 // fix up the special chars.. normaly like back quotes in word...
24402 // however we do not want to do this with chinese..
24403 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24405 var cc = match.charCodeAt();
24407 // Get the character value, handling surrogate pairs
24408 if (match.length == 2) {
24409 // It's a surrogate pair, calculate the Unicode code point
24410 var high = match.charCodeAt(0) - 0xD800;
24411 var low = match.charCodeAt(1) - 0xDC00;
24412 cc = (high * 0x400) + low + 0x10000;
24414 (cc >= 0x4E00 && cc < 0xA000 ) ||
24415 (cc >= 0x3400 && cc < 0x4E00 ) ||
24416 (cc >= 0xf900 && cc < 0xfb00 )
24421 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24422 return "&#" + cc + ";";
24429 if(this.owner.fireEvent('beforesync', this, html) !== false){
24430 this.el.dom.value = html;
24431 this.owner.fireEvent('sync', this, html);
24437 * Protected method that will not generally be called directly. Pushes the value of the textarea
24438 * into the iframe editor.
24440 pushValue : function(){
24441 if(this.initialized){
24442 var v = this.el.dom.value.trim();
24444 // if(v.length < 1){
24448 if(this.owner.fireEvent('beforepush', this, v) !== false){
24449 var d = (this.doc.body || this.doc.documentElement);
24451 this.cleanUpPaste();
24452 this.el.dom.value = d.innerHTML;
24453 this.owner.fireEvent('push', this, v);
24459 deferFocus : function(){
24460 this.focus.defer(10, this);
24464 focus : function(){
24465 if(this.win && !this.sourceEditMode){
24472 assignDocWin: function()
24474 var iframe = this.iframe;
24477 this.doc = iframe.contentWindow.document;
24478 this.win = iframe.contentWindow;
24480 // if (!Roo.get(this.frameId)) {
24483 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24484 // this.win = Roo.get(this.frameId).dom.contentWindow;
24486 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24490 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24491 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24496 initEditor : function(){
24497 //console.log("INIT EDITOR");
24498 this.assignDocWin();
24502 this.doc.designMode="on";
24504 this.doc.write(this.getDocMarkup());
24507 var dbody = (this.doc.body || this.doc.documentElement);
24508 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24509 // this copies styles from the containing element into thsi one..
24510 // not sure why we need all of this..
24511 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24513 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24514 //ss['background-attachment'] = 'fixed'; // w3c
24515 dbody.bgProperties = 'fixed'; // ie
24516 //Roo.DomHelper.applyStyles(dbody, ss);
24517 Roo.EventManager.on(this.doc, {
24518 //'mousedown': this.onEditorEvent,
24519 'mouseup': this.onEditorEvent,
24520 'dblclick': this.onEditorEvent,
24521 'click': this.onEditorEvent,
24522 'keyup': this.onEditorEvent,
24527 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24529 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24530 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24532 this.initialized = true;
24534 this.owner.fireEvent('initialize', this);
24539 onDestroy : function(){
24545 //for (var i =0; i < this.toolbars.length;i++) {
24546 // // fixme - ask toolbars for heights?
24547 // this.toolbars[i].onDestroy();
24550 //this.wrap.dom.innerHTML = '';
24551 //this.wrap.remove();
24556 onFirstFocus : function(){
24558 this.assignDocWin();
24561 this.activated = true;
24564 if(Roo.isGecko){ // prevent silly gecko errors
24566 var s = this.win.getSelection();
24567 if(!s.focusNode || s.focusNode.nodeType != 3){
24568 var r = s.getRangeAt(0);
24569 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24574 this.execCmd('useCSS', true);
24575 this.execCmd('styleWithCSS', false);
24578 this.owner.fireEvent('activate', this);
24582 adjustFont: function(btn){
24583 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24584 //if(Roo.isSafari){ // safari
24587 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24588 if(Roo.isSafari){ // safari
24589 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24590 v = (v < 10) ? 10 : v;
24591 v = (v > 48) ? 48 : v;
24592 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24597 v = Math.max(1, v+adjust);
24599 this.execCmd('FontSize', v );
24602 onEditorEvent : function(e)
24604 this.owner.fireEvent('editorevent', this, e);
24605 // this.updateToolbar();
24606 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24609 insertTag : function(tg)
24611 // could be a bit smarter... -> wrap the current selected tRoo..
24612 if (tg.toLowerCase() == 'span' ||
24613 tg.toLowerCase() == 'code' ||
24614 tg.toLowerCase() == 'sup' ||
24615 tg.toLowerCase() == 'sub'
24618 range = this.createRange(this.getSelection());
24619 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24620 wrappingNode.appendChild(range.extractContents());
24621 range.insertNode(wrappingNode);
24628 this.execCmd("formatblock", tg);
24632 insertText : function(txt)
24636 var range = this.createRange();
24637 range.deleteContents();
24638 //alert(Sender.getAttribute('label'));
24640 range.insertNode(this.doc.createTextNode(txt));
24646 * Executes a Midas editor command on the editor document and performs necessary focus and
24647 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24648 * @param {String} cmd The Midas command
24649 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24651 relayCmd : function(cmd, value){
24653 this.execCmd(cmd, value);
24654 this.owner.fireEvent('editorevent', this);
24655 //this.updateToolbar();
24656 this.owner.deferFocus();
24660 * Executes a Midas editor command directly on the editor document.
24661 * For visual commands, you should use {@link #relayCmd} instead.
24662 * <b>This should only be called after the editor is initialized.</b>
24663 * @param {String} cmd The Midas command
24664 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24666 execCmd : function(cmd, value){
24667 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24674 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24676 * @param {String} text | dom node..
24678 insertAtCursor : function(text)
24681 if(!this.activated){
24687 var r = this.doc.selection.createRange();
24698 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24702 // from jquery ui (MIT licenced)
24704 var win = this.win;
24706 if (win.getSelection && win.getSelection().getRangeAt) {
24707 range = win.getSelection().getRangeAt(0);
24708 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24709 range.insertNode(node);
24710 } else if (win.document.selection && win.document.selection.createRange) {
24711 // no firefox support
24712 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24713 win.document.selection.createRange().pasteHTML(txt);
24715 // no firefox support
24716 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24717 this.execCmd('InsertHTML', txt);
24726 mozKeyPress : function(e){
24728 var c = e.getCharCode(), cmd;
24731 c = String.fromCharCode(c).toLowerCase();
24745 this.cleanUpPaste.defer(100, this);
24753 e.preventDefault();
24761 fixKeys : function(){ // load time branching for fastest keydown performance
24763 return function(e){
24764 var k = e.getKey(), r;
24767 r = this.doc.selection.createRange();
24770 r.pasteHTML('    ');
24777 r = this.doc.selection.createRange();
24779 var target = r.parentElement();
24780 if(!target || target.tagName.toLowerCase() != 'li'){
24782 r.pasteHTML('<br />');
24788 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24789 this.cleanUpPaste.defer(100, this);
24795 }else if(Roo.isOpera){
24796 return function(e){
24797 var k = e.getKey();
24801 this.execCmd('InsertHTML','    ');
24804 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24805 this.cleanUpPaste.defer(100, this);
24810 }else if(Roo.isSafari){
24811 return function(e){
24812 var k = e.getKey();
24816 this.execCmd('InsertText','\t');
24820 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24821 this.cleanUpPaste.defer(100, this);
24829 getAllAncestors: function()
24831 var p = this.getSelectedNode();
24834 a.push(p); // push blank onto stack..
24835 p = this.getParentElement();
24839 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24843 a.push(this.doc.body);
24847 lastSelNode : false,
24850 getSelection : function()
24852 this.assignDocWin();
24853 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24856 getSelectedNode: function()
24858 // this may only work on Gecko!!!
24860 // should we cache this!!!!
24865 var range = this.createRange(this.getSelection()).cloneRange();
24868 var parent = range.parentElement();
24870 var testRange = range.duplicate();
24871 testRange.moveToElementText(parent);
24872 if (testRange.inRange(range)) {
24875 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24878 parent = parent.parentElement;
24883 // is ancestor a text element.
24884 var ac = range.commonAncestorContainer;
24885 if (ac.nodeType == 3) {
24886 ac = ac.parentNode;
24889 var ar = ac.childNodes;
24892 var other_nodes = [];
24893 var has_other_nodes = false;
24894 for (var i=0;i<ar.length;i++) {
24895 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24898 // fullly contained node.
24900 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24905 // probably selected..
24906 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24907 other_nodes.push(ar[i]);
24911 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
24916 has_other_nodes = true;
24918 if (!nodes.length && other_nodes.length) {
24919 nodes= other_nodes;
24921 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24927 createRange: function(sel)
24929 // this has strange effects when using with
24930 // top toolbar - not sure if it's a great idea.
24931 //this.editor.contentWindow.focus();
24932 if (typeof sel != "undefined") {
24934 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24936 return this.doc.createRange();
24939 return this.doc.createRange();
24942 getParentElement: function()
24945 this.assignDocWin();
24946 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24948 var range = this.createRange(sel);
24951 var p = range.commonAncestorContainer;
24952 while (p.nodeType == 3) { // text node
24963 * Range intersection.. the hard stuff...
24967 * [ -- selected range --- ]
24971 * if end is before start or hits it. fail.
24972 * if start is after end or hits it fail.
24974 * if either hits (but other is outside. - then it's not
24980 // @see http://www.thismuchiknow.co.uk/?p=64.
24981 rangeIntersectsNode : function(range, node)
24983 var nodeRange = node.ownerDocument.createRange();
24985 nodeRange.selectNode(node);
24987 nodeRange.selectNodeContents(node);
24990 var rangeStartRange = range.cloneRange();
24991 rangeStartRange.collapse(true);
24993 var rangeEndRange = range.cloneRange();
24994 rangeEndRange.collapse(false);
24996 var nodeStartRange = nodeRange.cloneRange();
24997 nodeStartRange.collapse(true);
24999 var nodeEndRange = nodeRange.cloneRange();
25000 nodeEndRange.collapse(false);
25002 return rangeStartRange.compareBoundaryPoints(
25003 Range.START_TO_START, nodeEndRange) == -1 &&
25004 rangeEndRange.compareBoundaryPoints(
25005 Range.START_TO_START, nodeStartRange) == 1;
25009 rangeCompareNode : function(range, node)
25011 var nodeRange = node.ownerDocument.createRange();
25013 nodeRange.selectNode(node);
25015 nodeRange.selectNodeContents(node);
25019 range.collapse(true);
25021 nodeRange.collapse(true);
25023 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25024 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25026 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25028 var nodeIsBefore = ss == 1;
25029 var nodeIsAfter = ee == -1;
25031 if (nodeIsBefore && nodeIsAfter) {
25034 if (!nodeIsBefore && nodeIsAfter) {
25035 return 1; //right trailed.
25038 if (nodeIsBefore && !nodeIsAfter) {
25039 return 2; // left trailed.
25045 // private? - in a new class?
25046 cleanUpPaste : function()
25048 // cleans up the whole document..
25049 Roo.log('cleanuppaste');
25051 this.cleanUpChildren(this.doc.body);
25052 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25053 if (clean != this.doc.body.innerHTML) {
25054 this.doc.body.innerHTML = clean;
25059 cleanWordChars : function(input) {// change the chars to hex code
25060 var he = Roo.HtmlEditorCore;
25062 var output = input;
25063 Roo.each(he.swapCodes, function(sw) {
25064 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25066 output = output.replace(swapper, sw[1]);
25073 cleanUpChildren : function (n)
25075 if (!n.childNodes.length) {
25078 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25079 this.cleanUpChild(n.childNodes[i]);
25086 cleanUpChild : function (node)
25089 //console.log(node);
25090 if (node.nodeName == "#text") {
25091 // clean up silly Windows -- stuff?
25094 if (node.nodeName == "#comment") {
25095 node.parentNode.removeChild(node);
25096 // clean up silly Windows -- stuff?
25099 var lcname = node.tagName.toLowerCase();
25100 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25101 // whitelist of tags..
25103 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25105 node.parentNode.removeChild(node);
25110 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25112 // spans with no attributes - just remove them..
25113 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25114 remove_keep_children = true;
25117 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25118 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25120 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25121 // remove_keep_children = true;
25124 if (remove_keep_children) {
25125 this.cleanUpChildren(node);
25126 // inserts everything just before this node...
25127 while (node.childNodes.length) {
25128 var cn = node.childNodes[0];
25129 node.removeChild(cn);
25130 node.parentNode.insertBefore(cn, node);
25132 node.parentNode.removeChild(node);
25136 if (!node.attributes || !node.attributes.length) {
25141 this.cleanUpChildren(node);
25145 function cleanAttr(n,v)
25148 if (v.match(/^\./) || v.match(/^\//)) {
25151 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25154 if (v.match(/^#/)) {
25157 if (v.match(/^\{/)) { // allow template editing.
25160 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25161 node.removeAttribute(n);
25165 var cwhite = this.cwhite;
25166 var cblack = this.cblack;
25168 function cleanStyle(n,v)
25170 if (v.match(/expression/)) { //XSS?? should we even bother..
25171 node.removeAttribute(n);
25175 var parts = v.split(/;/);
25178 Roo.each(parts, function(p) {
25179 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25183 var l = p.split(':').shift().replace(/\s+/g,'');
25184 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25186 if ( cwhite.length && cblack.indexOf(l) > -1) {
25187 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25188 //node.removeAttribute(n);
25192 // only allow 'c whitelisted system attributes'
25193 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25194 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25195 //node.removeAttribute(n);
25205 if (clean.length) {
25206 node.setAttribute(n, clean.join(';'));
25208 node.removeAttribute(n);
25214 for (var i = node.attributes.length-1; i > -1 ; i--) {
25215 var a = node.attributes[i];
25218 if (a.name.toLowerCase().substr(0,2)=='on') {
25219 node.removeAttribute(a.name);
25222 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25223 node.removeAttribute(a.name);
25226 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25227 cleanAttr(a.name,a.value); // fixme..
25230 if (a.name == 'style') {
25231 cleanStyle(a.name,a.value);
25234 /// clean up MS crap..
25235 // tecnically this should be a list of valid class'es..
25238 if (a.name == 'class') {
25239 if (a.value.match(/^Mso/)) {
25240 node.removeAttribute('class');
25243 if (a.value.match(/^body$/)) {
25244 node.removeAttribute('class');
25255 this.cleanUpChildren(node);
25261 * Clean up MS wordisms...
25263 cleanWord : function(node)
25266 this.cleanWord(this.doc.body);
25271 node.nodeName == 'SPAN' &&
25272 !node.hasAttributes() &&
25273 node.childNodes.length == 1 &&
25274 node.firstChild.nodeName == "#text"
25276 var textNode = node.firstChild;
25277 node.removeChild(textNode);
25278 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25279 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25281 node.parentNode.insertBefore(textNode, node);
25282 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25283 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25285 node.parentNode.removeChild(node);
25288 if (node.nodeName == "#text") {
25289 // clean up silly Windows -- stuff?
25292 if (node.nodeName == "#comment") {
25293 node.parentNode.removeChild(node);
25294 // clean up silly Windows -- stuff?
25298 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25299 node.parentNode.removeChild(node);
25302 //Roo.log(node.tagName);
25303 // remove - but keep children..
25304 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25305 //Roo.log('-- removed');
25306 while (node.childNodes.length) {
25307 var cn = node.childNodes[0];
25308 node.removeChild(cn);
25309 node.parentNode.insertBefore(cn, node);
25310 // move node to parent - and clean it..
25311 this.cleanWord(cn);
25313 node.parentNode.removeChild(node);
25314 /// no need to iterate chidlren = it's got none..
25315 //this.iterateChildren(node, this.cleanWord);
25319 if (node.className.length) {
25321 var cn = node.className.split(/\W+/);
25323 Roo.each(cn, function(cls) {
25324 if (cls.match(/Mso[a-zA-Z]+/)) {
25329 node.className = cna.length ? cna.join(' ') : '';
25331 node.removeAttribute("class");
25335 if (node.hasAttribute("lang")) {
25336 node.removeAttribute("lang");
25339 if (node.hasAttribute("style")) {
25341 var styles = node.getAttribute("style").split(";");
25343 Roo.each(styles, function(s) {
25344 if (!s.match(/:/)) {
25347 var kv = s.split(":");
25348 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25351 // what ever is left... we allow.
25354 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25355 if (!nstyle.length) {
25356 node.removeAttribute('style');
25359 this.iterateChildren(node, this.cleanWord);
25365 * iterateChildren of a Node, calling fn each time, using this as the scole..
25366 * @param {DomNode} node node to iterate children of.
25367 * @param {Function} fn method of this class to call on each item.
25369 iterateChildren : function(node, fn)
25371 if (!node.childNodes.length) {
25374 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25375 fn.call(this, node.childNodes[i])
25381 * cleanTableWidths.
25383 * Quite often pasting from word etc.. results in tables with column and widths.
25384 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25387 cleanTableWidths : function(node)
25392 this.cleanTableWidths(this.doc.body);
25397 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25400 Roo.log(node.tagName);
25401 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25402 this.iterateChildren(node, this.cleanTableWidths);
25405 if (node.hasAttribute('width')) {
25406 node.removeAttribute('width');
25410 if (node.hasAttribute("style")) {
25413 var styles = node.getAttribute("style").split(";");
25415 Roo.each(styles, function(s) {
25416 if (!s.match(/:/)) {
25419 var kv = s.split(":");
25420 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25423 // what ever is left... we allow.
25426 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25427 if (!nstyle.length) {
25428 node.removeAttribute('style');
25432 this.iterateChildren(node, this.cleanTableWidths);
25440 domToHTML : function(currentElement, depth, nopadtext) {
25442 depth = depth || 0;
25443 nopadtext = nopadtext || false;
25445 if (!currentElement) {
25446 return this.domToHTML(this.doc.body);
25449 //Roo.log(currentElement);
25451 var allText = false;
25452 var nodeName = currentElement.nodeName;
25453 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25455 if (nodeName == '#text') {
25457 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25462 if (nodeName != 'BODY') {
25465 // Prints the node tagName, such as <A>, <IMG>, etc
25468 for(i = 0; i < currentElement.attributes.length;i++) {
25470 var aname = currentElement.attributes.item(i).name;
25471 if (!currentElement.attributes.item(i).value.length) {
25474 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25477 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25486 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25489 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25494 // Traverse the tree
25496 var currentElementChild = currentElement.childNodes.item(i);
25497 var allText = true;
25498 var innerHTML = '';
25500 while (currentElementChild) {
25501 // Formatting code (indent the tree so it looks nice on the screen)
25502 var nopad = nopadtext;
25503 if (lastnode == 'SPAN') {
25507 if (currentElementChild.nodeName == '#text') {
25508 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25509 toadd = nopadtext ? toadd : toadd.trim();
25510 if (!nopad && toadd.length > 80) {
25511 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25513 innerHTML += toadd;
25516 currentElementChild = currentElement.childNodes.item(i);
25522 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25524 // Recursively traverse the tree structure of the child node
25525 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25526 lastnode = currentElementChild.nodeName;
25528 currentElementChild=currentElement.childNodes.item(i);
25534 // The remaining code is mostly for formatting the tree
25535 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25540 ret+= "</"+tagName+">";
25546 applyBlacklists : function()
25548 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25549 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25553 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25554 if (b.indexOf(tag) > -1) {
25557 this.white.push(tag);
25561 Roo.each(w, function(tag) {
25562 if (b.indexOf(tag) > -1) {
25565 if (this.white.indexOf(tag) > -1) {
25568 this.white.push(tag);
25573 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25574 if (w.indexOf(tag) > -1) {
25577 this.black.push(tag);
25581 Roo.each(b, function(tag) {
25582 if (w.indexOf(tag) > -1) {
25585 if (this.black.indexOf(tag) > -1) {
25588 this.black.push(tag);
25593 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25594 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25598 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25599 if (b.indexOf(tag) > -1) {
25602 this.cwhite.push(tag);
25606 Roo.each(w, function(tag) {
25607 if (b.indexOf(tag) > -1) {
25610 if (this.cwhite.indexOf(tag) > -1) {
25613 this.cwhite.push(tag);
25618 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25619 if (w.indexOf(tag) > -1) {
25622 this.cblack.push(tag);
25626 Roo.each(b, function(tag) {
25627 if (w.indexOf(tag) > -1) {
25630 if (this.cblack.indexOf(tag) > -1) {
25633 this.cblack.push(tag);
25638 setStylesheets : function(stylesheets)
25640 if(typeof(stylesheets) == 'string'){
25641 Roo.get(this.iframe.contentDocument.head).createChild({
25643 rel : 'stylesheet',
25652 Roo.each(stylesheets, function(s) {
25657 Roo.get(_this.iframe.contentDocument.head).createChild({
25659 rel : 'stylesheet',
25668 removeStylesheets : function()
25672 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25677 setStyle : function(style)
25679 Roo.get(this.iframe.contentDocument.head).createChild({
25688 // hide stuff that is not compatible
25702 * @event specialkey
25706 * @cfg {String} fieldClass @hide
25709 * @cfg {String} focusClass @hide
25712 * @cfg {String} autoCreate @hide
25715 * @cfg {String} inputType @hide
25718 * @cfg {String} invalidClass @hide
25721 * @cfg {String} invalidText @hide
25724 * @cfg {String} msgFx @hide
25727 * @cfg {String} validateOnBlur @hide
25731 Roo.HtmlEditorCore.white = [
25732 'area', 'br', 'img', 'input', 'hr', 'wbr',
25734 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25735 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25736 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25737 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25738 'table', 'ul', 'xmp',
25740 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25743 'dir', 'menu', 'ol', 'ul', 'dl',
25749 Roo.HtmlEditorCore.black = [
25750 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25752 'base', 'basefont', 'bgsound', 'blink', 'body',
25753 'frame', 'frameset', 'head', 'html', 'ilayer',
25754 'iframe', 'layer', 'link', 'meta', 'object',
25755 'script', 'style' ,'title', 'xml' // clean later..
25757 Roo.HtmlEditorCore.clean = [
25758 'script', 'style', 'title', 'xml'
25760 Roo.HtmlEditorCore.remove = [
25765 Roo.HtmlEditorCore.ablack = [
25769 Roo.HtmlEditorCore.aclean = [
25770 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25774 Roo.HtmlEditorCore.pwhite= [
25775 'http', 'https', 'mailto'
25778 // white listed style attributes.
25779 Roo.HtmlEditorCore.cwhite= [
25780 // 'text-align', /// default is to allow most things..
25786 // black listed style attributes.
25787 Roo.HtmlEditorCore.cblack= [
25788 // 'font-size' -- this can be set by the project
25792 Roo.HtmlEditorCore.swapCodes =[
25811 * @class Roo.bootstrap.HtmlEditor
25812 * @extends Roo.bootstrap.TextArea
25813 * Bootstrap HtmlEditor class
25816 * Create a new HtmlEditor
25817 * @param {Object} config The config object
25820 Roo.bootstrap.HtmlEditor = function(config){
25821 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25822 if (!this.toolbars) {
25823 this.toolbars = [];
25826 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25829 * @event initialize
25830 * Fires when the editor is fully initialized (including the iframe)
25831 * @param {HtmlEditor} this
25836 * Fires when the editor is first receives the focus. Any insertion must wait
25837 * until after this event.
25838 * @param {HtmlEditor} this
25842 * @event beforesync
25843 * Fires before the textarea is updated with content from the editor iframe. Return false
25844 * to cancel the sync.
25845 * @param {HtmlEditor} this
25846 * @param {String} html
25850 * @event beforepush
25851 * Fires before the iframe editor is updated with content from the textarea. Return false
25852 * to cancel the push.
25853 * @param {HtmlEditor} this
25854 * @param {String} html
25859 * Fires when the textarea is updated with content from the editor iframe.
25860 * @param {HtmlEditor} this
25861 * @param {String} html
25866 * Fires when the iframe editor is updated with content from the textarea.
25867 * @param {HtmlEditor} this
25868 * @param {String} html
25872 * @event editmodechange
25873 * Fires when the editor switches edit modes
25874 * @param {HtmlEditor} this
25875 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25877 editmodechange: true,
25879 * @event editorevent
25880 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25881 * @param {HtmlEditor} this
25885 * @event firstfocus
25886 * Fires when on first focus - needed by toolbars..
25887 * @param {HtmlEditor} this
25892 * Auto save the htmlEditor value as a file into Events
25893 * @param {HtmlEditor} this
25897 * @event savedpreview
25898 * preview the saved version of htmlEditor
25899 * @param {HtmlEditor} this
25906 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
25910 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25915 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25920 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25925 * @cfg {Number} height (in pixels)
25929 * @cfg {Number} width (in pixels)
25934 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25937 stylesheets: false,
25942 // private properties
25943 validationEvent : false,
25945 initialized : false,
25948 onFocus : Roo.emptyFn,
25950 hideMode:'offsets',
25952 tbContainer : false,
25956 toolbarContainer :function() {
25957 return this.wrap.select('.x-html-editor-tb',true).first();
25961 * Protected method that will not generally be called directly. It
25962 * is called when the editor creates its toolbar. Override this method if you need to
25963 * add custom toolbar buttons.
25964 * @param {HtmlEditor} editor
25966 createToolbar : function(){
25967 Roo.log('renewing');
25968 Roo.log("create toolbars");
25970 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25971 this.toolbars[0].render(this.toolbarContainer());
25975 // if (!editor.toolbars || !editor.toolbars.length) {
25976 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25979 // for (var i =0 ; i < editor.toolbars.length;i++) {
25980 // editor.toolbars[i] = Roo.factory(
25981 // typeof(editor.toolbars[i]) == 'string' ?
25982 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
25983 // Roo.bootstrap.HtmlEditor);
25984 // editor.toolbars[i].init(editor);
25990 onRender : function(ct, position)
25992 // Roo.log("Call onRender: " + this.xtype);
25994 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25996 this.wrap = this.inputEl().wrap({
25997 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26000 this.editorcore.onRender(ct, position);
26002 if (this.resizable) {
26003 this.resizeEl = new Roo.Resizable(this.wrap, {
26007 minHeight : this.height,
26008 height: this.height,
26009 handles : this.resizable,
26012 resize : function(r, w, h) {
26013 _t.onResize(w,h); // -something
26019 this.createToolbar(this);
26022 if(!this.width && this.resizable){
26023 this.setSize(this.wrap.getSize());
26025 if (this.resizeEl) {
26026 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26027 // should trigger onReize..
26033 onResize : function(w, h)
26035 Roo.log('resize: ' +w + ',' + h );
26036 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26040 if(this.inputEl() ){
26041 if(typeof w == 'number'){
26042 var aw = w - this.wrap.getFrameWidth('lr');
26043 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26046 if(typeof h == 'number'){
26047 var tbh = -11; // fixme it needs to tool bar size!
26048 for (var i =0; i < this.toolbars.length;i++) {
26049 // fixme - ask toolbars for heights?
26050 tbh += this.toolbars[i].el.getHeight();
26051 //if (this.toolbars[i].footer) {
26052 // tbh += this.toolbars[i].footer.el.getHeight();
26060 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26061 ah -= 5; // knock a few pixes off for look..
26062 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26066 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26067 this.editorcore.onResize(ew,eh);
26072 * Toggles the editor between standard and source edit mode.
26073 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26075 toggleSourceEdit : function(sourceEditMode)
26077 this.editorcore.toggleSourceEdit(sourceEditMode);
26079 if(this.editorcore.sourceEditMode){
26080 Roo.log('editor - showing textarea');
26083 // Roo.log(this.syncValue());
26085 this.inputEl().removeClass(['hide', 'x-hidden']);
26086 this.inputEl().dom.removeAttribute('tabIndex');
26087 this.inputEl().focus();
26089 Roo.log('editor - hiding textarea');
26091 // Roo.log(this.pushValue());
26094 this.inputEl().addClass(['hide', 'x-hidden']);
26095 this.inputEl().dom.setAttribute('tabIndex', -1);
26096 //this.deferFocus();
26099 if(this.resizable){
26100 this.setSize(this.wrap.getSize());
26103 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26106 // private (for BoxComponent)
26107 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26109 // private (for BoxComponent)
26110 getResizeEl : function(){
26114 // private (for BoxComponent)
26115 getPositionEl : function(){
26120 initEvents : function(){
26121 this.originalValue = this.getValue();
26125 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26128 // markInvalid : Roo.emptyFn,
26130 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26133 // clearInvalid : Roo.emptyFn,
26135 setValue : function(v){
26136 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26137 this.editorcore.pushValue();
26142 deferFocus : function(){
26143 this.focus.defer(10, this);
26147 focus : function(){
26148 this.editorcore.focus();
26154 onDestroy : function(){
26160 for (var i =0; i < this.toolbars.length;i++) {
26161 // fixme - ask toolbars for heights?
26162 this.toolbars[i].onDestroy();
26165 this.wrap.dom.innerHTML = '';
26166 this.wrap.remove();
26171 onFirstFocus : function(){
26172 //Roo.log("onFirstFocus");
26173 this.editorcore.onFirstFocus();
26174 for (var i =0; i < this.toolbars.length;i++) {
26175 this.toolbars[i].onFirstFocus();
26181 syncValue : function()
26183 this.editorcore.syncValue();
26186 pushValue : function()
26188 this.editorcore.pushValue();
26192 // hide stuff that is not compatible
26206 * @event specialkey
26210 * @cfg {String} fieldClass @hide
26213 * @cfg {String} focusClass @hide
26216 * @cfg {String} autoCreate @hide
26219 * @cfg {String} inputType @hide
26223 * @cfg {String} invalidText @hide
26226 * @cfg {String} msgFx @hide
26229 * @cfg {String} validateOnBlur @hide
26238 Roo.namespace('Roo.bootstrap.htmleditor');
26240 * @class Roo.bootstrap.HtmlEditorToolbar1
26246 new Roo.bootstrap.HtmlEditor({
26249 new Roo.bootstrap.HtmlEditorToolbar1({
26250 disable : { fonts: 1 , format: 1, ..., ... , ...],
26256 * @cfg {Object} disable List of elements to disable..
26257 * @cfg {Array} btns List of additional buttons.
26261 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26264 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26267 Roo.apply(this, config);
26269 // default disabled, based on 'good practice'..
26270 this.disable = this.disable || {};
26271 Roo.applyIf(this.disable, {
26274 specialElements : true
26276 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26278 this.editor = config.editor;
26279 this.editorcore = config.editor.editorcore;
26281 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26283 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26284 // dont call parent... till later.
26286 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26291 editorcore : false,
26296 "h1","h2","h3","h4","h5","h6",
26298 "abbr", "acronym", "address", "cite", "samp", "var",
26302 onRender : function(ct, position)
26304 // Roo.log("Call onRender: " + this.xtype);
26306 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26308 this.el.dom.style.marginBottom = '0';
26310 var editorcore = this.editorcore;
26311 var editor= this.editor;
26314 var btn = function(id,cmd , toggle, handler, html){
26316 var event = toggle ? 'toggle' : 'click';
26321 xns: Roo.bootstrap,
26325 enableToggle:toggle !== false,
26327 pressed : toggle ? false : null,
26330 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26331 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26337 // var cb_box = function...
26342 xns: Roo.bootstrap,
26347 xns: Roo.bootstrap,
26351 Roo.each(this.formats, function(f) {
26352 style.menu.items.push({
26354 xns: Roo.bootstrap,
26355 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26360 editorcore.insertTag(this.tagname);
26367 children.push(style);
26369 btn('bold',false,true);
26370 btn('italic',false,true);
26371 btn('align-left', 'justifyleft',true);
26372 btn('align-center', 'justifycenter',true);
26373 btn('align-right' , 'justifyright',true);
26374 btn('link', false, false, function(btn) {
26375 //Roo.log("create link?");
26376 var url = prompt(this.createLinkText, this.defaultLinkValue);
26377 if(url && url != 'http:/'+'/'){
26378 this.editorcore.relayCmd('createlink', url);
26381 btn('list','insertunorderedlist',true);
26382 btn('pencil', false,true, function(btn){
26384 this.toggleSourceEdit(btn.pressed);
26387 if (this.editor.btns.length > 0) {
26388 for (var i = 0; i<this.editor.btns.length; i++) {
26389 children.push(this.editor.btns[i]);
26397 xns: Roo.bootstrap,
26402 xns: Roo.bootstrap,
26407 cog.menu.items.push({
26409 xns: Roo.bootstrap,
26410 html : Clean styles,
26415 editorcore.insertTag(this.tagname);
26424 this.xtype = 'NavSimplebar';
26426 for(var i=0;i< children.length;i++) {
26428 this.buttons.add(this.addxtypeChild(children[i]));
26432 editor.on('editorevent', this.updateToolbar, this);
26434 onBtnClick : function(id)
26436 this.editorcore.relayCmd(id);
26437 this.editorcore.focus();
26441 * Protected method that will not generally be called directly. It triggers
26442 * a toolbar update by reading the markup state of the current selection in the editor.
26444 updateToolbar: function(){
26446 if(!this.editorcore.activated){
26447 this.editor.onFirstFocus(); // is this neeed?
26451 var btns = this.buttons;
26452 var doc = this.editorcore.doc;
26453 btns.get('bold').setActive(doc.queryCommandState('bold'));
26454 btns.get('italic').setActive(doc.queryCommandState('italic'));
26455 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26457 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26458 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26459 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26461 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26462 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26465 var ans = this.editorcore.getAllAncestors();
26466 if (this.formatCombo) {
26469 var store = this.formatCombo.store;
26470 this.formatCombo.setValue("");
26471 for (var i =0; i < ans.length;i++) {
26472 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26474 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26482 // hides menus... - so this cant be on a menu...
26483 Roo.bootstrap.MenuMgr.hideAll();
26485 Roo.bootstrap.MenuMgr.hideAll();
26486 //this.editorsyncValue();
26488 onFirstFocus: function() {
26489 this.buttons.each(function(item){
26493 toggleSourceEdit : function(sourceEditMode){
26496 if(sourceEditMode){
26497 Roo.log("disabling buttons");
26498 this.buttons.each( function(item){
26499 if(item.cmd != 'pencil'){
26505 Roo.log("enabling buttons");
26506 if(this.editorcore.initialized){
26507 this.buttons.each( function(item){
26513 Roo.log("calling toggole on editor");
26514 // tell the editor that it's been pressed..
26515 this.editor.toggleSourceEdit(sourceEditMode);
26529 * @class Roo.bootstrap.Markdown
26530 * @extends Roo.bootstrap.TextArea
26531 * Bootstrap Showdown editable area
26532 * @cfg {string} content
26535 * Create a new Showdown
26538 Roo.bootstrap.Markdown = function(config){
26539 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26543 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26547 initEvents : function()
26550 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26551 this.markdownEl = this.el.createChild({
26552 cls : 'roo-markdown-area'
26554 this.inputEl().addClass('d-none');
26555 if (this.getValue() == '') {
26556 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26559 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26561 this.markdownEl.on('click', this.toggleTextEdit, this);
26562 this.on('blur', this.toggleTextEdit, this);
26563 this.on('specialkey', this.resizeTextArea, this);
26566 toggleTextEdit : function()
26568 var sh = this.markdownEl.getHeight();
26569 this.inputEl().addClass('d-none');
26570 this.markdownEl.addClass('d-none');
26571 if (!this.editing) {
26573 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26574 this.inputEl().removeClass('d-none');
26575 this.inputEl().focus();
26576 this.editing = true;
26579 // show showdown...
26580 this.updateMarkdown();
26581 this.markdownEl.removeClass('d-none');
26582 this.editing = false;
26585 updateMarkdown : function()
26587 if (this.getValue() == '') {
26588 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26592 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26595 resizeTextArea: function () {
26598 Roo.log([sh, this.getValue().split("\n").length * 30]);
26599 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26601 setValue : function(val)
26603 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26604 if (!this.editing) {
26605 this.updateMarkdown();
26611 if (!this.editing) {
26612 this.toggleTextEdit();
26620 * @class Roo.bootstrap.Table.AbstractSelectionModel
26621 * @extends Roo.util.Observable
26622 * Abstract base class for grid SelectionModels. It provides the interface that should be
26623 * implemented by descendant classes. This class should not be directly instantiated.
26626 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26627 this.locked = false;
26628 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26632 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26633 /** @ignore Called by the grid automatically. Do not call directly. */
26634 init : function(grid){
26640 * Locks the selections.
26643 this.locked = true;
26647 * Unlocks the selections.
26649 unlock : function(){
26650 this.locked = false;
26654 * Returns true if the selections are locked.
26655 * @return {Boolean}
26657 isLocked : function(){
26658 return this.locked;
26662 initEvents : function ()
26668 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26669 * @class Roo.bootstrap.Table.RowSelectionModel
26670 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26671 * It supports multiple selections and keyboard selection/navigation.
26673 * @param {Object} config
26676 Roo.bootstrap.Table.RowSelectionModel = function(config){
26677 Roo.apply(this, config);
26678 this.selections = new Roo.util.MixedCollection(false, function(o){
26683 this.lastActive = false;
26687 * @event selectionchange
26688 * Fires when the selection changes
26689 * @param {SelectionModel} this
26691 "selectionchange" : true,
26693 * @event afterselectionchange
26694 * Fires after the selection changes (eg. by key press or clicking)
26695 * @param {SelectionModel} this
26697 "afterselectionchange" : true,
26699 * @event beforerowselect
26700 * Fires when a row is selected being selected, return false to cancel.
26701 * @param {SelectionModel} this
26702 * @param {Number} rowIndex The selected index
26703 * @param {Boolean} keepExisting False if other selections will be cleared
26705 "beforerowselect" : true,
26708 * Fires when a row is selected.
26709 * @param {SelectionModel} this
26710 * @param {Number} rowIndex The selected index
26711 * @param {Roo.data.Record} r The record
26713 "rowselect" : true,
26715 * @event rowdeselect
26716 * Fires when a row is deselected.
26717 * @param {SelectionModel} this
26718 * @param {Number} rowIndex The selected index
26720 "rowdeselect" : true
26722 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26723 this.locked = false;
26726 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26728 * @cfg {Boolean} singleSelect
26729 * True to allow selection of only one row at a time (defaults to false)
26731 singleSelect : false,
26734 initEvents : function()
26737 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26738 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26739 //}else{ // allow click to work like normal
26740 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26742 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26743 this.grid.on("rowclick", this.handleMouseDown, this);
26745 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26746 "up" : function(e){
26748 this.selectPrevious(e.shiftKey);
26749 }else if(this.last !== false && this.lastActive !== false){
26750 var last = this.last;
26751 this.selectRange(this.last, this.lastActive-1);
26752 this.grid.getView().focusRow(this.lastActive);
26753 if(last !== false){
26757 this.selectFirstRow();
26759 this.fireEvent("afterselectionchange", this);
26761 "down" : function(e){
26763 this.selectNext(e.shiftKey);
26764 }else if(this.last !== false && this.lastActive !== false){
26765 var last = this.last;
26766 this.selectRange(this.last, this.lastActive+1);
26767 this.grid.getView().focusRow(this.lastActive);
26768 if(last !== false){
26772 this.selectFirstRow();
26774 this.fireEvent("afterselectionchange", this);
26778 this.grid.store.on('load', function(){
26779 this.selections.clear();
26782 var view = this.grid.view;
26783 view.on("refresh", this.onRefresh, this);
26784 view.on("rowupdated", this.onRowUpdated, this);
26785 view.on("rowremoved", this.onRemove, this);
26790 onRefresh : function()
26792 var ds = this.grid.store, i, v = this.grid.view;
26793 var s = this.selections;
26794 s.each(function(r){
26795 if((i = ds.indexOfId(r.id)) != -1){
26804 onRemove : function(v, index, r){
26805 this.selections.remove(r);
26809 onRowUpdated : function(v, index, r){
26810 if(this.isSelected(r)){
26811 v.onRowSelect(index);
26817 * @param {Array} records The records to select
26818 * @param {Boolean} keepExisting (optional) True to keep existing selections
26820 selectRecords : function(records, keepExisting)
26823 this.clearSelections();
26825 var ds = this.grid.store;
26826 for(var i = 0, len = records.length; i < len; i++){
26827 this.selectRow(ds.indexOf(records[i]), true);
26832 * Gets the number of selected rows.
26835 getCount : function(){
26836 return this.selections.length;
26840 * Selects the first row in the grid.
26842 selectFirstRow : function(){
26847 * Select the last row.
26848 * @param {Boolean} keepExisting (optional) True to keep existing selections
26850 selectLastRow : function(keepExisting){
26851 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26852 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26856 * Selects the row immediately following the last selected row.
26857 * @param {Boolean} keepExisting (optional) True to keep existing selections
26859 selectNext : function(keepExisting)
26861 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26862 this.selectRow(this.last+1, keepExisting);
26863 this.grid.getView().focusRow(this.last);
26868 * Selects the row that precedes the last selected row.
26869 * @param {Boolean} keepExisting (optional) True to keep existing selections
26871 selectPrevious : function(keepExisting){
26873 this.selectRow(this.last-1, keepExisting);
26874 this.grid.getView().focusRow(this.last);
26879 * Returns the selected records
26880 * @return {Array} Array of selected records
26882 getSelections : function(){
26883 return [].concat(this.selections.items);
26887 * Returns the first selected record.
26890 getSelected : function(){
26891 return this.selections.itemAt(0);
26896 * Clears all selections.
26898 clearSelections : function(fast)
26904 var ds = this.grid.store;
26905 var s = this.selections;
26906 s.each(function(r){
26907 this.deselectRow(ds.indexOfId(r.id));
26911 this.selections.clear();
26918 * Selects all rows.
26920 selectAll : function(){
26924 this.selections.clear();
26925 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26926 this.selectRow(i, true);
26931 * Returns True if there is a selection.
26932 * @return {Boolean}
26934 hasSelection : function(){
26935 return this.selections.length > 0;
26939 * Returns True if the specified row is selected.
26940 * @param {Number/Record} record The record or index of the record to check
26941 * @return {Boolean}
26943 isSelected : function(index){
26944 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26945 return (r && this.selections.key(r.id) ? true : false);
26949 * Returns True if the specified record id is selected.
26950 * @param {String} id The id of record to check
26951 * @return {Boolean}
26953 isIdSelected : function(id){
26954 return (this.selections.key(id) ? true : false);
26959 handleMouseDBClick : function(e, t){
26963 handleMouseDown : function(e, t)
26965 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26966 if(this.isLocked() || rowIndex < 0 ){
26969 if(e.shiftKey && this.last !== false){
26970 var last = this.last;
26971 this.selectRange(last, rowIndex, e.ctrlKey);
26972 this.last = last; // reset the last
26976 var isSelected = this.isSelected(rowIndex);
26977 //Roo.log("select row:" + rowIndex);
26979 this.deselectRow(rowIndex);
26981 this.selectRow(rowIndex, true);
26985 if(e.button !== 0 && isSelected){
26986 alert('rowIndex 2: ' + rowIndex);
26987 view.focusRow(rowIndex);
26988 }else if(e.ctrlKey && isSelected){
26989 this.deselectRow(rowIndex);
26990 }else if(!isSelected){
26991 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26992 view.focusRow(rowIndex);
26996 this.fireEvent("afterselectionchange", this);
26999 handleDragableRowClick : function(grid, rowIndex, e)
27001 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27002 this.selectRow(rowIndex, false);
27003 grid.view.focusRow(rowIndex);
27004 this.fireEvent("afterselectionchange", this);
27009 * Selects multiple rows.
27010 * @param {Array} rows Array of the indexes of the row to select
27011 * @param {Boolean} keepExisting (optional) True to keep existing selections
27013 selectRows : function(rows, keepExisting){
27015 this.clearSelections();
27017 for(var i = 0, len = rows.length; i < len; i++){
27018 this.selectRow(rows[i], true);
27023 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27024 * @param {Number} startRow The index of the first row in the range
27025 * @param {Number} endRow The index of the last row in the range
27026 * @param {Boolean} keepExisting (optional) True to retain existing selections
27028 selectRange : function(startRow, endRow, keepExisting){
27033 this.clearSelections();
27035 if(startRow <= endRow){
27036 for(var i = startRow; i <= endRow; i++){
27037 this.selectRow(i, true);
27040 for(var i = startRow; i >= endRow; i--){
27041 this.selectRow(i, true);
27047 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27048 * @param {Number} startRow The index of the first row in the range
27049 * @param {Number} endRow The index of the last row in the range
27051 deselectRange : function(startRow, endRow, preventViewNotify){
27055 for(var i = startRow; i <= endRow; i++){
27056 this.deselectRow(i, preventViewNotify);
27062 * @param {Number} row The index of the row to select
27063 * @param {Boolean} keepExisting (optional) True to keep existing selections
27065 selectRow : function(index, keepExisting, preventViewNotify)
27067 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27070 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27071 if(!keepExisting || this.singleSelect){
27072 this.clearSelections();
27075 var r = this.grid.store.getAt(index);
27076 //console.log('selectRow - record id :' + r.id);
27078 this.selections.add(r);
27079 this.last = this.lastActive = index;
27080 if(!preventViewNotify){
27081 var proxy = new Roo.Element(
27082 this.grid.getRowDom(index)
27084 proxy.addClass('bg-info info');
27086 this.fireEvent("rowselect", this, index, r);
27087 this.fireEvent("selectionchange", this);
27093 * @param {Number} row The index of the row to deselect
27095 deselectRow : function(index, preventViewNotify)
27100 if(this.last == index){
27103 if(this.lastActive == index){
27104 this.lastActive = false;
27107 var r = this.grid.store.getAt(index);
27112 this.selections.remove(r);
27113 //.console.log('deselectRow - record id :' + r.id);
27114 if(!preventViewNotify){
27116 var proxy = new Roo.Element(
27117 this.grid.getRowDom(index)
27119 proxy.removeClass('bg-info info');
27121 this.fireEvent("rowdeselect", this, index);
27122 this.fireEvent("selectionchange", this);
27126 restoreLast : function(){
27128 this.last = this._last;
27133 acceptsNav : function(row, col, cm){
27134 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27138 onEditorKey : function(field, e){
27139 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27144 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27146 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27148 }else if(k == e.ENTER && !e.ctrlKey){
27152 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27154 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27156 }else if(k == e.ESC){
27160 g.startEditing(newCell[0], newCell[1]);
27166 * Ext JS Library 1.1.1
27167 * Copyright(c) 2006-2007, Ext JS, LLC.
27169 * Originally Released Under LGPL - original licence link has changed is not relivant.
27172 * <script type="text/javascript">
27176 * @class Roo.bootstrap.PagingToolbar
27177 * @extends Roo.bootstrap.NavSimplebar
27178 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27180 * Create a new PagingToolbar
27181 * @param {Object} config The config object
27182 * @param {Roo.data.Store} store
27184 Roo.bootstrap.PagingToolbar = function(config)
27186 // old args format still supported... - xtype is prefered..
27187 // created from xtype...
27189 this.ds = config.dataSource;
27191 if (config.store && !this.ds) {
27192 this.store= Roo.factory(config.store, Roo.data);
27193 this.ds = this.store;
27194 this.ds.xmodule = this.xmodule || false;
27197 this.toolbarItems = [];
27198 if (config.items) {
27199 this.toolbarItems = config.items;
27202 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27207 this.bind(this.ds);
27210 if (Roo.bootstrap.version == 4) {
27211 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27213 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27218 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27220 * @cfg {Roo.data.Store} dataSource
27221 * The underlying data store providing the paged data
27224 * @cfg {String/HTMLElement/Element} container
27225 * container The id or element that will contain the toolbar
27228 * @cfg {Boolean} displayInfo
27229 * True to display the displayMsg (defaults to false)
27232 * @cfg {Number} pageSize
27233 * The number of records to display per page (defaults to 20)
27237 * @cfg {String} displayMsg
27238 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27240 displayMsg : 'Displaying {0} - {1} of {2}',
27242 * @cfg {String} emptyMsg
27243 * The message to display when no records are found (defaults to "No data to display")
27245 emptyMsg : 'No data to display',
27247 * Customizable piece of the default paging text (defaults to "Page")
27250 beforePageText : "Page",
27252 * Customizable piece of the default paging text (defaults to "of %0")
27255 afterPageText : "of {0}",
27257 * Customizable piece of the default paging text (defaults to "First Page")
27260 firstText : "First Page",
27262 * Customizable piece of the default paging text (defaults to "Previous Page")
27265 prevText : "Previous Page",
27267 * Customizable piece of the default paging text (defaults to "Next Page")
27270 nextText : "Next Page",
27272 * Customizable piece of the default paging text (defaults to "Last Page")
27275 lastText : "Last Page",
27277 * Customizable piece of the default paging text (defaults to "Refresh")
27280 refreshText : "Refresh",
27284 onRender : function(ct, position)
27286 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27287 this.navgroup.parentId = this.id;
27288 this.navgroup.onRender(this.el, null);
27289 // add the buttons to the navgroup
27291 if(this.displayInfo){
27292 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27293 this.displayEl = this.el.select('.x-paging-info', true).first();
27294 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27295 // this.displayEl = navel.el.select('span',true).first();
27301 Roo.each(_this.buttons, function(e){ // this might need to use render????
27302 Roo.factory(e).render(_this.el);
27306 Roo.each(_this.toolbarItems, function(e) {
27307 _this.navgroup.addItem(e);
27311 this.first = this.navgroup.addItem({
27312 tooltip: this.firstText,
27313 cls: "prev btn-outline-secondary",
27314 html : ' <i class="fa fa-step-backward"></i>',
27316 preventDefault: true,
27317 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27320 this.prev = this.navgroup.addItem({
27321 tooltip: this.prevText,
27322 cls: "prev btn-outline-secondary",
27323 html : ' <i class="fa fa-backward"></i>',
27325 preventDefault: true,
27326 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27328 //this.addSeparator();
27331 var field = this.navgroup.addItem( {
27333 cls : 'x-paging-position btn-outline-secondary',
27335 html : this.beforePageText +
27336 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27337 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27340 this.field = field.el.select('input', true).first();
27341 this.field.on("keydown", this.onPagingKeydown, this);
27342 this.field.on("focus", function(){this.dom.select();});
27345 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27346 //this.field.setHeight(18);
27347 //this.addSeparator();
27348 this.next = this.navgroup.addItem({
27349 tooltip: this.nextText,
27350 cls: "next btn-outline-secondary",
27351 html : ' <i class="fa fa-forward"></i>',
27353 preventDefault: true,
27354 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27356 this.last = this.navgroup.addItem({
27357 tooltip: this.lastText,
27358 html : ' <i class="fa fa-step-forward"></i>',
27359 cls: "next btn-outline-secondary",
27361 preventDefault: true,
27362 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27364 //this.addSeparator();
27365 this.loading = this.navgroup.addItem({
27366 tooltip: this.refreshText,
27367 cls: "btn-outline-secondary",
27368 html : ' <i class="fa fa-refresh"></i>',
27369 preventDefault: true,
27370 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27376 updateInfo : function(){
27377 if(this.displayEl){
27378 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27379 var msg = count == 0 ?
27383 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27385 this.displayEl.update(msg);
27390 onLoad : function(ds, r, o)
27392 this.cursor = o.params && o.params.start ? o.params.start : 0;
27394 var d = this.getPageData(),
27399 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27400 this.field.dom.value = ap;
27401 this.first.setDisabled(ap == 1);
27402 this.prev.setDisabled(ap == 1);
27403 this.next.setDisabled(ap == ps);
27404 this.last.setDisabled(ap == ps);
27405 this.loading.enable();
27410 getPageData : function(){
27411 var total = this.ds.getTotalCount();
27414 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27415 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27420 onLoadError : function(){
27421 this.loading.enable();
27425 onPagingKeydown : function(e){
27426 var k = e.getKey();
27427 var d = this.getPageData();
27429 var v = this.field.dom.value, pageNum;
27430 if(!v || isNaN(pageNum = parseInt(v, 10))){
27431 this.field.dom.value = d.activePage;
27434 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27435 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27438 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))
27440 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27441 this.field.dom.value = pageNum;
27442 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27445 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27447 var v = this.field.dom.value, pageNum;
27448 var increment = (e.shiftKey) ? 10 : 1;
27449 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27452 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27453 this.field.dom.value = d.activePage;
27456 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27458 this.field.dom.value = parseInt(v, 10) + increment;
27459 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27460 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27467 beforeLoad : function(){
27469 this.loading.disable();
27474 onClick : function(which){
27483 ds.load({params:{start: 0, limit: this.pageSize}});
27486 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27489 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27492 var total = ds.getTotalCount();
27493 var extra = total % this.pageSize;
27494 var lastStart = extra ? (total - extra) : total-this.pageSize;
27495 ds.load({params:{start: lastStart, limit: this.pageSize}});
27498 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27504 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27505 * @param {Roo.data.Store} store The data store to unbind
27507 unbind : function(ds){
27508 ds.un("beforeload", this.beforeLoad, this);
27509 ds.un("load", this.onLoad, this);
27510 ds.un("loadexception", this.onLoadError, this);
27511 ds.un("remove", this.updateInfo, this);
27512 ds.un("add", this.updateInfo, this);
27513 this.ds = undefined;
27517 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27518 * @param {Roo.data.Store} store The data store to bind
27520 bind : function(ds){
27521 ds.on("beforeload", this.beforeLoad, this);
27522 ds.on("load", this.onLoad, this);
27523 ds.on("loadexception", this.onLoadError, this);
27524 ds.on("remove", this.updateInfo, this);
27525 ds.on("add", this.updateInfo, this);
27536 * @class Roo.bootstrap.MessageBar
27537 * @extends Roo.bootstrap.Component
27538 * Bootstrap MessageBar class
27539 * @cfg {String} html contents of the MessageBar
27540 * @cfg {String} weight (info | success | warning | danger) default info
27541 * @cfg {String} beforeClass insert the bar before the given class
27542 * @cfg {Boolean} closable (true | false) default false
27543 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27546 * Create a new Element
27547 * @param {Object} config The config object
27550 Roo.bootstrap.MessageBar = function(config){
27551 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27554 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27560 beforeClass: 'bootstrap-sticky-wrap',
27562 getAutoCreate : function(){
27566 cls: 'alert alert-dismissable alert-' + this.weight,
27571 html: this.html || ''
27577 cfg.cls += ' alert-messages-fixed';
27591 onRender : function(ct, position)
27593 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27596 var cfg = Roo.apply({}, this.getAutoCreate());
27600 cfg.cls += ' ' + this.cls;
27603 cfg.style = this.style;
27605 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27607 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27610 this.el.select('>button.close').on('click', this.hide, this);
27616 if (!this.rendered) {
27622 this.fireEvent('show', this);
27628 if (!this.rendered) {
27634 this.fireEvent('hide', this);
27637 update : function()
27639 // var e = this.el.dom.firstChild;
27641 // if(this.closable){
27642 // e = e.nextSibling;
27645 // e.data = this.html || '';
27647 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27663 * @class Roo.bootstrap.Graph
27664 * @extends Roo.bootstrap.Component
27665 * Bootstrap Graph class
27669 @cfg {String} graphtype bar | vbar | pie
27670 @cfg {number} g_x coodinator | centre x (pie)
27671 @cfg {number} g_y coodinator | centre y (pie)
27672 @cfg {number} g_r radius (pie)
27673 @cfg {number} g_height height of the chart (respected by all elements in the set)
27674 @cfg {number} g_width width of the chart (respected by all elements in the set)
27675 @cfg {Object} title The title of the chart
27678 -opts (object) options for the chart
27680 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27681 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27683 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.
27684 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27686 o stretch (boolean)
27688 -opts (object) options for the pie
27691 o startAngle (number)
27692 o endAngle (number)
27696 * Create a new Input
27697 * @param {Object} config The config object
27700 Roo.bootstrap.Graph = function(config){
27701 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27707 * The img click event for the img.
27708 * @param {Roo.EventObject} e
27714 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27725 //g_colors: this.colors,
27732 getAutoCreate : function(){
27743 onRender : function(ct,position){
27746 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27748 if (typeof(Raphael) == 'undefined') {
27749 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27753 this.raphael = Raphael(this.el.dom);
27755 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27756 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27757 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27758 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27760 r.text(160, 10, "Single Series Chart").attr(txtattr);
27761 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27762 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27763 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27765 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27766 r.barchart(330, 10, 300, 220, data1);
27767 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27768 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27771 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27772 // r.barchart(30, 30, 560, 250, xdata, {
27773 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27774 // axis : "0 0 1 1",
27775 // axisxlabels : xdata
27776 // //yvalues : cols,
27779 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27781 // this.load(null,xdata,{
27782 // axis : "0 0 1 1",
27783 // axisxlabels : xdata
27788 load : function(graphtype,xdata,opts)
27790 this.raphael.clear();
27792 graphtype = this.graphtype;
27797 var r = this.raphael,
27798 fin = function () {
27799 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27801 fout = function () {
27802 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27804 pfin = function() {
27805 this.sector.stop();
27806 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27809 this.label[0].stop();
27810 this.label[0].attr({ r: 7.5 });
27811 this.label[1].attr({ "font-weight": 800 });
27814 pfout = function() {
27815 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27818 this.label[0].animate({ r: 5 }, 500, "bounce");
27819 this.label[1].attr({ "font-weight": 400 });
27825 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27828 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27831 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27832 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27834 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27841 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27846 setTitle: function(o)
27851 initEvents: function() {
27854 this.el.on('click', this.onClick, this);
27858 onClick : function(e)
27860 Roo.log('img onclick');
27861 this.fireEvent('click', this, e);
27873 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27876 * @class Roo.bootstrap.dash.NumberBox
27877 * @extends Roo.bootstrap.Component
27878 * Bootstrap NumberBox class
27879 * @cfg {String} headline Box headline
27880 * @cfg {String} content Box content
27881 * @cfg {String} icon Box icon
27882 * @cfg {String} footer Footer text
27883 * @cfg {String} fhref Footer href
27886 * Create a new NumberBox
27887 * @param {Object} config The config object
27891 Roo.bootstrap.dash.NumberBox = function(config){
27892 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27896 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27905 getAutoCreate : function(){
27909 cls : 'small-box ',
27917 cls : 'roo-headline',
27918 html : this.headline
27922 cls : 'roo-content',
27923 html : this.content
27937 cls : 'ion ' + this.icon
27946 cls : 'small-box-footer',
27947 href : this.fhref || '#',
27951 cfg.cn.push(footer);
27958 onRender : function(ct,position){
27959 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27966 setHeadline: function (value)
27968 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27971 setFooter: function (value, href)
27973 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27976 this.el.select('a.small-box-footer',true).first().attr('href', href);
27981 setContent: function (value)
27983 this.el.select('.roo-content',true).first().dom.innerHTML = value;
27986 initEvents: function()
28000 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28003 * @class Roo.bootstrap.dash.TabBox
28004 * @extends Roo.bootstrap.Component
28005 * Bootstrap TabBox class
28006 * @cfg {String} title Title of the TabBox
28007 * @cfg {String} icon Icon of the TabBox
28008 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28009 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28012 * Create a new TabBox
28013 * @param {Object} config The config object
28017 Roo.bootstrap.dash.TabBox = function(config){
28018 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28023 * When a pane is added
28024 * @param {Roo.bootstrap.dash.TabPane} pane
28028 * @event activatepane
28029 * When a pane is activated
28030 * @param {Roo.bootstrap.dash.TabPane} pane
28032 "activatepane" : true
28040 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28045 tabScrollable : false,
28047 getChildContainer : function()
28049 return this.el.select('.tab-content', true).first();
28052 getAutoCreate : function(){
28056 cls: 'pull-left header',
28064 cls: 'fa ' + this.icon
28070 cls: 'nav nav-tabs pull-right',
28076 if(this.tabScrollable){
28083 cls: 'nav nav-tabs pull-right',
28094 cls: 'nav-tabs-custom',
28099 cls: 'tab-content no-padding',
28107 initEvents : function()
28109 //Roo.log('add add pane handler');
28110 this.on('addpane', this.onAddPane, this);
28113 * Updates the box title
28114 * @param {String} html to set the title to.
28116 setTitle : function(value)
28118 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28120 onAddPane : function(pane)
28122 this.panes.push(pane);
28123 //Roo.log('addpane');
28125 // tabs are rendere left to right..
28126 if(!this.showtabs){
28130 var ctr = this.el.select('.nav-tabs', true).first();
28133 var existing = ctr.select('.nav-tab',true);
28134 var qty = existing.getCount();;
28137 var tab = ctr.createChild({
28139 cls : 'nav-tab' + (qty ? '' : ' active'),
28147 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28150 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28152 pane.el.addClass('active');
28157 onTabClick : function(ev,un,ob,pane)
28159 //Roo.log('tab - prev default');
28160 ev.preventDefault();
28163 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28164 pane.tab.addClass('active');
28165 //Roo.log(pane.title);
28166 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28167 // technically we should have a deactivate event.. but maybe add later.
28168 // and it should not de-activate the selected tab...
28169 this.fireEvent('activatepane', pane);
28170 pane.el.addClass('active');
28171 pane.fireEvent('activate');
28176 getActivePane : function()
28179 Roo.each(this.panes, function(p) {
28180 if(p.el.hasClass('active')){
28201 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28203 * @class Roo.bootstrap.TabPane
28204 * @extends Roo.bootstrap.Component
28205 * Bootstrap TabPane class
28206 * @cfg {Boolean} active (false | true) Default false
28207 * @cfg {String} title title of panel
28211 * Create a new TabPane
28212 * @param {Object} config The config object
28215 Roo.bootstrap.dash.TabPane = function(config){
28216 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28222 * When a pane is activated
28223 * @param {Roo.bootstrap.dash.TabPane} pane
28230 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28235 // the tabBox that this is attached to.
28238 getAutoCreate : function()
28246 cfg.cls += ' active';
28251 initEvents : function()
28253 //Roo.log('trigger add pane handler');
28254 this.parent().fireEvent('addpane', this)
28258 * Updates the tab title
28259 * @param {String} html to set the title to.
28261 setTitle: function(str)
28267 this.tab.select('a', true).first().dom.innerHTML = str;
28284 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28287 * @class Roo.bootstrap.menu.Menu
28288 * @extends Roo.bootstrap.Component
28289 * Bootstrap Menu class - container for Menu
28290 * @cfg {String} html Text of the menu
28291 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28292 * @cfg {String} icon Font awesome icon
28293 * @cfg {String} pos Menu align to (top | bottom) default bottom
28297 * Create a new Menu
28298 * @param {Object} config The config object
28302 Roo.bootstrap.menu.Menu = function(config){
28303 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28307 * @event beforeshow
28308 * Fires before this menu is displayed
28309 * @param {Roo.bootstrap.menu.Menu} this
28313 * @event beforehide
28314 * Fires before this menu is hidden
28315 * @param {Roo.bootstrap.menu.Menu} this
28320 * Fires after this menu is displayed
28321 * @param {Roo.bootstrap.menu.Menu} this
28326 * Fires after this menu is hidden
28327 * @param {Roo.bootstrap.menu.Menu} this
28332 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28333 * @param {Roo.bootstrap.menu.Menu} this
28334 * @param {Roo.EventObject} e
28341 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28345 weight : 'default',
28350 getChildContainer : function() {
28351 if(this.isSubMenu){
28355 return this.el.select('ul.dropdown-menu', true).first();
28358 getAutoCreate : function()
28363 cls : 'roo-menu-text',
28371 cls : 'fa ' + this.icon
28382 cls : 'dropdown-button btn btn-' + this.weight,
28387 cls : 'dropdown-toggle btn btn-' + this.weight,
28397 cls : 'dropdown-menu'
28403 if(this.pos == 'top'){
28404 cfg.cls += ' dropup';
28407 if(this.isSubMenu){
28410 cls : 'dropdown-menu'
28417 onRender : function(ct, position)
28419 this.isSubMenu = ct.hasClass('dropdown-submenu');
28421 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28424 initEvents : function()
28426 if(this.isSubMenu){
28430 this.hidden = true;
28432 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28433 this.triggerEl.on('click', this.onTriggerPress, this);
28435 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28436 this.buttonEl.on('click', this.onClick, this);
28442 if(this.isSubMenu){
28446 return this.el.select('ul.dropdown-menu', true).first();
28449 onClick : function(e)
28451 this.fireEvent("click", this, e);
28454 onTriggerPress : function(e)
28456 if (this.isVisible()) {
28463 isVisible : function(){
28464 return !this.hidden;
28469 this.fireEvent("beforeshow", this);
28471 this.hidden = false;
28472 this.el.addClass('open');
28474 Roo.get(document).on("mouseup", this.onMouseUp, this);
28476 this.fireEvent("show", this);
28483 this.fireEvent("beforehide", this);
28485 this.hidden = true;
28486 this.el.removeClass('open');
28488 Roo.get(document).un("mouseup", this.onMouseUp);
28490 this.fireEvent("hide", this);
28493 onMouseUp : function()
28507 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28510 * @class Roo.bootstrap.menu.Item
28511 * @extends Roo.bootstrap.Component
28512 * Bootstrap MenuItem class
28513 * @cfg {Boolean} submenu (true | false) default false
28514 * @cfg {String} html text of the item
28515 * @cfg {String} href the link
28516 * @cfg {Boolean} disable (true | false) default false
28517 * @cfg {Boolean} preventDefault (true | false) default true
28518 * @cfg {String} icon Font awesome icon
28519 * @cfg {String} pos Submenu align to (left | right) default right
28523 * Create a new Item
28524 * @param {Object} config The config object
28528 Roo.bootstrap.menu.Item = function(config){
28529 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28533 * Fires when the mouse is hovering over this menu
28534 * @param {Roo.bootstrap.menu.Item} this
28535 * @param {Roo.EventObject} e
28540 * Fires when the mouse exits this menu
28541 * @param {Roo.bootstrap.menu.Item} this
28542 * @param {Roo.EventObject} e
28548 * The raw click event for the entire grid.
28549 * @param {Roo.EventObject} e
28555 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28560 preventDefault: true,
28565 getAutoCreate : function()
28570 cls : 'roo-menu-item-text',
28578 cls : 'fa ' + this.icon
28587 href : this.href || '#',
28594 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28598 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28600 if(this.pos == 'left'){
28601 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28608 initEvents : function()
28610 this.el.on('mouseover', this.onMouseOver, this);
28611 this.el.on('mouseout', this.onMouseOut, this);
28613 this.el.select('a', true).first().on('click', this.onClick, this);
28617 onClick : function(e)
28619 if(this.preventDefault){
28620 e.preventDefault();
28623 this.fireEvent("click", this, e);
28626 onMouseOver : function(e)
28628 if(this.submenu && this.pos == 'left'){
28629 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28632 this.fireEvent("mouseover", this, e);
28635 onMouseOut : function(e)
28637 this.fireEvent("mouseout", this, e);
28649 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28652 * @class Roo.bootstrap.menu.Separator
28653 * @extends Roo.bootstrap.Component
28654 * Bootstrap Separator class
28657 * Create a new Separator
28658 * @param {Object} config The config object
28662 Roo.bootstrap.menu.Separator = function(config){
28663 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28666 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28668 getAutoCreate : function(){
28689 * @class Roo.bootstrap.Tooltip
28690 * Bootstrap Tooltip class
28691 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28692 * to determine which dom element triggers the tooltip.
28694 * It needs to add support for additional attributes like tooltip-position
28697 * Create a new Toolti
28698 * @param {Object} config The config object
28701 Roo.bootstrap.Tooltip = function(config){
28702 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28704 this.alignment = Roo.bootstrap.Tooltip.alignment;
28706 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28707 this.alignment = config.alignment;
28712 Roo.apply(Roo.bootstrap.Tooltip, {
28714 * @function init initialize tooltip monitoring.
28718 currentTip : false,
28719 currentRegion : false,
28725 Roo.get(document).on('mouseover', this.enter ,this);
28726 Roo.get(document).on('mouseout', this.leave, this);
28729 this.currentTip = new Roo.bootstrap.Tooltip();
28732 enter : function(ev)
28734 var dom = ev.getTarget();
28736 //Roo.log(['enter',dom]);
28737 var el = Roo.fly(dom);
28738 if (this.currentEl) {
28740 //Roo.log(this.currentEl);
28741 //Roo.log(this.currentEl.contains(dom));
28742 if (this.currentEl == el) {
28745 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28751 if (this.currentTip.el) {
28752 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28756 if(!el || el.dom == document){
28762 // you can not look for children, as if el is the body.. then everythign is the child..
28763 if (!el.attr('tooltip')) { //
28764 if (!el.select("[tooltip]").elements.length) {
28767 // is the mouse over this child...?
28768 bindEl = el.select("[tooltip]").first();
28769 var xy = ev.getXY();
28770 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28771 //Roo.log("not in region.");
28774 //Roo.log("child element over..");
28777 this.currentEl = bindEl;
28778 this.currentTip.bind(bindEl);
28779 this.currentRegion = Roo.lib.Region.getRegion(dom);
28780 this.currentTip.enter();
28783 leave : function(ev)
28785 var dom = ev.getTarget();
28786 //Roo.log(['leave',dom]);
28787 if (!this.currentEl) {
28792 if (dom != this.currentEl.dom) {
28795 var xy = ev.getXY();
28796 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28799 // only activate leave if mouse cursor is outside... bounding box..
28804 if (this.currentTip) {
28805 this.currentTip.leave();
28807 //Roo.log('clear currentEl');
28808 this.currentEl = false;
28813 'left' : ['r-l', [-2,0], 'right'],
28814 'right' : ['l-r', [2,0], 'left'],
28815 'bottom' : ['t-b', [0,2], 'top'],
28816 'top' : [ 'b-t', [0,-2], 'bottom']
28822 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28827 delay : null, // can be { show : 300 , hide: 500}
28831 hoverState : null, //???
28833 placement : 'bottom',
28837 getAutoCreate : function(){
28844 cls : 'tooltip-arrow arrow'
28847 cls : 'tooltip-inner'
28854 bind : function(el)
28859 initEvents : function()
28861 this.arrowEl = this.el.select('.arrow', true).first();
28862 this.innerEl = this.el.select('.tooltip-inner', true).first();
28865 enter : function () {
28867 if (this.timeout != null) {
28868 clearTimeout(this.timeout);
28871 this.hoverState = 'in';
28872 //Roo.log("enter - show");
28873 if (!this.delay || !this.delay.show) {
28878 this.timeout = setTimeout(function () {
28879 if (_t.hoverState == 'in') {
28882 }, this.delay.show);
28886 clearTimeout(this.timeout);
28888 this.hoverState = 'out';
28889 if (!this.delay || !this.delay.hide) {
28895 this.timeout = setTimeout(function () {
28896 //Roo.log("leave - timeout");
28898 if (_t.hoverState == 'out') {
28900 Roo.bootstrap.Tooltip.currentEl = false;
28905 show : function (msg)
28908 this.render(document.body);
28911 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28913 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28915 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28917 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28918 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28920 var placement = typeof this.placement == 'function' ?
28921 this.placement.call(this, this.el, on_el) :
28924 var autoToken = /\s?auto?\s?/i;
28925 var autoPlace = autoToken.test(placement);
28927 placement = placement.replace(autoToken, '') || 'top';
28931 //this.el.setXY([0,0]);
28933 //this.el.dom.style.display='block';
28935 //this.el.appendTo(on_el);
28937 var p = this.getPosition();
28938 var box = this.el.getBox();
28944 var align = this.alignment[placement];
28946 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28948 if(placement == 'top' || placement == 'bottom'){
28950 placement = 'right';
28953 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28954 placement = 'left';
28957 var scroll = Roo.select('body', true).first().getScroll();
28959 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28963 align = this.alignment[placement];
28965 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28969 this.el.alignTo(this.bindEl, align[0],align[1]);
28970 //var arrow = this.el.select('.arrow',true).first();
28971 //arrow.set(align[2],
28973 this.el.addClass(placement);
28974 this.el.addClass("bs-tooltip-"+ placement);
28976 this.el.addClass('in fade show');
28978 this.hoverState = null;
28980 if (this.el.hasClass('fade')) {
28995 //this.el.setXY([0,0]);
28996 this.el.removeClass(['show', 'in']);
29012 * @class Roo.bootstrap.LocationPicker
29013 * @extends Roo.bootstrap.Component
29014 * Bootstrap LocationPicker class
29015 * @cfg {Number} latitude Position when init default 0
29016 * @cfg {Number} longitude Position when init default 0
29017 * @cfg {Number} zoom default 15
29018 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29019 * @cfg {Boolean} mapTypeControl default false
29020 * @cfg {Boolean} disableDoubleClickZoom default false
29021 * @cfg {Boolean} scrollwheel default true
29022 * @cfg {Boolean} streetViewControl default false
29023 * @cfg {Number} radius default 0
29024 * @cfg {String} locationName
29025 * @cfg {Boolean} draggable default true
29026 * @cfg {Boolean} enableAutocomplete default false
29027 * @cfg {Boolean} enableReverseGeocode default true
29028 * @cfg {String} markerTitle
29031 * Create a new LocationPicker
29032 * @param {Object} config The config object
29036 Roo.bootstrap.LocationPicker = function(config){
29038 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29043 * Fires when the picker initialized.
29044 * @param {Roo.bootstrap.LocationPicker} this
29045 * @param {Google Location} location
29049 * @event positionchanged
29050 * Fires when the picker position changed.
29051 * @param {Roo.bootstrap.LocationPicker} this
29052 * @param {Google Location} location
29054 positionchanged : true,
29057 * Fires when the map resize.
29058 * @param {Roo.bootstrap.LocationPicker} this
29063 * Fires when the map show.
29064 * @param {Roo.bootstrap.LocationPicker} this
29069 * Fires when the map hide.
29070 * @param {Roo.bootstrap.LocationPicker} this
29075 * Fires when click the map.
29076 * @param {Roo.bootstrap.LocationPicker} this
29077 * @param {Map event} e
29081 * @event mapRightClick
29082 * Fires when right click the map.
29083 * @param {Roo.bootstrap.LocationPicker} this
29084 * @param {Map event} e
29086 mapRightClick : true,
29088 * @event markerClick
29089 * Fires when click the marker.
29090 * @param {Roo.bootstrap.LocationPicker} this
29091 * @param {Map event} e
29093 markerClick : true,
29095 * @event markerRightClick
29096 * Fires when right click the marker.
29097 * @param {Roo.bootstrap.LocationPicker} this
29098 * @param {Map event} e
29100 markerRightClick : true,
29102 * @event OverlayViewDraw
29103 * Fires when OverlayView Draw
29104 * @param {Roo.bootstrap.LocationPicker} this
29106 OverlayViewDraw : true,
29108 * @event OverlayViewOnAdd
29109 * Fires when OverlayView Draw
29110 * @param {Roo.bootstrap.LocationPicker} this
29112 OverlayViewOnAdd : true,
29114 * @event OverlayViewOnRemove
29115 * Fires when OverlayView Draw
29116 * @param {Roo.bootstrap.LocationPicker} this
29118 OverlayViewOnRemove : true,
29120 * @event OverlayViewShow
29121 * Fires when OverlayView Draw
29122 * @param {Roo.bootstrap.LocationPicker} this
29123 * @param {Pixel} cpx
29125 OverlayViewShow : true,
29127 * @event OverlayViewHide
29128 * Fires when OverlayView Draw
29129 * @param {Roo.bootstrap.LocationPicker} this
29131 OverlayViewHide : true,
29133 * @event loadexception
29134 * Fires when load google lib failed.
29135 * @param {Roo.bootstrap.LocationPicker} this
29137 loadexception : true
29142 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29144 gMapContext: false,
29150 mapTypeControl: false,
29151 disableDoubleClickZoom: false,
29153 streetViewControl: false,
29157 enableAutocomplete: false,
29158 enableReverseGeocode: true,
29161 getAutoCreate: function()
29166 cls: 'roo-location-picker'
29172 initEvents: function(ct, position)
29174 if(!this.el.getWidth() || this.isApplied()){
29178 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29183 initial: function()
29185 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29186 this.fireEvent('loadexception', this);
29190 if(!this.mapTypeId){
29191 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29194 this.gMapContext = this.GMapContext();
29196 this.initOverlayView();
29198 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29202 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29203 _this.setPosition(_this.gMapContext.marker.position);
29206 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29207 _this.fireEvent('mapClick', this, event);
29211 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29212 _this.fireEvent('mapRightClick', this, event);
29216 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29217 _this.fireEvent('markerClick', this, event);
29221 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29222 _this.fireEvent('markerRightClick', this, event);
29226 this.setPosition(this.gMapContext.location);
29228 this.fireEvent('initial', this, this.gMapContext.location);
29231 initOverlayView: function()
29235 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29239 _this.fireEvent('OverlayViewDraw', _this);
29244 _this.fireEvent('OverlayViewOnAdd', _this);
29247 onRemove: function()
29249 _this.fireEvent('OverlayViewOnRemove', _this);
29252 show: function(cpx)
29254 _this.fireEvent('OverlayViewShow', _this, cpx);
29259 _this.fireEvent('OverlayViewHide', _this);
29265 fromLatLngToContainerPixel: function(event)
29267 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29270 isApplied: function()
29272 return this.getGmapContext() == false ? false : true;
29275 getGmapContext: function()
29277 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29280 GMapContext: function()
29282 var position = new google.maps.LatLng(this.latitude, this.longitude);
29284 var _map = new google.maps.Map(this.el.dom, {
29287 mapTypeId: this.mapTypeId,
29288 mapTypeControl: this.mapTypeControl,
29289 disableDoubleClickZoom: this.disableDoubleClickZoom,
29290 scrollwheel: this.scrollwheel,
29291 streetViewControl: this.streetViewControl,
29292 locationName: this.locationName,
29293 draggable: this.draggable,
29294 enableAutocomplete: this.enableAutocomplete,
29295 enableReverseGeocode: this.enableReverseGeocode
29298 var _marker = new google.maps.Marker({
29299 position: position,
29301 title: this.markerTitle,
29302 draggable: this.draggable
29309 location: position,
29310 radius: this.radius,
29311 locationName: this.locationName,
29312 addressComponents: {
29313 formatted_address: null,
29314 addressLine1: null,
29315 addressLine2: null,
29317 streetNumber: null,
29321 stateOrProvince: null
29324 domContainer: this.el.dom,
29325 geodecoder: new google.maps.Geocoder()
29329 drawCircle: function(center, radius, options)
29331 if (this.gMapContext.circle != null) {
29332 this.gMapContext.circle.setMap(null);
29336 options = Roo.apply({}, options, {
29337 strokeColor: "#0000FF",
29338 strokeOpacity: .35,
29340 fillColor: "#0000FF",
29344 options.map = this.gMapContext.map;
29345 options.radius = radius;
29346 options.center = center;
29347 this.gMapContext.circle = new google.maps.Circle(options);
29348 return this.gMapContext.circle;
29354 setPosition: function(location)
29356 this.gMapContext.location = location;
29357 this.gMapContext.marker.setPosition(location);
29358 this.gMapContext.map.panTo(location);
29359 this.drawCircle(location, this.gMapContext.radius, {});
29363 if (this.gMapContext.settings.enableReverseGeocode) {
29364 this.gMapContext.geodecoder.geocode({
29365 latLng: this.gMapContext.location
29366 }, function(results, status) {
29368 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29369 _this.gMapContext.locationName = results[0].formatted_address;
29370 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29372 _this.fireEvent('positionchanged', this, location);
29379 this.fireEvent('positionchanged', this, location);
29384 google.maps.event.trigger(this.gMapContext.map, "resize");
29386 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29388 this.fireEvent('resize', this);
29391 setPositionByLatLng: function(latitude, longitude)
29393 this.setPosition(new google.maps.LatLng(latitude, longitude));
29396 getCurrentPosition: function()
29399 latitude: this.gMapContext.location.lat(),
29400 longitude: this.gMapContext.location.lng()
29404 getAddressName: function()
29406 return this.gMapContext.locationName;
29409 getAddressComponents: function()
29411 return this.gMapContext.addressComponents;
29414 address_component_from_google_geocode: function(address_components)
29418 for (var i = 0; i < address_components.length; i++) {
29419 var component = address_components[i];
29420 if (component.types.indexOf("postal_code") >= 0) {
29421 result.postalCode = component.short_name;
29422 } else if (component.types.indexOf("street_number") >= 0) {
29423 result.streetNumber = component.short_name;
29424 } else if (component.types.indexOf("route") >= 0) {
29425 result.streetName = component.short_name;
29426 } else if (component.types.indexOf("neighborhood") >= 0) {
29427 result.city = component.short_name;
29428 } else if (component.types.indexOf("locality") >= 0) {
29429 result.city = component.short_name;
29430 } else if (component.types.indexOf("sublocality") >= 0) {
29431 result.district = component.short_name;
29432 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29433 result.stateOrProvince = component.short_name;
29434 } else if (component.types.indexOf("country") >= 0) {
29435 result.country = component.short_name;
29439 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29440 result.addressLine2 = "";
29444 setZoomLevel: function(zoom)
29446 this.gMapContext.map.setZoom(zoom);
29459 this.fireEvent('show', this);
29470 this.fireEvent('hide', this);
29475 Roo.apply(Roo.bootstrap.LocationPicker, {
29477 OverlayView : function(map, options)
29479 options = options || {};
29486 * @class Roo.bootstrap.Alert
29487 * @extends Roo.bootstrap.Component
29488 * Bootstrap Alert class - shows an alert area box
29490 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29491 Enter a valid email address
29494 * @cfg {String} title The title of alert
29495 * @cfg {String} html The content of alert
29496 * @cfg {String} weight ( success | info | warning | danger )
29497 * @cfg {String} faicon font-awesomeicon
29500 * Create a new alert
29501 * @param {Object} config The config object
29505 Roo.bootstrap.Alert = function(config){
29506 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29510 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29517 getAutoCreate : function()
29526 cls : 'roo-alert-icon'
29531 cls : 'roo-alert-title',
29536 cls : 'roo-alert-text',
29543 cfg.cn[0].cls += ' fa ' + this.faicon;
29547 cfg.cls += ' alert-' + this.weight;
29553 initEvents: function()
29555 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29558 setTitle : function(str)
29560 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29563 setText : function(str)
29565 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29568 setWeight : function(weight)
29571 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29574 this.weight = weight;
29576 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29579 setIcon : function(icon)
29582 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29585 this.faicon = icon;
29587 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29608 * @class Roo.bootstrap.UploadCropbox
29609 * @extends Roo.bootstrap.Component
29610 * Bootstrap UploadCropbox class
29611 * @cfg {String} emptyText show when image has been loaded
29612 * @cfg {String} rotateNotify show when image too small to rotate
29613 * @cfg {Number} errorTimeout default 3000
29614 * @cfg {Number} minWidth default 300
29615 * @cfg {Number} minHeight default 300
29616 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29617 * @cfg {Boolean} isDocument (true|false) default false
29618 * @cfg {String} url action url
29619 * @cfg {String} paramName default 'imageUpload'
29620 * @cfg {String} method default POST
29621 * @cfg {Boolean} loadMask (true|false) default true
29622 * @cfg {Boolean} loadingText default 'Loading...'
29625 * Create a new UploadCropbox
29626 * @param {Object} config The config object
29629 Roo.bootstrap.UploadCropbox = function(config){
29630 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29634 * @event beforeselectfile
29635 * Fire before select file
29636 * @param {Roo.bootstrap.UploadCropbox} this
29638 "beforeselectfile" : true,
29641 * Fire after initEvent
29642 * @param {Roo.bootstrap.UploadCropbox} this
29647 * Fire after initEvent
29648 * @param {Roo.bootstrap.UploadCropbox} this
29649 * @param {String} data
29654 * Fire when preparing the file data
29655 * @param {Roo.bootstrap.UploadCropbox} this
29656 * @param {Object} file
29661 * Fire when get exception
29662 * @param {Roo.bootstrap.UploadCropbox} this
29663 * @param {XMLHttpRequest} xhr
29665 "exception" : true,
29667 * @event beforeloadcanvas
29668 * Fire before load the canvas
29669 * @param {Roo.bootstrap.UploadCropbox} this
29670 * @param {String} src
29672 "beforeloadcanvas" : true,
29675 * Fire when trash image
29676 * @param {Roo.bootstrap.UploadCropbox} this
29681 * Fire when download the image
29682 * @param {Roo.bootstrap.UploadCropbox} this
29686 * @event footerbuttonclick
29687 * Fire when footerbuttonclick
29688 * @param {Roo.bootstrap.UploadCropbox} this
29689 * @param {String} type
29691 "footerbuttonclick" : true,
29695 * @param {Roo.bootstrap.UploadCropbox} this
29700 * Fire when rotate the image
29701 * @param {Roo.bootstrap.UploadCropbox} this
29702 * @param {String} pos
29707 * Fire when inspect the file
29708 * @param {Roo.bootstrap.UploadCropbox} this
29709 * @param {Object} file
29714 * Fire when xhr upload the file
29715 * @param {Roo.bootstrap.UploadCropbox} this
29716 * @param {Object} data
29721 * Fire when arrange the file data
29722 * @param {Roo.bootstrap.UploadCropbox} this
29723 * @param {Object} formData
29728 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29731 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29733 emptyText : 'Click to upload image',
29734 rotateNotify : 'Image is too small to rotate',
29735 errorTimeout : 3000,
29749 cropType : 'image/jpeg',
29751 canvasLoaded : false,
29752 isDocument : false,
29754 paramName : 'imageUpload',
29756 loadingText : 'Loading...',
29759 getAutoCreate : function()
29763 cls : 'roo-upload-cropbox',
29767 cls : 'roo-upload-cropbox-selector',
29772 cls : 'roo-upload-cropbox-body',
29773 style : 'cursor:pointer',
29777 cls : 'roo-upload-cropbox-preview'
29781 cls : 'roo-upload-cropbox-thumb'
29785 cls : 'roo-upload-cropbox-empty-notify',
29786 html : this.emptyText
29790 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29791 html : this.rotateNotify
29797 cls : 'roo-upload-cropbox-footer',
29800 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29810 onRender : function(ct, position)
29812 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29814 if (this.buttons.length) {
29816 Roo.each(this.buttons, function(bb) {
29818 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29820 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29826 this.maskEl = this.el;
29830 initEvents : function()
29832 this.urlAPI = (window.createObjectURL && window) ||
29833 (window.URL && URL.revokeObjectURL && URL) ||
29834 (window.webkitURL && webkitURL);
29836 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29837 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29839 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29840 this.selectorEl.hide();
29842 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29843 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29845 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29846 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29847 this.thumbEl.hide();
29849 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29850 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29852 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29853 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29854 this.errorEl.hide();
29856 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29857 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29858 this.footerEl.hide();
29860 this.setThumbBoxSize();
29866 this.fireEvent('initial', this);
29873 window.addEventListener("resize", function() { _this.resize(); } );
29875 this.bodyEl.on('click', this.beforeSelectFile, this);
29878 this.bodyEl.on('touchstart', this.onTouchStart, this);
29879 this.bodyEl.on('touchmove', this.onTouchMove, this);
29880 this.bodyEl.on('touchend', this.onTouchEnd, this);
29884 this.bodyEl.on('mousedown', this.onMouseDown, this);
29885 this.bodyEl.on('mousemove', this.onMouseMove, this);
29886 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29887 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29888 Roo.get(document).on('mouseup', this.onMouseUp, this);
29891 this.selectorEl.on('change', this.onFileSelected, this);
29897 this.baseScale = 1;
29899 this.baseRotate = 1;
29900 this.dragable = false;
29901 this.pinching = false;
29904 this.cropData = false;
29905 this.notifyEl.dom.innerHTML = this.emptyText;
29907 this.selectorEl.dom.value = '';
29911 resize : function()
29913 if(this.fireEvent('resize', this) != false){
29914 this.setThumbBoxPosition();
29915 this.setCanvasPosition();
29919 onFooterButtonClick : function(e, el, o, type)
29922 case 'rotate-left' :
29923 this.onRotateLeft(e);
29925 case 'rotate-right' :
29926 this.onRotateRight(e);
29929 this.beforeSelectFile(e);
29944 this.fireEvent('footerbuttonclick', this, type);
29947 beforeSelectFile : function(e)
29949 e.preventDefault();
29951 if(this.fireEvent('beforeselectfile', this) != false){
29952 this.selectorEl.dom.click();
29956 onFileSelected : function(e)
29958 e.preventDefault();
29960 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29964 var file = this.selectorEl.dom.files[0];
29966 if(this.fireEvent('inspect', this, file) != false){
29967 this.prepare(file);
29972 trash : function(e)
29974 this.fireEvent('trash', this);
29977 download : function(e)
29979 this.fireEvent('download', this);
29982 loadCanvas : function(src)
29984 if(this.fireEvent('beforeloadcanvas', this, src) != false){
29988 this.imageEl = document.createElement('img');
29992 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29994 this.imageEl.src = src;
29998 onLoadCanvas : function()
30000 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30001 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30003 this.bodyEl.un('click', this.beforeSelectFile, this);
30005 this.notifyEl.hide();
30006 this.thumbEl.show();
30007 this.footerEl.show();
30009 this.baseRotateLevel();
30011 if(this.isDocument){
30012 this.setThumbBoxSize();
30015 this.setThumbBoxPosition();
30017 this.baseScaleLevel();
30023 this.canvasLoaded = true;
30026 this.maskEl.unmask();
30031 setCanvasPosition : function()
30033 if(!this.canvasEl){
30037 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30038 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30040 this.previewEl.setLeft(pw);
30041 this.previewEl.setTop(ph);
30045 onMouseDown : function(e)
30049 this.dragable = true;
30050 this.pinching = false;
30052 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30053 this.dragable = false;
30057 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30058 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30062 onMouseMove : function(e)
30066 if(!this.canvasLoaded){
30070 if (!this.dragable){
30074 var minX = Math.ceil(this.thumbEl.getLeft(true));
30075 var minY = Math.ceil(this.thumbEl.getTop(true));
30077 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30078 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30080 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30081 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30083 x = x - this.mouseX;
30084 y = y - this.mouseY;
30086 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30087 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30089 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30090 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30092 this.previewEl.setLeft(bgX);
30093 this.previewEl.setTop(bgY);
30095 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30096 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30099 onMouseUp : function(e)
30103 this.dragable = false;
30106 onMouseWheel : function(e)
30110 this.startScale = this.scale;
30112 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30114 if(!this.zoomable()){
30115 this.scale = this.startScale;
30124 zoomable : function()
30126 var minScale = this.thumbEl.getWidth() / this.minWidth;
30128 if(this.minWidth < this.minHeight){
30129 minScale = this.thumbEl.getHeight() / this.minHeight;
30132 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30133 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30137 (this.rotate == 0 || this.rotate == 180) &&
30139 width > this.imageEl.OriginWidth ||
30140 height > this.imageEl.OriginHeight ||
30141 (width < this.minWidth && height < this.minHeight)
30149 (this.rotate == 90 || this.rotate == 270) &&
30151 width > this.imageEl.OriginWidth ||
30152 height > this.imageEl.OriginHeight ||
30153 (width < this.minHeight && height < this.minWidth)
30160 !this.isDocument &&
30161 (this.rotate == 0 || this.rotate == 180) &&
30163 width < this.minWidth ||
30164 width > this.imageEl.OriginWidth ||
30165 height < this.minHeight ||
30166 height > this.imageEl.OriginHeight
30173 !this.isDocument &&
30174 (this.rotate == 90 || this.rotate == 270) &&
30176 width < this.minHeight ||
30177 width > this.imageEl.OriginWidth ||
30178 height < this.minWidth ||
30179 height > this.imageEl.OriginHeight
30189 onRotateLeft : function(e)
30191 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30193 var minScale = this.thumbEl.getWidth() / this.minWidth;
30195 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30196 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30198 this.startScale = this.scale;
30200 while (this.getScaleLevel() < minScale){
30202 this.scale = this.scale + 1;
30204 if(!this.zoomable()){
30209 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30210 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30215 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30222 this.scale = this.startScale;
30224 this.onRotateFail();
30229 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30231 if(this.isDocument){
30232 this.setThumbBoxSize();
30233 this.setThumbBoxPosition();
30234 this.setCanvasPosition();
30239 this.fireEvent('rotate', this, 'left');
30243 onRotateRight : function(e)
30245 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30247 var minScale = this.thumbEl.getWidth() / this.minWidth;
30249 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30250 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30252 this.startScale = this.scale;
30254 while (this.getScaleLevel() < minScale){
30256 this.scale = this.scale + 1;
30258 if(!this.zoomable()){
30263 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30264 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30269 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30276 this.scale = this.startScale;
30278 this.onRotateFail();
30283 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30285 if(this.isDocument){
30286 this.setThumbBoxSize();
30287 this.setThumbBoxPosition();
30288 this.setCanvasPosition();
30293 this.fireEvent('rotate', this, 'right');
30296 onRotateFail : function()
30298 this.errorEl.show(true);
30302 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30307 this.previewEl.dom.innerHTML = '';
30309 var canvasEl = document.createElement("canvas");
30311 var contextEl = canvasEl.getContext("2d");
30313 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30314 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30315 var center = this.imageEl.OriginWidth / 2;
30317 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30318 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30319 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30320 center = this.imageEl.OriginHeight / 2;
30323 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30325 contextEl.translate(center, center);
30326 contextEl.rotate(this.rotate * Math.PI / 180);
30328 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30330 this.canvasEl = document.createElement("canvas");
30332 this.contextEl = this.canvasEl.getContext("2d");
30334 switch (this.rotate) {
30337 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30338 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30340 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30345 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30346 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30348 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30349 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);
30353 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30358 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30359 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30361 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30362 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);
30366 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);
30371 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30372 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30374 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30375 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30379 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);
30386 this.previewEl.appendChild(this.canvasEl);
30388 this.setCanvasPosition();
30393 if(!this.canvasLoaded){
30397 var imageCanvas = document.createElement("canvas");
30399 var imageContext = imageCanvas.getContext("2d");
30401 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30402 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30404 var center = imageCanvas.width / 2;
30406 imageContext.translate(center, center);
30408 imageContext.rotate(this.rotate * Math.PI / 180);
30410 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30412 var canvas = document.createElement("canvas");
30414 var context = canvas.getContext("2d");
30416 canvas.width = this.minWidth;
30417 canvas.height = this.minHeight;
30419 switch (this.rotate) {
30422 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30423 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30425 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30426 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30428 var targetWidth = this.minWidth - 2 * x;
30429 var targetHeight = this.minHeight - 2 * y;
30433 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30434 scale = targetWidth / width;
30437 if(x > 0 && y == 0){
30438 scale = targetHeight / height;
30441 if(x > 0 && y > 0){
30442 scale = targetWidth / width;
30444 if(width < height){
30445 scale = targetHeight / height;
30449 context.scale(scale, scale);
30451 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30452 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30454 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30455 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30457 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30462 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30463 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30465 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30466 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30468 var targetWidth = this.minWidth - 2 * x;
30469 var targetHeight = this.minHeight - 2 * y;
30473 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30474 scale = targetWidth / width;
30477 if(x > 0 && y == 0){
30478 scale = targetHeight / height;
30481 if(x > 0 && y > 0){
30482 scale = targetWidth / width;
30484 if(width < height){
30485 scale = targetHeight / height;
30489 context.scale(scale, scale);
30491 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30492 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30494 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30495 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30497 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30499 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30504 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30505 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30507 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30508 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30510 var targetWidth = this.minWidth - 2 * x;
30511 var targetHeight = this.minHeight - 2 * y;
30515 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30516 scale = targetWidth / width;
30519 if(x > 0 && y == 0){
30520 scale = targetHeight / height;
30523 if(x > 0 && y > 0){
30524 scale = targetWidth / width;
30526 if(width < height){
30527 scale = targetHeight / height;
30531 context.scale(scale, scale);
30533 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30534 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30536 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30537 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30539 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30540 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30542 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30547 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30548 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30550 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30551 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30553 var targetWidth = this.minWidth - 2 * x;
30554 var targetHeight = this.minHeight - 2 * y;
30558 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30559 scale = targetWidth / width;
30562 if(x > 0 && y == 0){
30563 scale = targetHeight / height;
30566 if(x > 0 && y > 0){
30567 scale = targetWidth / width;
30569 if(width < height){
30570 scale = targetHeight / height;
30574 context.scale(scale, scale);
30576 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30577 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30579 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30580 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30582 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30584 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30591 this.cropData = canvas.toDataURL(this.cropType);
30593 if(this.fireEvent('crop', this, this.cropData) !== false){
30594 this.process(this.file, this.cropData);
30601 setThumbBoxSize : function()
30605 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30606 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30607 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30609 this.minWidth = width;
30610 this.minHeight = height;
30612 if(this.rotate == 90 || this.rotate == 270){
30613 this.minWidth = height;
30614 this.minHeight = width;
30619 width = Math.ceil(this.minWidth * height / this.minHeight);
30621 if(this.minWidth > this.minHeight){
30623 height = Math.ceil(this.minHeight * width / this.minWidth);
30626 this.thumbEl.setStyle({
30627 width : width + 'px',
30628 height : height + 'px'
30635 setThumbBoxPosition : function()
30637 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30638 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30640 this.thumbEl.setLeft(x);
30641 this.thumbEl.setTop(y);
30645 baseRotateLevel : function()
30647 this.baseRotate = 1;
30650 typeof(this.exif) != 'undefined' &&
30651 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30652 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30654 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30657 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30661 baseScaleLevel : function()
30665 if(this.isDocument){
30667 if(this.baseRotate == 6 || this.baseRotate == 8){
30669 height = this.thumbEl.getHeight();
30670 this.baseScale = height / this.imageEl.OriginWidth;
30672 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30673 width = this.thumbEl.getWidth();
30674 this.baseScale = width / this.imageEl.OriginHeight;
30680 height = this.thumbEl.getHeight();
30681 this.baseScale = height / this.imageEl.OriginHeight;
30683 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30684 width = this.thumbEl.getWidth();
30685 this.baseScale = width / this.imageEl.OriginWidth;
30691 if(this.baseRotate == 6 || this.baseRotate == 8){
30693 width = this.thumbEl.getHeight();
30694 this.baseScale = width / this.imageEl.OriginHeight;
30696 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30697 height = this.thumbEl.getWidth();
30698 this.baseScale = height / this.imageEl.OriginHeight;
30701 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30702 height = this.thumbEl.getWidth();
30703 this.baseScale = height / this.imageEl.OriginHeight;
30705 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30706 width = this.thumbEl.getHeight();
30707 this.baseScale = width / this.imageEl.OriginWidth;
30714 width = this.thumbEl.getWidth();
30715 this.baseScale = width / this.imageEl.OriginWidth;
30717 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30718 height = this.thumbEl.getHeight();
30719 this.baseScale = height / this.imageEl.OriginHeight;
30722 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30724 height = this.thumbEl.getHeight();
30725 this.baseScale = height / this.imageEl.OriginHeight;
30727 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30728 width = this.thumbEl.getWidth();
30729 this.baseScale = width / this.imageEl.OriginWidth;
30737 getScaleLevel : function()
30739 return this.baseScale * Math.pow(1.1, this.scale);
30742 onTouchStart : function(e)
30744 if(!this.canvasLoaded){
30745 this.beforeSelectFile(e);
30749 var touches = e.browserEvent.touches;
30755 if(touches.length == 1){
30756 this.onMouseDown(e);
30760 if(touches.length != 2){
30766 for(var i = 0, finger; finger = touches[i]; i++){
30767 coords.push(finger.pageX, finger.pageY);
30770 var x = Math.pow(coords[0] - coords[2], 2);
30771 var y = Math.pow(coords[1] - coords[3], 2);
30773 this.startDistance = Math.sqrt(x + y);
30775 this.startScale = this.scale;
30777 this.pinching = true;
30778 this.dragable = false;
30782 onTouchMove : function(e)
30784 if(!this.pinching && !this.dragable){
30788 var touches = e.browserEvent.touches;
30795 this.onMouseMove(e);
30801 for(var i = 0, finger; finger = touches[i]; i++){
30802 coords.push(finger.pageX, finger.pageY);
30805 var x = Math.pow(coords[0] - coords[2], 2);
30806 var y = Math.pow(coords[1] - coords[3], 2);
30808 this.endDistance = Math.sqrt(x + y);
30810 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30812 if(!this.zoomable()){
30813 this.scale = this.startScale;
30821 onTouchEnd : function(e)
30823 this.pinching = false;
30824 this.dragable = false;
30828 process : function(file, crop)
30831 this.maskEl.mask(this.loadingText);
30834 this.xhr = new XMLHttpRequest();
30836 file.xhr = this.xhr;
30838 this.xhr.open(this.method, this.url, true);
30841 "Accept": "application/json",
30842 "Cache-Control": "no-cache",
30843 "X-Requested-With": "XMLHttpRequest"
30846 for (var headerName in headers) {
30847 var headerValue = headers[headerName];
30849 this.xhr.setRequestHeader(headerName, headerValue);
30855 this.xhr.onload = function()
30857 _this.xhrOnLoad(_this.xhr);
30860 this.xhr.onerror = function()
30862 _this.xhrOnError(_this.xhr);
30865 var formData = new FormData();
30867 formData.append('returnHTML', 'NO');
30870 formData.append('crop', crop);
30873 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30874 formData.append(this.paramName, file, file.name);
30877 if(typeof(file.filename) != 'undefined'){
30878 formData.append('filename', file.filename);
30881 if(typeof(file.mimetype) != 'undefined'){
30882 formData.append('mimetype', file.mimetype);
30885 if(this.fireEvent('arrange', this, formData) != false){
30886 this.xhr.send(formData);
30890 xhrOnLoad : function(xhr)
30893 this.maskEl.unmask();
30896 if (xhr.readyState !== 4) {
30897 this.fireEvent('exception', this, xhr);
30901 var response = Roo.decode(xhr.responseText);
30903 if(!response.success){
30904 this.fireEvent('exception', this, xhr);
30908 var response = Roo.decode(xhr.responseText);
30910 this.fireEvent('upload', this, response);
30914 xhrOnError : function()
30917 this.maskEl.unmask();
30920 Roo.log('xhr on error');
30922 var response = Roo.decode(xhr.responseText);
30928 prepare : function(file)
30931 this.maskEl.mask(this.loadingText);
30937 if(typeof(file) === 'string'){
30938 this.loadCanvas(file);
30942 if(!file || !this.urlAPI){
30947 this.cropType = file.type;
30951 if(this.fireEvent('prepare', this, this.file) != false){
30953 var reader = new FileReader();
30955 reader.onload = function (e) {
30956 if (e.target.error) {
30957 Roo.log(e.target.error);
30961 var buffer = e.target.result,
30962 dataView = new DataView(buffer),
30964 maxOffset = dataView.byteLength - 4,
30968 if (dataView.getUint16(0) === 0xffd8) {
30969 while (offset < maxOffset) {
30970 markerBytes = dataView.getUint16(offset);
30972 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30973 markerLength = dataView.getUint16(offset + 2) + 2;
30974 if (offset + markerLength > dataView.byteLength) {
30975 Roo.log('Invalid meta data: Invalid segment size.');
30979 if(markerBytes == 0xffe1){
30980 _this.parseExifData(
30987 offset += markerLength;
30997 var url = _this.urlAPI.createObjectURL(_this.file);
30999 _this.loadCanvas(url);
31004 reader.readAsArrayBuffer(this.file);
31010 parseExifData : function(dataView, offset, length)
31012 var tiffOffset = offset + 10,
31016 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31017 // No Exif data, might be XMP data instead
31021 // Check for the ASCII code for "Exif" (0x45786966):
31022 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31023 // No Exif data, might be XMP data instead
31026 if (tiffOffset + 8 > dataView.byteLength) {
31027 Roo.log('Invalid Exif data: Invalid segment size.');
31030 // Check for the two null bytes:
31031 if (dataView.getUint16(offset + 8) !== 0x0000) {
31032 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31035 // Check the byte alignment:
31036 switch (dataView.getUint16(tiffOffset)) {
31038 littleEndian = true;
31041 littleEndian = false;
31044 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31047 // Check for the TIFF tag marker (0x002A):
31048 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31049 Roo.log('Invalid Exif data: Missing TIFF marker.');
31052 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31053 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31055 this.parseExifTags(
31058 tiffOffset + dirOffset,
31063 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31068 if (dirOffset + 6 > dataView.byteLength) {
31069 Roo.log('Invalid Exif data: Invalid directory offset.');
31072 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31073 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31074 if (dirEndOffset + 4 > dataView.byteLength) {
31075 Roo.log('Invalid Exif data: Invalid directory size.');
31078 for (i = 0; i < tagsNumber; i += 1) {
31082 dirOffset + 2 + 12 * i, // tag offset
31086 // Return the offset to the next directory:
31087 return dataView.getUint32(dirEndOffset, littleEndian);
31090 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31092 var tag = dataView.getUint16(offset, littleEndian);
31094 this.exif[tag] = this.getExifValue(
31098 dataView.getUint16(offset + 2, littleEndian), // tag type
31099 dataView.getUint32(offset + 4, littleEndian), // tag length
31104 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31106 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31115 Roo.log('Invalid Exif data: Invalid tag type.');
31119 tagSize = tagType.size * length;
31120 // Determine if the value is contained in the dataOffset bytes,
31121 // or if the value at the dataOffset is a pointer to the actual data:
31122 dataOffset = tagSize > 4 ?
31123 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31124 if (dataOffset + tagSize > dataView.byteLength) {
31125 Roo.log('Invalid Exif data: Invalid data offset.');
31128 if (length === 1) {
31129 return tagType.getValue(dataView, dataOffset, littleEndian);
31132 for (i = 0; i < length; i += 1) {
31133 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31136 if (tagType.ascii) {
31138 // Concatenate the chars:
31139 for (i = 0; i < values.length; i += 1) {
31141 // Ignore the terminating NULL byte(s):
31142 if (c === '\u0000') {
31154 Roo.apply(Roo.bootstrap.UploadCropbox, {
31156 'Orientation': 0x0112
31160 1: 0, //'top-left',
31162 3: 180, //'bottom-right',
31163 // 4: 'bottom-left',
31165 6: 90, //'right-top',
31166 // 7: 'right-bottom',
31167 8: 270 //'left-bottom'
31171 // byte, 8-bit unsigned int:
31173 getValue: function (dataView, dataOffset) {
31174 return dataView.getUint8(dataOffset);
31178 // ascii, 8-bit byte:
31180 getValue: function (dataView, dataOffset) {
31181 return String.fromCharCode(dataView.getUint8(dataOffset));
31186 // short, 16 bit int:
31188 getValue: function (dataView, dataOffset, littleEndian) {
31189 return dataView.getUint16(dataOffset, littleEndian);
31193 // long, 32 bit int:
31195 getValue: function (dataView, dataOffset, littleEndian) {
31196 return dataView.getUint32(dataOffset, littleEndian);
31200 // rational = two long values, first is numerator, second is denominator:
31202 getValue: function (dataView, dataOffset, littleEndian) {
31203 return dataView.getUint32(dataOffset, littleEndian) /
31204 dataView.getUint32(dataOffset + 4, littleEndian);
31208 // slong, 32 bit signed int:
31210 getValue: function (dataView, dataOffset, littleEndian) {
31211 return dataView.getInt32(dataOffset, littleEndian);
31215 // srational, two slongs, first is numerator, second is denominator:
31217 getValue: function (dataView, dataOffset, littleEndian) {
31218 return dataView.getInt32(dataOffset, littleEndian) /
31219 dataView.getInt32(dataOffset + 4, littleEndian);
31229 cls : 'btn-group roo-upload-cropbox-rotate-left',
31230 action : 'rotate-left',
31234 cls : 'btn btn-default',
31235 html : '<i class="fa fa-undo"></i>'
31241 cls : 'btn-group roo-upload-cropbox-picture',
31242 action : 'picture',
31246 cls : 'btn btn-default',
31247 html : '<i class="fa fa-picture-o"></i>'
31253 cls : 'btn-group roo-upload-cropbox-rotate-right',
31254 action : 'rotate-right',
31258 cls : 'btn btn-default',
31259 html : '<i class="fa fa-repeat"></i>'
31267 cls : 'btn-group roo-upload-cropbox-rotate-left',
31268 action : 'rotate-left',
31272 cls : 'btn btn-default',
31273 html : '<i class="fa fa-undo"></i>'
31279 cls : 'btn-group roo-upload-cropbox-download',
31280 action : 'download',
31284 cls : 'btn btn-default',
31285 html : '<i class="fa fa-download"></i>'
31291 cls : 'btn-group roo-upload-cropbox-crop',
31296 cls : 'btn btn-default',
31297 html : '<i class="fa fa-crop"></i>'
31303 cls : 'btn-group roo-upload-cropbox-trash',
31308 cls : 'btn btn-default',
31309 html : '<i class="fa fa-trash"></i>'
31315 cls : 'btn-group roo-upload-cropbox-rotate-right',
31316 action : 'rotate-right',
31320 cls : 'btn btn-default',
31321 html : '<i class="fa fa-repeat"></i>'
31329 cls : 'btn-group roo-upload-cropbox-rotate-left',
31330 action : 'rotate-left',
31334 cls : 'btn btn-default',
31335 html : '<i class="fa fa-undo"></i>'
31341 cls : 'btn-group roo-upload-cropbox-rotate-right',
31342 action : 'rotate-right',
31346 cls : 'btn btn-default',
31347 html : '<i class="fa fa-repeat"></i>'
31360 * @class Roo.bootstrap.DocumentManager
31361 * @extends Roo.bootstrap.Component
31362 * Bootstrap DocumentManager class
31363 * @cfg {String} paramName default 'imageUpload'
31364 * @cfg {String} toolTipName default 'filename'
31365 * @cfg {String} method default POST
31366 * @cfg {String} url action url
31367 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31368 * @cfg {Boolean} multiple multiple upload default true
31369 * @cfg {Number} thumbSize default 300
31370 * @cfg {String} fieldLabel
31371 * @cfg {Number} labelWidth default 4
31372 * @cfg {String} labelAlign (left|top) default left
31373 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31374 * @cfg {Number} labellg set the width of label (1-12)
31375 * @cfg {Number} labelmd set the width of label (1-12)
31376 * @cfg {Number} labelsm set the width of label (1-12)
31377 * @cfg {Number} labelxs set the width of label (1-12)
31380 * Create a new DocumentManager
31381 * @param {Object} config The config object
31384 Roo.bootstrap.DocumentManager = function(config){
31385 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31388 this.delegates = [];
31393 * Fire when initial the DocumentManager
31394 * @param {Roo.bootstrap.DocumentManager} this
31399 * inspect selected file
31400 * @param {Roo.bootstrap.DocumentManager} this
31401 * @param {File} file
31406 * Fire when xhr load exception
31407 * @param {Roo.bootstrap.DocumentManager} this
31408 * @param {XMLHttpRequest} xhr
31410 "exception" : true,
31412 * @event afterupload
31413 * Fire when xhr load exception
31414 * @param {Roo.bootstrap.DocumentManager} this
31415 * @param {XMLHttpRequest} xhr
31417 "afterupload" : true,
31420 * prepare the form data
31421 * @param {Roo.bootstrap.DocumentManager} this
31422 * @param {Object} formData
31427 * Fire when remove the file
31428 * @param {Roo.bootstrap.DocumentManager} this
31429 * @param {Object} file
31434 * Fire after refresh the file
31435 * @param {Roo.bootstrap.DocumentManager} this
31440 * Fire after click the image
31441 * @param {Roo.bootstrap.DocumentManager} this
31442 * @param {Object} file
31447 * Fire when upload a image and editable set to true
31448 * @param {Roo.bootstrap.DocumentManager} this
31449 * @param {Object} file
31453 * @event beforeselectfile
31454 * Fire before select file
31455 * @param {Roo.bootstrap.DocumentManager} this
31457 "beforeselectfile" : true,
31460 * Fire before process file
31461 * @param {Roo.bootstrap.DocumentManager} this
31462 * @param {Object} file
31466 * @event previewrendered
31467 * Fire when preview rendered
31468 * @param {Roo.bootstrap.DocumentManager} this
31469 * @param {Object} file
31471 "previewrendered" : true,
31474 "previewResize" : true
31479 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31488 paramName : 'imageUpload',
31489 toolTipName : 'filename',
31492 labelAlign : 'left',
31502 getAutoCreate : function()
31504 var managerWidget = {
31506 cls : 'roo-document-manager',
31510 cls : 'roo-document-manager-selector',
31515 cls : 'roo-document-manager-uploader',
31519 cls : 'roo-document-manager-upload-btn',
31520 html : '<i class="fa fa-plus"></i>'
31531 cls : 'column col-md-12',
31536 if(this.fieldLabel.length){
31541 cls : 'column col-md-12',
31542 html : this.fieldLabel
31546 cls : 'column col-md-12',
31551 if(this.labelAlign == 'left'){
31556 html : this.fieldLabel
31565 if(this.labelWidth > 12){
31566 content[0].style = "width: " + this.labelWidth + 'px';
31569 if(this.labelWidth < 13 && this.labelmd == 0){
31570 this.labelmd = this.labelWidth;
31573 if(this.labellg > 0){
31574 content[0].cls += ' col-lg-' + this.labellg;
31575 content[1].cls += ' col-lg-' + (12 - this.labellg);
31578 if(this.labelmd > 0){
31579 content[0].cls += ' col-md-' + this.labelmd;
31580 content[1].cls += ' col-md-' + (12 - this.labelmd);
31583 if(this.labelsm > 0){
31584 content[0].cls += ' col-sm-' + this.labelsm;
31585 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31588 if(this.labelxs > 0){
31589 content[0].cls += ' col-xs-' + this.labelxs;
31590 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31598 cls : 'row clearfix',
31606 initEvents : function()
31608 this.managerEl = this.el.select('.roo-document-manager', true).first();
31609 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31611 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31612 this.selectorEl.hide();
31615 this.selectorEl.attr('multiple', 'multiple');
31618 this.selectorEl.on('change', this.onFileSelected, this);
31620 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31621 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31623 this.uploader.on('click', this.onUploaderClick, this);
31625 this.renderProgressDialog();
31629 window.addEventListener("resize", function() { _this.refresh(); } );
31631 this.fireEvent('initial', this);
31634 renderProgressDialog : function()
31638 this.progressDialog = new Roo.bootstrap.Modal({
31639 cls : 'roo-document-manager-progress-dialog',
31640 allow_close : false,
31651 btnclick : function() {
31652 _this.uploadCancel();
31658 this.progressDialog.render(Roo.get(document.body));
31660 this.progress = new Roo.bootstrap.Progress({
31661 cls : 'roo-document-manager-progress',
31666 this.progress.render(this.progressDialog.getChildContainer());
31668 this.progressBar = new Roo.bootstrap.ProgressBar({
31669 cls : 'roo-document-manager-progress-bar',
31672 aria_valuemax : 12,
31676 this.progressBar.render(this.progress.getChildContainer());
31679 onUploaderClick : function(e)
31681 e.preventDefault();
31683 if(this.fireEvent('beforeselectfile', this) != false){
31684 this.selectorEl.dom.click();
31689 onFileSelected : function(e)
31691 e.preventDefault();
31693 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31697 Roo.each(this.selectorEl.dom.files, function(file){
31698 if(this.fireEvent('inspect', this, file) != false){
31699 this.files.push(file);
31709 this.selectorEl.dom.value = '';
31711 if(!this.files || !this.files.length){
31715 if(this.boxes > 0 && this.files.length > this.boxes){
31716 this.files = this.files.slice(0, this.boxes);
31719 this.uploader.show();
31721 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31722 this.uploader.hide();
31731 Roo.each(this.files, function(file){
31733 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31734 var f = this.renderPreview(file);
31739 if(file.type.indexOf('image') != -1){
31740 this.delegates.push(
31742 _this.process(file);
31743 }).createDelegate(this)
31751 _this.process(file);
31752 }).createDelegate(this)
31757 this.files = files;
31759 this.delegates = this.delegates.concat(docs);
31761 if(!this.delegates.length){
31766 this.progressBar.aria_valuemax = this.delegates.length;
31773 arrange : function()
31775 if(!this.delegates.length){
31776 this.progressDialog.hide();
31781 var delegate = this.delegates.shift();
31783 this.progressDialog.show();
31785 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31787 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31792 refresh : function()
31794 this.uploader.show();
31796 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31797 this.uploader.hide();
31800 Roo.isTouch ? this.closable(false) : this.closable(true);
31802 this.fireEvent('refresh', this);
31805 onRemove : function(e, el, o)
31807 e.preventDefault();
31809 this.fireEvent('remove', this, o);
31813 remove : function(o)
31817 Roo.each(this.files, function(file){
31818 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31827 this.files = files;
31834 Roo.each(this.files, function(file){
31839 file.target.remove();
31848 onClick : function(e, el, o)
31850 e.preventDefault();
31852 this.fireEvent('click', this, o);
31856 closable : function(closable)
31858 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31860 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31872 xhrOnLoad : function(xhr)
31874 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31878 if (xhr.readyState !== 4) {
31880 this.fireEvent('exception', this, xhr);
31884 var response = Roo.decode(xhr.responseText);
31886 if(!response.success){
31888 this.fireEvent('exception', this, xhr);
31892 var file = this.renderPreview(response.data);
31894 this.files.push(file);
31898 this.fireEvent('afterupload', this, xhr);
31902 xhrOnError : function(xhr)
31904 Roo.log('xhr on error');
31906 var response = Roo.decode(xhr.responseText);
31913 process : function(file)
31915 if(this.fireEvent('process', this, file) !== false){
31916 if(this.editable && file.type.indexOf('image') != -1){
31917 this.fireEvent('edit', this, file);
31921 this.uploadStart(file, false);
31928 uploadStart : function(file, crop)
31930 this.xhr = new XMLHttpRequest();
31932 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31937 file.xhr = this.xhr;
31939 this.managerEl.createChild({
31941 cls : 'roo-document-manager-loading',
31945 tooltip : file.name,
31946 cls : 'roo-document-manager-thumb',
31947 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31953 this.xhr.open(this.method, this.url, true);
31956 "Accept": "application/json",
31957 "Cache-Control": "no-cache",
31958 "X-Requested-With": "XMLHttpRequest"
31961 for (var headerName in headers) {
31962 var headerValue = headers[headerName];
31964 this.xhr.setRequestHeader(headerName, headerValue);
31970 this.xhr.onload = function()
31972 _this.xhrOnLoad(_this.xhr);
31975 this.xhr.onerror = function()
31977 _this.xhrOnError(_this.xhr);
31980 var formData = new FormData();
31982 formData.append('returnHTML', 'NO');
31985 formData.append('crop', crop);
31988 formData.append(this.paramName, file, file.name);
31995 if(this.fireEvent('prepare', this, formData, options) != false){
31997 if(options.manually){
32001 this.xhr.send(formData);
32005 this.uploadCancel();
32008 uploadCancel : function()
32014 this.delegates = [];
32016 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32023 renderPreview : function(file)
32025 if(typeof(file.target) != 'undefined' && file.target){
32029 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32031 var previewEl = this.managerEl.createChild({
32033 cls : 'roo-document-manager-preview',
32037 tooltip : file[this.toolTipName],
32038 cls : 'roo-document-manager-thumb',
32039 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32044 html : '<i class="fa fa-times-circle"></i>'
32049 var close = previewEl.select('button.close', true).first();
32051 close.on('click', this.onRemove, this, file);
32053 file.target = previewEl;
32055 var image = previewEl.select('img', true).first();
32059 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32061 image.on('click', this.onClick, this, file);
32063 this.fireEvent('previewrendered', this, file);
32069 onPreviewLoad : function(file, image)
32071 if(typeof(file.target) == 'undefined' || !file.target){
32075 var width = image.dom.naturalWidth || image.dom.width;
32076 var height = image.dom.naturalHeight || image.dom.height;
32078 if(!this.previewResize) {
32082 if(width > height){
32083 file.target.addClass('wide');
32087 file.target.addClass('tall');
32092 uploadFromSource : function(file, crop)
32094 this.xhr = new XMLHttpRequest();
32096 this.managerEl.createChild({
32098 cls : 'roo-document-manager-loading',
32102 tooltip : file.name,
32103 cls : 'roo-document-manager-thumb',
32104 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32110 this.xhr.open(this.method, this.url, true);
32113 "Accept": "application/json",
32114 "Cache-Control": "no-cache",
32115 "X-Requested-With": "XMLHttpRequest"
32118 for (var headerName in headers) {
32119 var headerValue = headers[headerName];
32121 this.xhr.setRequestHeader(headerName, headerValue);
32127 this.xhr.onload = function()
32129 _this.xhrOnLoad(_this.xhr);
32132 this.xhr.onerror = function()
32134 _this.xhrOnError(_this.xhr);
32137 var formData = new FormData();
32139 formData.append('returnHTML', 'NO');
32141 formData.append('crop', crop);
32143 if(typeof(file.filename) != 'undefined'){
32144 formData.append('filename', file.filename);
32147 if(typeof(file.mimetype) != 'undefined'){
32148 formData.append('mimetype', file.mimetype);
32153 if(this.fireEvent('prepare', this, formData) != false){
32154 this.xhr.send(formData);
32164 * @class Roo.bootstrap.DocumentViewer
32165 * @extends Roo.bootstrap.Component
32166 * Bootstrap DocumentViewer class
32167 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32168 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32171 * Create a new DocumentViewer
32172 * @param {Object} config The config object
32175 Roo.bootstrap.DocumentViewer = function(config){
32176 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32181 * Fire after initEvent
32182 * @param {Roo.bootstrap.DocumentViewer} this
32188 * @param {Roo.bootstrap.DocumentViewer} this
32193 * Fire after download button
32194 * @param {Roo.bootstrap.DocumentViewer} this
32199 * Fire after trash button
32200 * @param {Roo.bootstrap.DocumentViewer} this
32207 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32209 showDownload : true,
32213 getAutoCreate : function()
32217 cls : 'roo-document-viewer',
32221 cls : 'roo-document-viewer-body',
32225 cls : 'roo-document-viewer-thumb',
32229 cls : 'roo-document-viewer-image'
32237 cls : 'roo-document-viewer-footer',
32240 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32244 cls : 'btn-group roo-document-viewer-download',
32248 cls : 'btn btn-default',
32249 html : '<i class="fa fa-download"></i>'
32255 cls : 'btn-group roo-document-viewer-trash',
32259 cls : 'btn btn-default',
32260 html : '<i class="fa fa-trash"></i>'
32273 initEvents : function()
32275 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32276 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32278 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32279 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32281 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32282 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32284 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32285 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32287 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32288 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32290 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32291 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32293 this.bodyEl.on('click', this.onClick, this);
32294 this.downloadBtn.on('click', this.onDownload, this);
32295 this.trashBtn.on('click', this.onTrash, this);
32297 this.downloadBtn.hide();
32298 this.trashBtn.hide();
32300 if(this.showDownload){
32301 this.downloadBtn.show();
32304 if(this.showTrash){
32305 this.trashBtn.show();
32308 if(!this.showDownload && !this.showTrash) {
32309 this.footerEl.hide();
32314 initial : function()
32316 this.fireEvent('initial', this);
32320 onClick : function(e)
32322 e.preventDefault();
32324 this.fireEvent('click', this);
32327 onDownload : function(e)
32329 e.preventDefault();
32331 this.fireEvent('download', this);
32334 onTrash : function(e)
32336 e.preventDefault();
32338 this.fireEvent('trash', this);
32350 * @class Roo.bootstrap.NavProgressBar
32351 * @extends Roo.bootstrap.Component
32352 * Bootstrap NavProgressBar class
32355 * Create a new nav progress bar
32356 * @param {Object} config The config object
32359 Roo.bootstrap.NavProgressBar = function(config){
32360 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32362 this.bullets = this.bullets || [];
32364 // Roo.bootstrap.NavProgressBar.register(this);
32368 * Fires when the active item changes
32369 * @param {Roo.bootstrap.NavProgressBar} this
32370 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32371 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32378 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32383 getAutoCreate : function()
32385 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32389 cls : 'roo-navigation-bar-group',
32393 cls : 'roo-navigation-top-bar'
32397 cls : 'roo-navigation-bullets-bar',
32401 cls : 'roo-navigation-bar'
32408 cls : 'roo-navigation-bottom-bar'
32418 initEvents: function()
32423 onRender : function(ct, position)
32425 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32427 if(this.bullets.length){
32428 Roo.each(this.bullets, function(b){
32437 addItem : function(cfg)
32439 var item = new Roo.bootstrap.NavProgressItem(cfg);
32441 item.parentId = this.id;
32442 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32445 var top = new Roo.bootstrap.Element({
32447 cls : 'roo-navigation-bar-text'
32450 var bottom = new Roo.bootstrap.Element({
32452 cls : 'roo-navigation-bar-text'
32455 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32456 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32458 var topText = new Roo.bootstrap.Element({
32460 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32463 var bottomText = new Roo.bootstrap.Element({
32465 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32468 topText.onRender(top.el, null);
32469 bottomText.onRender(bottom.el, null);
32472 item.bottomEl = bottom;
32475 this.barItems.push(item);
32480 getActive : function()
32482 var active = false;
32484 Roo.each(this.barItems, function(v){
32486 if (!v.isActive()) {
32498 setActiveItem : function(item)
32502 Roo.each(this.barItems, function(v){
32503 if (v.rid == item.rid) {
32507 if (v.isActive()) {
32508 v.setActive(false);
32513 item.setActive(true);
32515 this.fireEvent('changed', this, item, prev);
32518 getBarItem: function(rid)
32522 Roo.each(this.barItems, function(e) {
32523 if (e.rid != rid) {
32534 indexOfItem : function(item)
32538 Roo.each(this.barItems, function(v, i){
32540 if (v.rid != item.rid) {
32551 setActiveNext : function()
32553 var i = this.indexOfItem(this.getActive());
32555 if (i > this.barItems.length) {
32559 this.setActiveItem(this.barItems[i+1]);
32562 setActivePrev : function()
32564 var i = this.indexOfItem(this.getActive());
32570 this.setActiveItem(this.barItems[i-1]);
32573 format : function()
32575 if(!this.barItems.length){
32579 var width = 100 / this.barItems.length;
32581 Roo.each(this.barItems, function(i){
32582 i.el.setStyle('width', width + '%');
32583 i.topEl.el.setStyle('width', width + '%');
32584 i.bottomEl.el.setStyle('width', width + '%');
32593 * Nav Progress Item
32598 * @class Roo.bootstrap.NavProgressItem
32599 * @extends Roo.bootstrap.Component
32600 * Bootstrap NavProgressItem class
32601 * @cfg {String} rid the reference id
32602 * @cfg {Boolean} active (true|false) Is item active default false
32603 * @cfg {Boolean} disabled (true|false) Is item active default false
32604 * @cfg {String} html
32605 * @cfg {String} position (top|bottom) text position default bottom
32606 * @cfg {String} icon show icon instead of number
32609 * Create a new NavProgressItem
32610 * @param {Object} config The config object
32612 Roo.bootstrap.NavProgressItem = function(config){
32613 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32618 * The raw click event for the entire grid.
32619 * @param {Roo.bootstrap.NavProgressItem} this
32620 * @param {Roo.EventObject} e
32627 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32633 position : 'bottom',
32636 getAutoCreate : function()
32638 var iconCls = 'roo-navigation-bar-item-icon';
32640 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32644 cls: 'roo-navigation-bar-item',
32654 cfg.cls += ' active';
32657 cfg.cls += ' disabled';
32663 disable : function()
32665 this.setDisabled(true);
32668 enable : function()
32670 this.setDisabled(false);
32673 initEvents: function()
32675 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32677 this.iconEl.on('click', this.onClick, this);
32680 onClick : function(e)
32682 e.preventDefault();
32688 if(this.fireEvent('click', this, e) === false){
32692 this.parent().setActiveItem(this);
32695 isActive: function ()
32697 return this.active;
32700 setActive : function(state)
32702 if(this.active == state){
32706 this.active = state;
32709 this.el.addClass('active');
32713 this.el.removeClass('active');
32718 setDisabled : function(state)
32720 if(this.disabled == state){
32724 this.disabled = state;
32727 this.el.addClass('disabled');
32731 this.el.removeClass('disabled');
32734 tooltipEl : function()
32736 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32749 * @class Roo.bootstrap.FieldLabel
32750 * @extends Roo.bootstrap.Component
32751 * Bootstrap FieldLabel class
32752 * @cfg {String} html contents of the element
32753 * @cfg {String} tag tag of the element default label
32754 * @cfg {String} cls class of the element
32755 * @cfg {String} target label target
32756 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32757 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32758 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32759 * @cfg {String} iconTooltip default "This field is required"
32760 * @cfg {String} indicatorpos (left|right) default left
32763 * Create a new FieldLabel
32764 * @param {Object} config The config object
32767 Roo.bootstrap.FieldLabel = function(config){
32768 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32773 * Fires after the field has been marked as invalid.
32774 * @param {Roo.form.FieldLabel} this
32775 * @param {String} msg The validation message
32780 * Fires after the field has been validated with no errors.
32781 * @param {Roo.form.FieldLabel} this
32787 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32794 invalidClass : 'has-warning',
32795 validClass : 'has-success',
32796 iconTooltip : 'This field is required',
32797 indicatorpos : 'left',
32799 getAutoCreate : function(){
32802 if (!this.allowBlank) {
32808 cls : 'roo-bootstrap-field-label ' + this.cls,
32813 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32814 tooltip : this.iconTooltip
32823 if(this.indicatorpos == 'right'){
32826 cls : 'roo-bootstrap-field-label ' + this.cls,
32835 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32836 tooltip : this.iconTooltip
32845 initEvents: function()
32847 Roo.bootstrap.Element.superclass.initEvents.call(this);
32849 this.indicator = this.indicatorEl();
32851 if(this.indicator){
32852 this.indicator.removeClass('visible');
32853 this.indicator.addClass('invisible');
32856 Roo.bootstrap.FieldLabel.register(this);
32859 indicatorEl : function()
32861 var indicator = this.el.select('i.roo-required-indicator',true).first();
32872 * Mark this field as valid
32874 markValid : function()
32876 if(this.indicator){
32877 this.indicator.removeClass('visible');
32878 this.indicator.addClass('invisible');
32880 if (Roo.bootstrap.version == 3) {
32881 this.el.removeClass(this.invalidClass);
32882 this.el.addClass(this.validClass);
32884 this.el.removeClass('is-invalid');
32885 this.el.addClass('is-valid');
32889 this.fireEvent('valid', this);
32893 * Mark this field as invalid
32894 * @param {String} msg The validation message
32896 markInvalid : function(msg)
32898 if(this.indicator){
32899 this.indicator.removeClass('invisible');
32900 this.indicator.addClass('visible');
32902 if (Roo.bootstrap.version == 3) {
32903 this.el.removeClass(this.validClass);
32904 this.el.addClass(this.invalidClass);
32906 this.el.removeClass('is-valid');
32907 this.el.addClass('is-invalid');
32911 this.fireEvent('invalid', this, msg);
32917 Roo.apply(Roo.bootstrap.FieldLabel, {
32922 * register a FieldLabel Group
32923 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32925 register : function(label)
32927 if(this.groups.hasOwnProperty(label.target)){
32931 this.groups[label.target] = label;
32935 * fetch a FieldLabel Group based on the target
32936 * @param {string} target
32937 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32939 get: function(target) {
32940 if (typeof(this.groups[target]) == 'undefined') {
32944 return this.groups[target] ;
32953 * page DateSplitField.
32959 * @class Roo.bootstrap.DateSplitField
32960 * @extends Roo.bootstrap.Component
32961 * Bootstrap DateSplitField class
32962 * @cfg {string} fieldLabel - the label associated
32963 * @cfg {Number} labelWidth set the width of label (0-12)
32964 * @cfg {String} labelAlign (top|left)
32965 * @cfg {Boolean} dayAllowBlank (true|false) default false
32966 * @cfg {Boolean} monthAllowBlank (true|false) default false
32967 * @cfg {Boolean} yearAllowBlank (true|false) default false
32968 * @cfg {string} dayPlaceholder
32969 * @cfg {string} monthPlaceholder
32970 * @cfg {string} yearPlaceholder
32971 * @cfg {string} dayFormat default 'd'
32972 * @cfg {string} monthFormat default 'm'
32973 * @cfg {string} yearFormat default 'Y'
32974 * @cfg {Number} labellg set the width of label (1-12)
32975 * @cfg {Number} labelmd set the width of label (1-12)
32976 * @cfg {Number} labelsm set the width of label (1-12)
32977 * @cfg {Number} labelxs set the width of label (1-12)
32981 * Create a new DateSplitField
32982 * @param {Object} config The config object
32985 Roo.bootstrap.DateSplitField = function(config){
32986 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32992 * getting the data of years
32993 * @param {Roo.bootstrap.DateSplitField} this
32994 * @param {Object} years
32999 * getting the data of days
33000 * @param {Roo.bootstrap.DateSplitField} this
33001 * @param {Object} days
33006 * Fires after the field has been marked as invalid.
33007 * @param {Roo.form.Field} this
33008 * @param {String} msg The validation message
33013 * Fires after the field has been validated with no errors.
33014 * @param {Roo.form.Field} this
33020 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33023 labelAlign : 'top',
33025 dayAllowBlank : false,
33026 monthAllowBlank : false,
33027 yearAllowBlank : false,
33028 dayPlaceholder : '',
33029 monthPlaceholder : '',
33030 yearPlaceholder : '',
33034 isFormField : true,
33040 getAutoCreate : function()
33044 cls : 'row roo-date-split-field-group',
33049 cls : 'form-hidden-field roo-date-split-field-group-value',
33055 var labelCls = 'col-md-12';
33056 var contentCls = 'col-md-4';
33058 if(this.fieldLabel){
33062 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33066 html : this.fieldLabel
33071 if(this.labelAlign == 'left'){
33073 if(this.labelWidth > 12){
33074 label.style = "width: " + this.labelWidth + 'px';
33077 if(this.labelWidth < 13 && this.labelmd == 0){
33078 this.labelmd = this.labelWidth;
33081 if(this.labellg > 0){
33082 labelCls = ' col-lg-' + this.labellg;
33083 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33086 if(this.labelmd > 0){
33087 labelCls = ' col-md-' + this.labelmd;
33088 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33091 if(this.labelsm > 0){
33092 labelCls = ' col-sm-' + this.labelsm;
33093 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33096 if(this.labelxs > 0){
33097 labelCls = ' col-xs-' + this.labelxs;
33098 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33102 label.cls += ' ' + labelCls;
33104 cfg.cn.push(label);
33107 Roo.each(['day', 'month', 'year'], function(t){
33110 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33117 inputEl: function ()
33119 return this.el.select('.roo-date-split-field-group-value', true).first();
33122 onRender : function(ct, position)
33126 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33128 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33130 this.dayField = new Roo.bootstrap.ComboBox({
33131 allowBlank : this.dayAllowBlank,
33132 alwaysQuery : true,
33133 displayField : 'value',
33136 forceSelection : true,
33138 placeholder : this.dayPlaceholder,
33139 selectOnFocus : true,
33140 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33141 triggerAction : 'all',
33143 valueField : 'value',
33144 store : new Roo.data.SimpleStore({
33145 data : (function() {
33147 _this.fireEvent('days', _this, days);
33150 fields : [ 'value' ]
33153 select : function (_self, record, index)
33155 _this.setValue(_this.getValue());
33160 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33162 this.monthField = new Roo.bootstrap.MonthField({
33163 after : '<i class=\"fa fa-calendar\"></i>',
33164 allowBlank : this.monthAllowBlank,
33165 placeholder : this.monthPlaceholder,
33168 render : function (_self)
33170 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33171 e.preventDefault();
33175 select : function (_self, oldvalue, newvalue)
33177 _this.setValue(_this.getValue());
33182 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33184 this.yearField = new Roo.bootstrap.ComboBox({
33185 allowBlank : this.yearAllowBlank,
33186 alwaysQuery : true,
33187 displayField : 'value',
33190 forceSelection : true,
33192 placeholder : this.yearPlaceholder,
33193 selectOnFocus : true,
33194 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33195 triggerAction : 'all',
33197 valueField : 'value',
33198 store : new Roo.data.SimpleStore({
33199 data : (function() {
33201 _this.fireEvent('years', _this, years);
33204 fields : [ 'value' ]
33207 select : function (_self, record, index)
33209 _this.setValue(_this.getValue());
33214 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33217 setValue : function(v, format)
33219 this.inputEl.dom.value = v;
33221 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33223 var d = Date.parseDate(v, f);
33230 this.setDay(d.format(this.dayFormat));
33231 this.setMonth(d.format(this.monthFormat));
33232 this.setYear(d.format(this.yearFormat));
33239 setDay : function(v)
33241 this.dayField.setValue(v);
33242 this.inputEl.dom.value = this.getValue();
33247 setMonth : function(v)
33249 this.monthField.setValue(v, true);
33250 this.inputEl.dom.value = this.getValue();
33255 setYear : function(v)
33257 this.yearField.setValue(v);
33258 this.inputEl.dom.value = this.getValue();
33263 getDay : function()
33265 return this.dayField.getValue();
33268 getMonth : function()
33270 return this.monthField.getValue();
33273 getYear : function()
33275 return this.yearField.getValue();
33278 getValue : function()
33280 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33282 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33292 this.inputEl.dom.value = '';
33297 validate : function()
33299 var d = this.dayField.validate();
33300 var m = this.monthField.validate();
33301 var y = this.yearField.validate();
33306 (!this.dayAllowBlank && !d) ||
33307 (!this.monthAllowBlank && !m) ||
33308 (!this.yearAllowBlank && !y)
33313 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33322 this.markInvalid();
33327 markValid : function()
33330 var label = this.el.select('label', true).first();
33331 var icon = this.el.select('i.fa-star', true).first();
33337 this.fireEvent('valid', this);
33341 * Mark this field as invalid
33342 * @param {String} msg The validation message
33344 markInvalid : function(msg)
33347 var label = this.el.select('label', true).first();
33348 var icon = this.el.select('i.fa-star', true).first();
33350 if(label && !icon){
33351 this.el.select('.roo-date-split-field-label', true).createChild({
33353 cls : 'text-danger fa fa-lg fa-star',
33354 tooltip : 'This field is required',
33355 style : 'margin-right:5px;'
33359 this.fireEvent('invalid', this, msg);
33362 clearInvalid : function()
33364 var label = this.el.select('label', true).first();
33365 var icon = this.el.select('i.fa-star', true).first();
33371 this.fireEvent('valid', this);
33374 getName: function()
33384 * http://masonry.desandro.com
33386 * The idea is to render all the bricks based on vertical width...
33388 * The original code extends 'outlayer' - we might need to use that....
33394 * @class Roo.bootstrap.LayoutMasonry
33395 * @extends Roo.bootstrap.Component
33396 * Bootstrap Layout Masonry class
33399 * Create a new Element
33400 * @param {Object} config The config object
33403 Roo.bootstrap.LayoutMasonry = function(config){
33405 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33409 Roo.bootstrap.LayoutMasonry.register(this);
33415 * Fire after layout the items
33416 * @param {Roo.bootstrap.LayoutMasonry} this
33417 * @param {Roo.EventObject} e
33424 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33427 * @cfg {Boolean} isLayoutInstant = no animation?
33429 isLayoutInstant : false, // needed?
33432 * @cfg {Number} boxWidth width of the columns
33437 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33442 * @cfg {Number} padWidth padding below box..
33447 * @cfg {Number} gutter gutter width..
33452 * @cfg {Number} maxCols maximum number of columns
33458 * @cfg {Boolean} isAutoInitial defalut true
33460 isAutoInitial : true,
33465 * @cfg {Boolean} isHorizontal defalut false
33467 isHorizontal : false,
33469 currentSize : null,
33475 bricks: null, //CompositeElement
33479 _isLayoutInited : false,
33481 // isAlternative : false, // only use for vertical layout...
33484 * @cfg {Number} alternativePadWidth padding below box..
33486 alternativePadWidth : 50,
33488 selectedBrick : [],
33490 getAutoCreate : function(){
33492 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33496 cls: 'blog-masonary-wrapper ' + this.cls,
33498 cls : 'mas-boxes masonary'
33505 getChildContainer: function( )
33507 if (this.boxesEl) {
33508 return this.boxesEl;
33511 this.boxesEl = this.el.select('.mas-boxes').first();
33513 return this.boxesEl;
33517 initEvents : function()
33521 if(this.isAutoInitial){
33522 Roo.log('hook children rendered');
33523 this.on('childrenrendered', function() {
33524 Roo.log('children rendered');
33530 initial : function()
33532 this.selectedBrick = [];
33534 this.currentSize = this.el.getBox(true);
33536 Roo.EventManager.onWindowResize(this.resize, this);
33538 if(!this.isAutoInitial){
33546 //this.layout.defer(500,this);
33550 resize : function()
33552 var cs = this.el.getBox(true);
33555 this.currentSize.width == cs.width &&
33556 this.currentSize.x == cs.x &&
33557 this.currentSize.height == cs.height &&
33558 this.currentSize.y == cs.y
33560 Roo.log("no change in with or X or Y");
33564 this.currentSize = cs;
33570 layout : function()
33572 this._resetLayout();
33574 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33576 this.layoutItems( isInstant );
33578 this._isLayoutInited = true;
33580 this.fireEvent('layout', this);
33584 _resetLayout : function()
33586 if(this.isHorizontal){
33587 this.horizontalMeasureColumns();
33591 this.verticalMeasureColumns();
33595 verticalMeasureColumns : function()
33597 this.getContainerWidth();
33599 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33600 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33604 var boxWidth = this.boxWidth + this.padWidth;
33606 if(this.containerWidth < this.boxWidth){
33607 boxWidth = this.containerWidth
33610 var containerWidth = this.containerWidth;
33612 var cols = Math.floor(containerWidth / boxWidth);
33614 this.cols = Math.max( cols, 1 );
33616 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33618 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33620 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33622 this.colWidth = boxWidth + avail - this.padWidth;
33624 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33625 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33628 horizontalMeasureColumns : function()
33630 this.getContainerWidth();
33632 var boxWidth = this.boxWidth;
33634 if(this.containerWidth < boxWidth){
33635 boxWidth = this.containerWidth;
33638 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33640 this.el.setHeight(boxWidth);
33644 getContainerWidth : function()
33646 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33649 layoutItems : function( isInstant )
33651 Roo.log(this.bricks);
33653 var items = Roo.apply([], this.bricks);
33655 if(this.isHorizontal){
33656 this._horizontalLayoutItems( items , isInstant );
33660 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33661 // this._verticalAlternativeLayoutItems( items , isInstant );
33665 this._verticalLayoutItems( items , isInstant );
33669 _verticalLayoutItems : function ( items , isInstant)
33671 if ( !items || !items.length ) {
33676 ['xs', 'xs', 'xs', 'tall'],
33677 ['xs', 'xs', 'tall'],
33678 ['xs', 'xs', 'sm'],
33679 ['xs', 'xs', 'xs'],
33685 ['sm', 'xs', 'xs'],
33689 ['tall', 'xs', 'xs', 'xs'],
33690 ['tall', 'xs', 'xs'],
33702 Roo.each(items, function(item, k){
33704 switch (item.size) {
33705 // these layouts take up a full box,
33716 boxes.push([item]);
33739 var filterPattern = function(box, length)
33747 var pattern = box.slice(0, length);
33751 Roo.each(pattern, function(i){
33752 format.push(i.size);
33755 Roo.each(standard, function(s){
33757 if(String(s) != String(format)){
33766 if(!match && length == 1){
33771 filterPattern(box, length - 1);
33775 queue.push(pattern);
33777 box = box.slice(length, box.length);
33779 filterPattern(box, 4);
33785 Roo.each(boxes, function(box, k){
33791 if(box.length == 1){
33796 filterPattern(box, 4);
33800 this._processVerticalLayoutQueue( queue, isInstant );
33804 // _verticalAlternativeLayoutItems : function( items , isInstant )
33806 // if ( !items || !items.length ) {
33810 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33814 _horizontalLayoutItems : function ( items , isInstant)
33816 if ( !items || !items.length || items.length < 3) {
33822 var eItems = items.slice(0, 3);
33824 items = items.slice(3, items.length);
33827 ['xs', 'xs', 'xs', 'wide'],
33828 ['xs', 'xs', 'wide'],
33829 ['xs', 'xs', 'sm'],
33830 ['xs', 'xs', 'xs'],
33836 ['sm', 'xs', 'xs'],
33840 ['wide', 'xs', 'xs', 'xs'],
33841 ['wide', 'xs', 'xs'],
33854 Roo.each(items, function(item, k){
33856 switch (item.size) {
33867 boxes.push([item]);
33891 var filterPattern = function(box, length)
33899 var pattern = box.slice(0, length);
33903 Roo.each(pattern, function(i){
33904 format.push(i.size);
33907 Roo.each(standard, function(s){
33909 if(String(s) != String(format)){
33918 if(!match && length == 1){
33923 filterPattern(box, length - 1);
33927 queue.push(pattern);
33929 box = box.slice(length, box.length);
33931 filterPattern(box, 4);
33937 Roo.each(boxes, function(box, k){
33943 if(box.length == 1){
33948 filterPattern(box, 4);
33955 var pos = this.el.getBox(true);
33959 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33961 var hit_end = false;
33963 Roo.each(queue, function(box){
33967 Roo.each(box, function(b){
33969 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33979 Roo.each(box, function(b){
33981 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33984 mx = Math.max(mx, b.x);
33988 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33992 Roo.each(box, function(b){
33994 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34008 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34011 /** Sets position of item in DOM
34012 * @param {Element} item
34013 * @param {Number} x - horizontal position
34014 * @param {Number} y - vertical position
34015 * @param {Boolean} isInstant - disables transitions
34017 _processVerticalLayoutQueue : function( queue, isInstant )
34019 var pos = this.el.getBox(true);
34024 for (var i = 0; i < this.cols; i++){
34028 Roo.each(queue, function(box, k){
34030 var col = k % this.cols;
34032 Roo.each(box, function(b,kk){
34034 b.el.position('absolute');
34036 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34037 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34039 if(b.size == 'md-left' || b.size == 'md-right'){
34040 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34041 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34044 b.el.setWidth(width);
34045 b.el.setHeight(height);
34047 b.el.select('iframe',true).setSize(width,height);
34051 for (var i = 0; i < this.cols; i++){
34053 if(maxY[i] < maxY[col]){
34058 col = Math.min(col, i);
34062 x = pos.x + col * (this.colWidth + this.padWidth);
34066 var positions = [];
34068 switch (box.length){
34070 positions = this.getVerticalOneBoxColPositions(x, y, box);
34073 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34076 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34079 positions = this.getVerticalFourBoxColPositions(x, y, box);
34085 Roo.each(box, function(b,kk){
34087 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34089 var sz = b.el.getSize();
34091 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34099 for (var i = 0; i < this.cols; i++){
34100 mY = Math.max(mY, maxY[i]);
34103 this.el.setHeight(mY - pos.y);
34107 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34109 // var pos = this.el.getBox(true);
34112 // var maxX = pos.right;
34114 // var maxHeight = 0;
34116 // Roo.each(items, function(item, k){
34120 // item.el.position('absolute');
34122 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34124 // item.el.setWidth(width);
34126 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34128 // item.el.setHeight(height);
34131 // item.el.setXY([x, y], isInstant ? false : true);
34133 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34136 // y = y + height + this.alternativePadWidth;
34138 // maxHeight = maxHeight + height + this.alternativePadWidth;
34142 // this.el.setHeight(maxHeight);
34146 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34148 var pos = this.el.getBox(true);
34153 var maxX = pos.right;
34155 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34157 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34159 Roo.each(queue, function(box, k){
34161 Roo.each(box, function(b, kk){
34163 b.el.position('absolute');
34165 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34166 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34168 if(b.size == 'md-left' || b.size == 'md-right'){
34169 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34170 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34173 b.el.setWidth(width);
34174 b.el.setHeight(height);
34182 var positions = [];
34184 switch (box.length){
34186 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34189 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34192 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34195 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34201 Roo.each(box, function(b,kk){
34203 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34205 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34213 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34215 Roo.each(eItems, function(b,k){
34217 b.size = (k == 0) ? 'sm' : 'xs';
34218 b.x = (k == 0) ? 2 : 1;
34219 b.y = (k == 0) ? 2 : 1;
34221 b.el.position('absolute');
34223 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34225 b.el.setWidth(width);
34227 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34229 b.el.setHeight(height);
34233 var positions = [];
34236 x : maxX - this.unitWidth * 2 - this.gutter,
34241 x : maxX - this.unitWidth,
34242 y : minY + (this.unitWidth + this.gutter) * 2
34246 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34250 Roo.each(eItems, function(b,k){
34252 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34258 getVerticalOneBoxColPositions : function(x, y, box)
34262 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34264 if(box[0].size == 'md-left'){
34268 if(box[0].size == 'md-right'){
34273 x : x + (this.unitWidth + this.gutter) * rand,
34280 getVerticalTwoBoxColPositions : function(x, y, box)
34284 if(box[0].size == 'xs'){
34288 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34292 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34306 x : x + (this.unitWidth + this.gutter) * 2,
34307 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34314 getVerticalThreeBoxColPositions : function(x, y, box)
34318 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34326 x : x + (this.unitWidth + this.gutter) * 1,
34331 x : x + (this.unitWidth + this.gutter) * 2,
34339 if(box[0].size == 'xs' && box[1].size == 'xs'){
34348 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34352 x : x + (this.unitWidth + this.gutter) * 1,
34366 x : x + (this.unitWidth + this.gutter) * 2,
34371 x : x + (this.unitWidth + this.gutter) * 2,
34372 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34379 getVerticalFourBoxColPositions : function(x, y, box)
34383 if(box[0].size == 'xs'){
34392 y : y + (this.unitHeight + this.gutter) * 1
34397 y : y + (this.unitHeight + this.gutter) * 2
34401 x : x + (this.unitWidth + this.gutter) * 1,
34415 x : x + (this.unitWidth + this.gutter) * 2,
34420 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34421 y : y + (this.unitHeight + this.gutter) * 1
34425 x : x + (this.unitWidth + this.gutter) * 2,
34426 y : y + (this.unitWidth + this.gutter) * 2
34433 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34437 if(box[0].size == 'md-left'){
34439 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34446 if(box[0].size == 'md-right'){
34448 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34449 y : minY + (this.unitWidth + this.gutter) * 1
34455 var rand = Math.floor(Math.random() * (4 - box[0].y));
34458 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34459 y : minY + (this.unitWidth + this.gutter) * rand
34466 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34470 if(box[0].size == 'xs'){
34473 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34478 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34479 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34487 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34492 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34493 y : minY + (this.unitWidth + this.gutter) * 2
34500 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34504 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34507 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34512 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34513 y : minY + (this.unitWidth + this.gutter) * 1
34517 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34518 y : minY + (this.unitWidth + this.gutter) * 2
34525 if(box[0].size == 'xs' && box[1].size == 'xs'){
34528 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34533 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34538 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34539 y : minY + (this.unitWidth + this.gutter) * 1
34547 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34552 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34553 y : minY + (this.unitWidth + this.gutter) * 2
34557 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34558 y : minY + (this.unitWidth + this.gutter) * 2
34565 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34569 if(box[0].size == 'xs'){
34572 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34577 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34582 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),
34587 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34588 y : minY + (this.unitWidth + this.gutter) * 1
34596 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34601 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34602 y : minY + (this.unitWidth + this.gutter) * 2
34606 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34607 y : minY + (this.unitWidth + this.gutter) * 2
34611 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),
34612 y : minY + (this.unitWidth + this.gutter) * 2
34620 * remove a Masonry Brick
34621 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34623 removeBrick : function(brick_id)
34629 for (var i = 0; i<this.bricks.length; i++) {
34630 if (this.bricks[i].id == brick_id) {
34631 this.bricks.splice(i,1);
34632 this.el.dom.removeChild(Roo.get(brick_id).dom);
34639 * adds a Masonry Brick
34640 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34642 addBrick : function(cfg)
34644 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34645 //this.register(cn);
34646 cn.parentId = this.id;
34647 cn.render(this.el);
34652 * register a Masonry Brick
34653 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34656 register : function(brick)
34658 this.bricks.push(brick);
34659 brick.masonryId = this.id;
34663 * clear all the Masonry Brick
34665 clearAll : function()
34668 //this.getChildContainer().dom.innerHTML = "";
34669 this.el.dom.innerHTML = '';
34672 getSelected : function()
34674 if (!this.selectedBrick) {
34678 return this.selectedBrick;
34682 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34686 * register a Masonry Layout
34687 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34690 register : function(layout)
34692 this.groups[layout.id] = layout;
34695 * fetch a Masonry Layout based on the masonry layout ID
34696 * @param {string} the masonry layout to add
34697 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34700 get: function(layout_id) {
34701 if (typeof(this.groups[layout_id]) == 'undefined') {
34704 return this.groups[layout_id] ;
34716 * http://masonry.desandro.com
34718 * The idea is to render all the bricks based on vertical width...
34720 * The original code extends 'outlayer' - we might need to use that....
34726 * @class Roo.bootstrap.LayoutMasonryAuto
34727 * @extends Roo.bootstrap.Component
34728 * Bootstrap Layout Masonry class
34731 * Create a new Element
34732 * @param {Object} config The config object
34735 Roo.bootstrap.LayoutMasonryAuto = function(config){
34736 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34739 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34742 * @cfg {Boolean} isFitWidth - resize the width..
34744 isFitWidth : false, // options..
34746 * @cfg {Boolean} isOriginLeft = left align?
34748 isOriginLeft : true,
34750 * @cfg {Boolean} isOriginTop = top align?
34752 isOriginTop : false,
34754 * @cfg {Boolean} isLayoutInstant = no animation?
34756 isLayoutInstant : false, // needed?
34758 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34760 isResizingContainer : true,
34762 * @cfg {Number} columnWidth width of the columns
34768 * @cfg {Number} maxCols maximum number of columns
34773 * @cfg {Number} padHeight padding below box..
34779 * @cfg {Boolean} isAutoInitial defalut true
34782 isAutoInitial : true,
34788 initialColumnWidth : 0,
34789 currentSize : null,
34791 colYs : null, // array.
34798 bricks: null, //CompositeElement
34799 cols : 0, // array?
34800 // element : null, // wrapped now this.el
34801 _isLayoutInited : null,
34804 getAutoCreate : function(){
34808 cls: 'blog-masonary-wrapper ' + this.cls,
34810 cls : 'mas-boxes masonary'
34817 getChildContainer: function( )
34819 if (this.boxesEl) {
34820 return this.boxesEl;
34823 this.boxesEl = this.el.select('.mas-boxes').first();
34825 return this.boxesEl;
34829 initEvents : function()
34833 if(this.isAutoInitial){
34834 Roo.log('hook children rendered');
34835 this.on('childrenrendered', function() {
34836 Roo.log('children rendered');
34843 initial : function()
34845 this.reloadItems();
34847 this.currentSize = this.el.getBox(true);
34849 /// was window resize... - let's see if this works..
34850 Roo.EventManager.onWindowResize(this.resize, this);
34852 if(!this.isAutoInitial){
34857 this.layout.defer(500,this);
34860 reloadItems: function()
34862 this.bricks = this.el.select('.masonry-brick', true);
34864 this.bricks.each(function(b) {
34865 //Roo.log(b.getSize());
34866 if (!b.attr('originalwidth')) {
34867 b.attr('originalwidth', b.getSize().width);
34872 Roo.log(this.bricks.elements.length);
34875 resize : function()
34878 var cs = this.el.getBox(true);
34880 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34881 Roo.log("no change in with or X");
34884 this.currentSize = cs;
34888 layout : function()
34891 this._resetLayout();
34892 //this._manageStamps();
34894 // don't animate first layout
34895 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34896 this.layoutItems( isInstant );
34898 // flag for initalized
34899 this._isLayoutInited = true;
34902 layoutItems : function( isInstant )
34904 //var items = this._getItemsForLayout( this.items );
34905 // original code supports filtering layout items.. we just ignore it..
34907 this._layoutItems( this.bricks , isInstant );
34909 this._postLayout();
34911 _layoutItems : function ( items , isInstant)
34913 //this.fireEvent( 'layout', this, items );
34916 if ( !items || !items.elements.length ) {
34917 // no items, emit event with empty array
34922 items.each(function(item) {
34923 Roo.log("layout item");
34925 // get x/y object from method
34926 var position = this._getItemLayoutPosition( item );
34928 position.item = item;
34929 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34930 queue.push( position );
34933 this._processLayoutQueue( queue );
34935 /** Sets position of item in DOM
34936 * @param {Element} item
34937 * @param {Number} x - horizontal position
34938 * @param {Number} y - vertical position
34939 * @param {Boolean} isInstant - disables transitions
34941 _processLayoutQueue : function( queue )
34943 for ( var i=0, len = queue.length; i < len; i++ ) {
34944 var obj = queue[i];
34945 obj.item.position('absolute');
34946 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34952 * Any logic you want to do after each layout,
34953 * i.e. size the container
34955 _postLayout : function()
34957 this.resizeContainer();
34960 resizeContainer : function()
34962 if ( !this.isResizingContainer ) {
34965 var size = this._getContainerSize();
34967 this.el.setSize(size.width,size.height);
34968 this.boxesEl.setSize(size.width,size.height);
34974 _resetLayout : function()
34976 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34977 this.colWidth = this.el.getWidth();
34978 //this.gutter = this.el.getWidth();
34980 this.measureColumns();
34986 this.colYs.push( 0 );
34992 measureColumns : function()
34994 this.getContainerWidth();
34995 // if columnWidth is 0, default to outerWidth of first item
34996 if ( !this.columnWidth ) {
34997 var firstItem = this.bricks.first();
34998 Roo.log(firstItem);
34999 this.columnWidth = this.containerWidth;
35000 if (firstItem && firstItem.attr('originalwidth') ) {
35001 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35003 // columnWidth fall back to item of first element
35004 Roo.log("set column width?");
35005 this.initialColumnWidth = this.columnWidth ;
35007 // if first elem has no width, default to size of container
35012 if (this.initialColumnWidth) {
35013 this.columnWidth = this.initialColumnWidth;
35018 // column width is fixed at the top - however if container width get's smaller we should
35021 // this bit calcs how man columns..
35023 var columnWidth = this.columnWidth += this.gutter;
35025 // calculate columns
35026 var containerWidth = this.containerWidth + this.gutter;
35028 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35029 // fix rounding errors, typically with gutters
35030 var excess = columnWidth - containerWidth % columnWidth;
35033 // if overshoot is less than a pixel, round up, otherwise floor it
35034 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35035 cols = Math[ mathMethod ]( cols );
35036 this.cols = Math.max( cols, 1 );
35037 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35039 // padding positioning..
35040 var totalColWidth = this.cols * this.columnWidth;
35041 var padavail = this.containerWidth - totalColWidth;
35042 // so for 2 columns - we need 3 'pads'
35044 var padNeeded = (1+this.cols) * this.padWidth;
35046 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35048 this.columnWidth += padExtra
35049 //this.padWidth = Math.floor(padavail / ( this.cols));
35051 // adjust colum width so that padding is fixed??
35053 // we have 3 columns ... total = width * 3
35054 // we have X left over... that should be used by
35056 //if (this.expandC) {
35064 getContainerWidth : function()
35066 /* // container is parent if fit width
35067 var container = this.isFitWidth ? this.element.parentNode : this.element;
35068 // check that this.size and size are there
35069 // IE8 triggers resize on body size change, so they might not be
35071 var size = getSize( container ); //FIXME
35072 this.containerWidth = size && size.innerWidth; //FIXME
35075 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35079 _getItemLayoutPosition : function( item ) // what is item?
35081 // we resize the item to our columnWidth..
35083 item.setWidth(this.columnWidth);
35084 item.autoBoxAdjust = false;
35086 var sz = item.getSize();
35088 // how many columns does this brick span
35089 var remainder = this.containerWidth % this.columnWidth;
35091 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35092 // round if off by 1 pixel, otherwise use ceil
35093 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35094 colSpan = Math.min( colSpan, this.cols );
35096 // normally this should be '1' as we dont' currently allow multi width columns..
35098 var colGroup = this._getColGroup( colSpan );
35099 // get the minimum Y value from the columns
35100 var minimumY = Math.min.apply( Math, colGroup );
35101 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35103 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35105 // position the brick
35107 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35108 y: this.currentSize.y + minimumY + this.padHeight
35112 // apply setHeight to necessary columns
35113 var setHeight = minimumY + sz.height + this.padHeight;
35114 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35116 var setSpan = this.cols + 1 - colGroup.length;
35117 for ( var i = 0; i < setSpan; i++ ) {
35118 this.colYs[ shortColIndex + i ] = setHeight ;
35125 * @param {Number} colSpan - number of columns the element spans
35126 * @returns {Array} colGroup
35128 _getColGroup : function( colSpan )
35130 if ( colSpan < 2 ) {
35131 // if brick spans only one column, use all the column Ys
35136 // how many different places could this brick fit horizontally
35137 var groupCount = this.cols + 1 - colSpan;
35138 // for each group potential horizontal position
35139 for ( var i = 0; i < groupCount; i++ ) {
35140 // make an array of colY values for that one group
35141 var groupColYs = this.colYs.slice( i, i + colSpan );
35142 // and get the max value of the array
35143 colGroup[i] = Math.max.apply( Math, groupColYs );
35148 _manageStamp : function( stamp )
35150 var stampSize = stamp.getSize();
35151 var offset = stamp.getBox();
35152 // get the columns that this stamp affects
35153 var firstX = this.isOriginLeft ? offset.x : offset.right;
35154 var lastX = firstX + stampSize.width;
35155 var firstCol = Math.floor( firstX / this.columnWidth );
35156 firstCol = Math.max( 0, firstCol );
35158 var lastCol = Math.floor( lastX / this.columnWidth );
35159 // lastCol should not go over if multiple of columnWidth #425
35160 lastCol -= lastX % this.columnWidth ? 0 : 1;
35161 lastCol = Math.min( this.cols - 1, lastCol );
35163 // set colYs to bottom of the stamp
35164 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35167 for ( var i = firstCol; i <= lastCol; i++ ) {
35168 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35173 _getContainerSize : function()
35175 this.maxY = Math.max.apply( Math, this.colYs );
35180 if ( this.isFitWidth ) {
35181 size.width = this._getContainerFitWidth();
35187 _getContainerFitWidth : function()
35189 var unusedCols = 0;
35190 // count unused columns
35193 if ( this.colYs[i] !== 0 ) {
35198 // fit container to columns that have been used
35199 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35202 needsResizeLayout : function()
35204 var previousWidth = this.containerWidth;
35205 this.getContainerWidth();
35206 return previousWidth !== this.containerWidth;
35221 * @class Roo.bootstrap.MasonryBrick
35222 * @extends Roo.bootstrap.Component
35223 * Bootstrap MasonryBrick class
35226 * Create a new MasonryBrick
35227 * @param {Object} config The config object
35230 Roo.bootstrap.MasonryBrick = function(config){
35232 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35234 Roo.bootstrap.MasonryBrick.register(this);
35240 * When a MasonryBrick is clcik
35241 * @param {Roo.bootstrap.MasonryBrick} this
35242 * @param {Roo.EventObject} e
35248 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35251 * @cfg {String} title
35255 * @cfg {String} html
35259 * @cfg {String} bgimage
35263 * @cfg {String} videourl
35267 * @cfg {String} cls
35271 * @cfg {String} href
35275 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35280 * @cfg {String} placetitle (center|bottom)
35285 * @cfg {Boolean} isFitContainer defalut true
35287 isFitContainer : true,
35290 * @cfg {Boolean} preventDefault defalut false
35292 preventDefault : false,
35295 * @cfg {Boolean} inverse defalut false
35297 maskInverse : false,
35299 getAutoCreate : function()
35301 if(!this.isFitContainer){
35302 return this.getSplitAutoCreate();
35305 var cls = 'masonry-brick masonry-brick-full';
35307 if(this.href.length){
35308 cls += ' masonry-brick-link';
35311 if(this.bgimage.length){
35312 cls += ' masonry-brick-image';
35315 if(this.maskInverse){
35316 cls += ' mask-inverse';
35319 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35320 cls += ' enable-mask';
35324 cls += ' masonry-' + this.size + '-brick';
35327 if(this.placetitle.length){
35329 switch (this.placetitle) {
35331 cls += ' masonry-center-title';
35334 cls += ' masonry-bottom-title';
35341 if(!this.html.length && !this.bgimage.length){
35342 cls += ' masonry-center-title';
35345 if(!this.html.length && this.bgimage.length){
35346 cls += ' masonry-bottom-title';
35351 cls += ' ' + this.cls;
35355 tag: (this.href.length) ? 'a' : 'div',
35360 cls: 'masonry-brick-mask'
35364 cls: 'masonry-brick-paragraph',
35370 if(this.href.length){
35371 cfg.href = this.href;
35374 var cn = cfg.cn[1].cn;
35376 if(this.title.length){
35379 cls: 'masonry-brick-title',
35384 if(this.html.length){
35387 cls: 'masonry-brick-text',
35392 if (!this.title.length && !this.html.length) {
35393 cfg.cn[1].cls += ' hide';
35396 if(this.bgimage.length){
35399 cls: 'masonry-brick-image-view',
35404 if(this.videourl.length){
35405 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35406 // youtube support only?
35409 cls: 'masonry-brick-image-view',
35412 allowfullscreen : true
35420 getSplitAutoCreate : function()
35422 var cls = 'masonry-brick masonry-brick-split';
35424 if(this.href.length){
35425 cls += ' masonry-brick-link';
35428 if(this.bgimage.length){
35429 cls += ' masonry-brick-image';
35433 cls += ' masonry-' + this.size + '-brick';
35436 switch (this.placetitle) {
35438 cls += ' masonry-center-title';
35441 cls += ' masonry-bottom-title';
35444 if(!this.bgimage.length){
35445 cls += ' masonry-center-title';
35448 if(this.bgimage.length){
35449 cls += ' masonry-bottom-title';
35455 cls += ' ' + this.cls;
35459 tag: (this.href.length) ? 'a' : 'div',
35464 cls: 'masonry-brick-split-head',
35468 cls: 'masonry-brick-paragraph',
35475 cls: 'masonry-brick-split-body',
35481 if(this.href.length){
35482 cfg.href = this.href;
35485 if(this.title.length){
35486 cfg.cn[0].cn[0].cn.push({
35488 cls: 'masonry-brick-title',
35493 if(this.html.length){
35494 cfg.cn[1].cn.push({
35496 cls: 'masonry-brick-text',
35501 if(this.bgimage.length){
35502 cfg.cn[0].cn.push({
35504 cls: 'masonry-brick-image-view',
35509 if(this.videourl.length){
35510 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35511 // youtube support only?
35512 cfg.cn[0].cn.cn.push({
35514 cls: 'masonry-brick-image-view',
35517 allowfullscreen : true
35524 initEvents: function()
35526 switch (this.size) {
35559 this.el.on('touchstart', this.onTouchStart, this);
35560 this.el.on('touchmove', this.onTouchMove, this);
35561 this.el.on('touchend', this.onTouchEnd, this);
35562 this.el.on('contextmenu', this.onContextMenu, this);
35564 this.el.on('mouseenter' ,this.enter, this);
35565 this.el.on('mouseleave', this.leave, this);
35566 this.el.on('click', this.onClick, this);
35569 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35570 this.parent().bricks.push(this);
35575 onClick: function(e, el)
35577 var time = this.endTimer - this.startTimer;
35578 // Roo.log(e.preventDefault());
35581 e.preventDefault();
35586 if(!this.preventDefault){
35590 e.preventDefault();
35592 if (this.activeClass != '') {
35593 this.selectBrick();
35596 this.fireEvent('click', this, e);
35599 enter: function(e, el)
35601 e.preventDefault();
35603 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35607 if(this.bgimage.length && this.html.length){
35608 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35612 leave: function(e, el)
35614 e.preventDefault();
35616 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35620 if(this.bgimage.length && this.html.length){
35621 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35625 onTouchStart: function(e, el)
35627 // e.preventDefault();
35629 this.touchmoved = false;
35631 if(!this.isFitContainer){
35635 if(!this.bgimage.length || !this.html.length){
35639 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35641 this.timer = new Date().getTime();
35645 onTouchMove: function(e, el)
35647 this.touchmoved = true;
35650 onContextMenu : function(e,el)
35652 e.preventDefault();
35653 e.stopPropagation();
35657 onTouchEnd: function(e, el)
35659 // e.preventDefault();
35661 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35668 if(!this.bgimage.length || !this.html.length){
35670 if(this.href.length){
35671 window.location.href = this.href;
35677 if(!this.isFitContainer){
35681 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35683 window.location.href = this.href;
35686 //selection on single brick only
35687 selectBrick : function() {
35689 if (!this.parentId) {
35693 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35694 var index = m.selectedBrick.indexOf(this.id);
35697 m.selectedBrick.splice(index,1);
35698 this.el.removeClass(this.activeClass);
35702 for(var i = 0; i < m.selectedBrick.length; i++) {
35703 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35704 b.el.removeClass(b.activeClass);
35707 m.selectedBrick = [];
35709 m.selectedBrick.push(this.id);
35710 this.el.addClass(this.activeClass);
35714 isSelected : function(){
35715 return this.el.hasClass(this.activeClass);
35720 Roo.apply(Roo.bootstrap.MasonryBrick, {
35723 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35725 * register a Masonry Brick
35726 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35729 register : function(brick)
35731 //this.groups[brick.id] = brick;
35732 this.groups.add(brick.id, brick);
35735 * fetch a masonry brick based on the masonry brick ID
35736 * @param {string} the masonry brick to add
35737 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35740 get: function(brick_id)
35742 // if (typeof(this.groups[brick_id]) == 'undefined') {
35745 // return this.groups[brick_id] ;
35747 if(this.groups.key(brick_id)) {
35748 return this.groups.key(brick_id);
35766 * @class Roo.bootstrap.Brick
35767 * @extends Roo.bootstrap.Component
35768 * Bootstrap Brick class
35771 * Create a new Brick
35772 * @param {Object} config The config object
35775 Roo.bootstrap.Brick = function(config){
35776 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35782 * When a Brick is click
35783 * @param {Roo.bootstrap.Brick} this
35784 * @param {Roo.EventObject} e
35790 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35793 * @cfg {String} title
35797 * @cfg {String} html
35801 * @cfg {String} bgimage
35805 * @cfg {String} cls
35809 * @cfg {String} href
35813 * @cfg {String} video
35817 * @cfg {Boolean} square
35821 getAutoCreate : function()
35823 var cls = 'roo-brick';
35825 if(this.href.length){
35826 cls += ' roo-brick-link';
35829 if(this.bgimage.length){
35830 cls += ' roo-brick-image';
35833 if(!this.html.length && !this.bgimage.length){
35834 cls += ' roo-brick-center-title';
35837 if(!this.html.length && this.bgimage.length){
35838 cls += ' roo-brick-bottom-title';
35842 cls += ' ' + this.cls;
35846 tag: (this.href.length) ? 'a' : 'div',
35851 cls: 'roo-brick-paragraph',
35857 if(this.href.length){
35858 cfg.href = this.href;
35861 var cn = cfg.cn[0].cn;
35863 if(this.title.length){
35866 cls: 'roo-brick-title',
35871 if(this.html.length){
35874 cls: 'roo-brick-text',
35881 if(this.bgimage.length){
35884 cls: 'roo-brick-image-view',
35892 initEvents: function()
35894 if(this.title.length || this.html.length){
35895 this.el.on('mouseenter' ,this.enter, this);
35896 this.el.on('mouseleave', this.leave, this);
35899 Roo.EventManager.onWindowResize(this.resize, this);
35901 if(this.bgimage.length){
35902 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35903 this.imageEl.on('load', this.onImageLoad, this);
35910 onImageLoad : function()
35915 resize : function()
35917 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35919 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35921 if(this.bgimage.length){
35922 var image = this.el.select('.roo-brick-image-view', true).first();
35924 image.setWidth(paragraph.getWidth());
35927 image.setHeight(paragraph.getWidth());
35930 this.el.setHeight(image.getHeight());
35931 paragraph.setHeight(image.getHeight());
35937 enter: function(e, el)
35939 e.preventDefault();
35941 if(this.bgimage.length){
35942 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35943 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35947 leave: function(e, el)
35949 e.preventDefault();
35951 if(this.bgimage.length){
35952 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35953 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35968 * @class Roo.bootstrap.NumberField
35969 * @extends Roo.bootstrap.Input
35970 * Bootstrap NumberField class
35976 * Create a new NumberField
35977 * @param {Object} config The config object
35980 Roo.bootstrap.NumberField = function(config){
35981 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35984 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35987 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35989 allowDecimals : true,
35991 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35993 decimalSeparator : ".",
35995 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35997 decimalPrecision : 2,
35999 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36001 allowNegative : true,
36004 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36008 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36010 minValue : Number.NEGATIVE_INFINITY,
36012 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36014 maxValue : Number.MAX_VALUE,
36016 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36018 minText : "The minimum value for this field is {0}",
36020 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36022 maxText : "The maximum value for this field is {0}",
36024 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36025 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36027 nanText : "{0} is not a valid number",
36029 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36031 thousandsDelimiter : false,
36033 * @cfg {String} valueAlign alignment of value
36035 valueAlign : "left",
36037 getAutoCreate : function()
36039 var hiddenInput = {
36043 cls: 'hidden-number-input'
36047 hiddenInput.name = this.name;
36052 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36054 this.name = hiddenInput.name;
36056 if(cfg.cn.length > 0) {
36057 cfg.cn.push(hiddenInput);
36064 initEvents : function()
36066 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36068 var allowed = "0123456789";
36070 if(this.allowDecimals){
36071 allowed += this.decimalSeparator;
36074 if(this.allowNegative){
36078 if(this.thousandsDelimiter) {
36082 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36084 var keyPress = function(e){
36086 var k = e.getKey();
36088 var c = e.getCharCode();
36091 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36092 allowed.indexOf(String.fromCharCode(c)) === -1
36098 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36102 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36107 this.el.on("keypress", keyPress, this);
36110 validateValue : function(value)
36113 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36117 var num = this.parseValue(value);
36120 this.markInvalid(String.format(this.nanText, value));
36124 if(num < this.minValue){
36125 this.markInvalid(String.format(this.minText, this.minValue));
36129 if(num > this.maxValue){
36130 this.markInvalid(String.format(this.maxText, this.maxValue));
36137 getValue : function()
36139 var v = this.hiddenEl().getValue();
36141 return this.fixPrecision(this.parseValue(v));
36144 parseValue : function(value)
36146 if(this.thousandsDelimiter) {
36148 r = new RegExp(",", "g");
36149 value = value.replace(r, "");
36152 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36153 return isNaN(value) ? '' : value;
36156 fixPrecision : function(value)
36158 if(this.thousandsDelimiter) {
36160 r = new RegExp(",", "g");
36161 value = value.replace(r, "");
36164 var nan = isNaN(value);
36166 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36167 return nan ? '' : value;
36169 return parseFloat(value).toFixed(this.decimalPrecision);
36172 setValue : function(v)
36174 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36180 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36182 this.inputEl().dom.value = (v == '') ? '' :
36183 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36185 if(!this.allowZero && v === '0') {
36186 this.hiddenEl().dom.value = '';
36187 this.inputEl().dom.value = '';
36194 decimalPrecisionFcn : function(v)
36196 return Math.floor(v);
36199 beforeBlur : function()
36201 var v = this.parseValue(this.getRawValue());
36203 if(v || v === 0 || v === ''){
36208 hiddenEl : function()
36210 return this.el.select('input.hidden-number-input',true).first();
36222 * @class Roo.bootstrap.DocumentSlider
36223 * @extends Roo.bootstrap.Component
36224 * Bootstrap DocumentSlider class
36227 * Create a new DocumentViewer
36228 * @param {Object} config The config object
36231 Roo.bootstrap.DocumentSlider = function(config){
36232 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36239 * Fire after initEvent
36240 * @param {Roo.bootstrap.DocumentSlider} this
36245 * Fire after update
36246 * @param {Roo.bootstrap.DocumentSlider} this
36252 * @param {Roo.bootstrap.DocumentSlider} this
36258 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36264 getAutoCreate : function()
36268 cls : 'roo-document-slider',
36272 cls : 'roo-document-slider-header',
36276 cls : 'roo-document-slider-header-title'
36282 cls : 'roo-document-slider-body',
36286 cls : 'roo-document-slider-prev',
36290 cls : 'fa fa-chevron-left'
36296 cls : 'roo-document-slider-thumb',
36300 cls : 'roo-document-slider-image'
36306 cls : 'roo-document-slider-next',
36310 cls : 'fa fa-chevron-right'
36322 initEvents : function()
36324 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36325 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36327 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36328 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36330 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36331 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36333 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36334 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36336 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36337 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36339 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36340 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36342 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36343 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36345 this.thumbEl.on('click', this.onClick, this);
36347 this.prevIndicator.on('click', this.prev, this);
36349 this.nextIndicator.on('click', this.next, this);
36353 initial : function()
36355 if(this.files.length){
36356 this.indicator = 1;
36360 this.fireEvent('initial', this);
36363 update : function()
36365 this.imageEl.attr('src', this.files[this.indicator - 1]);
36367 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36369 this.prevIndicator.show();
36371 if(this.indicator == 1){
36372 this.prevIndicator.hide();
36375 this.nextIndicator.show();
36377 if(this.indicator == this.files.length){
36378 this.nextIndicator.hide();
36381 this.thumbEl.scrollTo('top');
36383 this.fireEvent('update', this);
36386 onClick : function(e)
36388 e.preventDefault();
36390 this.fireEvent('click', this);
36395 e.preventDefault();
36397 this.indicator = Math.max(1, this.indicator - 1);
36404 e.preventDefault();
36406 this.indicator = Math.min(this.files.length, this.indicator + 1);
36420 * @class Roo.bootstrap.RadioSet
36421 * @extends Roo.bootstrap.Input
36422 * Bootstrap RadioSet class
36423 * @cfg {String} indicatorpos (left|right) default left
36424 * @cfg {Boolean} inline (true|false) inline the element (default true)
36425 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36427 * Create a new RadioSet
36428 * @param {Object} config The config object
36431 Roo.bootstrap.RadioSet = function(config){
36433 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36437 Roo.bootstrap.RadioSet.register(this);
36442 * Fires when the element is checked or unchecked.
36443 * @param {Roo.bootstrap.RadioSet} this This radio
36444 * @param {Roo.bootstrap.Radio} item The checked item
36449 * Fires when the element is click.
36450 * @param {Roo.bootstrap.RadioSet} this This radio set
36451 * @param {Roo.bootstrap.Radio} item The checked item
36452 * @param {Roo.EventObject} e The event object
36459 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36467 indicatorpos : 'left',
36469 getAutoCreate : function()
36473 cls : 'roo-radio-set-label',
36477 html : this.fieldLabel
36481 if (Roo.bootstrap.version == 3) {
36484 if(this.indicatorpos == 'left'){
36487 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36488 tooltip : 'This field is required'
36493 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36494 tooltip : 'This field is required'
36500 cls : 'roo-radio-set-items'
36503 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36505 if (align === 'left' && this.fieldLabel.length) {
36508 cls : "roo-radio-set-right",
36514 if(this.labelWidth > 12){
36515 label.style = "width: " + this.labelWidth + 'px';
36518 if(this.labelWidth < 13 && this.labelmd == 0){
36519 this.labelmd = this.labelWidth;
36522 if(this.labellg > 0){
36523 label.cls += ' col-lg-' + this.labellg;
36524 items.cls += ' col-lg-' + (12 - this.labellg);
36527 if(this.labelmd > 0){
36528 label.cls += ' col-md-' + this.labelmd;
36529 items.cls += ' col-md-' + (12 - this.labelmd);
36532 if(this.labelsm > 0){
36533 label.cls += ' col-sm-' + this.labelsm;
36534 items.cls += ' col-sm-' + (12 - this.labelsm);
36537 if(this.labelxs > 0){
36538 label.cls += ' col-xs-' + this.labelxs;
36539 items.cls += ' col-xs-' + (12 - this.labelxs);
36545 cls : 'roo-radio-set',
36549 cls : 'roo-radio-set-input',
36552 value : this.value ? this.value : ''
36559 if(this.weight.length){
36560 cfg.cls += ' roo-radio-' + this.weight;
36564 cfg.cls += ' roo-radio-set-inline';
36568 ['xs','sm','md','lg'].map(function(size){
36569 if (settings[size]) {
36570 cfg.cls += ' col-' + size + '-' + settings[size];
36578 initEvents : function()
36580 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36581 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36583 if(!this.fieldLabel.length){
36584 this.labelEl.hide();
36587 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36588 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36590 this.indicator = this.indicatorEl();
36592 if(this.indicator){
36593 this.indicator.addClass('invisible');
36596 this.originalValue = this.getValue();
36600 inputEl: function ()
36602 return this.el.select('.roo-radio-set-input', true).first();
36605 getChildContainer : function()
36607 return this.itemsEl;
36610 register : function(item)
36612 this.radioes.push(item);
36616 validate : function()
36618 if(this.getVisibilityEl().hasClass('hidden')){
36624 Roo.each(this.radioes, function(i){
36633 if(this.allowBlank) {
36637 if(this.disabled || valid){
36642 this.markInvalid();
36647 markValid : function()
36649 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36650 this.indicatorEl().removeClass('visible');
36651 this.indicatorEl().addClass('invisible');
36655 if (Roo.bootstrap.version == 3) {
36656 this.el.removeClass([this.invalidClass, this.validClass]);
36657 this.el.addClass(this.validClass);
36659 this.el.removeClass(['is-invalid','is-valid']);
36660 this.el.addClass(['is-valid']);
36662 this.fireEvent('valid', this);
36665 markInvalid : function(msg)
36667 if(this.allowBlank || this.disabled){
36671 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36672 this.indicatorEl().removeClass('invisible');
36673 this.indicatorEl().addClass('visible');
36675 if (Roo.bootstrap.version == 3) {
36676 this.el.removeClass([this.invalidClass, this.validClass]);
36677 this.el.addClass(this.invalidClass);
36679 this.el.removeClass(['is-invalid','is-valid']);
36680 this.el.addClass(['is-invalid']);
36683 this.fireEvent('invalid', this, msg);
36687 setValue : function(v, suppressEvent)
36689 if(this.value === v){
36696 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36699 Roo.each(this.radioes, function(i){
36701 i.el.removeClass('checked');
36704 Roo.each(this.radioes, function(i){
36706 if(i.value === v || i.value.toString() === v.toString()){
36708 i.el.addClass('checked');
36710 if(suppressEvent !== true){
36711 this.fireEvent('check', this, i);
36722 clearInvalid : function(){
36724 if(!this.el || this.preventMark){
36728 this.el.removeClass([this.invalidClass]);
36730 this.fireEvent('valid', this);
36735 Roo.apply(Roo.bootstrap.RadioSet, {
36739 register : function(set)
36741 this.groups[set.name] = set;
36744 get: function(name)
36746 if (typeof(this.groups[name]) == 'undefined') {
36750 return this.groups[name] ;
36756 * Ext JS Library 1.1.1
36757 * Copyright(c) 2006-2007, Ext JS, LLC.
36759 * Originally Released Under LGPL - original licence link has changed is not relivant.
36762 * <script type="text/javascript">
36767 * @class Roo.bootstrap.SplitBar
36768 * @extends Roo.util.Observable
36769 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36773 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36774 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36775 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36776 split.minSize = 100;
36777 split.maxSize = 600;
36778 split.animate = true;
36779 split.on('moved', splitterMoved);
36782 * Create a new SplitBar
36783 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36784 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36785 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36786 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36787 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36788 position of the SplitBar).
36790 Roo.bootstrap.SplitBar = function(cfg){
36795 // dragElement : elm
36796 // resizingElement: el,
36798 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36799 // placement : Roo.bootstrap.SplitBar.LEFT ,
36800 // existingProxy ???
36803 this.el = Roo.get(cfg.dragElement, true);
36804 this.el.dom.unselectable = "on";
36806 this.resizingEl = Roo.get(cfg.resizingElement, true);
36810 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36811 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36814 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36817 * The minimum size of the resizing element. (Defaults to 0)
36823 * The maximum size of the resizing element. (Defaults to 2000)
36826 this.maxSize = 2000;
36829 * Whether to animate the transition to the new size
36832 this.animate = false;
36835 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36838 this.useShim = false;
36843 if(!cfg.existingProxy){
36845 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36847 this.proxy = Roo.get(cfg.existingProxy).dom;
36850 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36853 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36856 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36859 this.dragSpecs = {};
36862 * @private The adapter to use to positon and resize elements
36864 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36865 this.adapter.init(this);
36867 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36869 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36870 this.el.addClass("roo-splitbar-h");
36873 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36874 this.el.addClass("roo-splitbar-v");
36880 * Fires when the splitter is moved (alias for {@link #event-moved})
36881 * @param {Roo.bootstrap.SplitBar} this
36882 * @param {Number} newSize the new width or height
36887 * Fires when the splitter is moved
36888 * @param {Roo.bootstrap.SplitBar} this
36889 * @param {Number} newSize the new width or height
36893 * @event beforeresize
36894 * Fires before the splitter is dragged
36895 * @param {Roo.bootstrap.SplitBar} this
36897 "beforeresize" : true,
36899 "beforeapply" : true
36902 Roo.util.Observable.call(this);
36905 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36906 onStartProxyDrag : function(x, y){
36907 this.fireEvent("beforeresize", this);
36909 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
36911 o.enableDisplayMode("block");
36912 // all splitbars share the same overlay
36913 Roo.bootstrap.SplitBar.prototype.overlay = o;
36915 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36916 this.overlay.show();
36917 Roo.get(this.proxy).setDisplayed("block");
36918 var size = this.adapter.getElementSize(this);
36919 this.activeMinSize = this.getMinimumSize();;
36920 this.activeMaxSize = this.getMaximumSize();;
36921 var c1 = size - this.activeMinSize;
36922 var c2 = Math.max(this.activeMaxSize - size, 0);
36923 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36924 this.dd.resetConstraints();
36925 this.dd.setXConstraint(
36926 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
36927 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36929 this.dd.setYConstraint(0, 0);
36931 this.dd.resetConstraints();
36932 this.dd.setXConstraint(0, 0);
36933 this.dd.setYConstraint(
36934 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
36935 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36938 this.dragSpecs.startSize = size;
36939 this.dragSpecs.startPoint = [x, y];
36940 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36944 * @private Called after the drag operation by the DDProxy
36946 onEndProxyDrag : function(e){
36947 Roo.get(this.proxy).setDisplayed(false);
36948 var endPoint = Roo.lib.Event.getXY(e);
36950 this.overlay.hide();
36953 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36954 newSize = this.dragSpecs.startSize +
36955 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36956 endPoint[0] - this.dragSpecs.startPoint[0] :
36957 this.dragSpecs.startPoint[0] - endPoint[0]
36960 newSize = this.dragSpecs.startSize +
36961 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36962 endPoint[1] - this.dragSpecs.startPoint[1] :
36963 this.dragSpecs.startPoint[1] - endPoint[1]
36966 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36967 if(newSize != this.dragSpecs.startSize){
36968 if(this.fireEvent('beforeapply', this, newSize) !== false){
36969 this.adapter.setElementSize(this, newSize);
36970 this.fireEvent("moved", this, newSize);
36971 this.fireEvent("resize", this, newSize);
36977 * Get the adapter this SplitBar uses
36978 * @return The adapter object
36980 getAdapter : function(){
36981 return this.adapter;
36985 * Set the adapter this SplitBar uses
36986 * @param {Object} adapter A SplitBar adapter object
36988 setAdapter : function(adapter){
36989 this.adapter = adapter;
36990 this.adapter.init(this);
36994 * Gets the minimum size for the resizing element
36995 * @return {Number} The minimum size
36997 getMinimumSize : function(){
36998 return this.minSize;
37002 * Sets the minimum size for the resizing element
37003 * @param {Number} minSize The minimum size
37005 setMinimumSize : function(minSize){
37006 this.minSize = minSize;
37010 * Gets the maximum size for the resizing element
37011 * @return {Number} The maximum size
37013 getMaximumSize : function(){
37014 return this.maxSize;
37018 * Sets the maximum size for the resizing element
37019 * @param {Number} maxSize The maximum size
37021 setMaximumSize : function(maxSize){
37022 this.maxSize = maxSize;
37026 * Sets the initialize size for the resizing element
37027 * @param {Number} size The initial size
37029 setCurrentSize : function(size){
37030 var oldAnimate = this.animate;
37031 this.animate = false;
37032 this.adapter.setElementSize(this, size);
37033 this.animate = oldAnimate;
37037 * Destroy this splitbar.
37038 * @param {Boolean} removeEl True to remove the element
37040 destroy : function(removeEl){
37042 this.shim.remove();
37045 this.proxy.parentNode.removeChild(this.proxy);
37053 * @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.
37055 Roo.bootstrap.SplitBar.createProxy = function(dir){
37056 var proxy = new Roo.Element(document.createElement("div"));
37057 proxy.unselectable();
37058 var cls = 'roo-splitbar-proxy';
37059 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37060 document.body.appendChild(proxy.dom);
37065 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37066 * Default Adapter. It assumes the splitter and resizing element are not positioned
37067 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37069 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37072 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37073 // do nothing for now
37074 init : function(s){
37078 * Called before drag operations to get the current size of the resizing element.
37079 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37081 getElementSize : function(s){
37082 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37083 return s.resizingEl.getWidth();
37085 return s.resizingEl.getHeight();
37090 * Called after drag operations to set the size of the resizing element.
37091 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37092 * @param {Number} newSize The new size to set
37093 * @param {Function} onComplete A function to be invoked when resizing is complete
37095 setElementSize : function(s, newSize, onComplete){
37096 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37098 s.resizingEl.setWidth(newSize);
37100 onComplete(s, newSize);
37103 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37108 s.resizingEl.setHeight(newSize);
37110 onComplete(s, newSize);
37113 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37120 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37121 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37122 * Adapter that moves the splitter element to align with the resized sizing element.
37123 * Used with an absolute positioned SplitBar.
37124 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37125 * document.body, make sure you assign an id to the body element.
37127 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37128 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37129 this.container = Roo.get(container);
37132 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37133 init : function(s){
37134 this.basic.init(s);
37137 getElementSize : function(s){
37138 return this.basic.getElementSize(s);
37141 setElementSize : function(s, newSize, onComplete){
37142 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37145 moveSplitter : function(s){
37146 var yes = Roo.bootstrap.SplitBar;
37147 switch(s.placement){
37149 s.el.setX(s.resizingEl.getRight());
37152 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37155 s.el.setY(s.resizingEl.getBottom());
37158 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37165 * Orientation constant - Create a vertical SplitBar
37169 Roo.bootstrap.SplitBar.VERTICAL = 1;
37172 * Orientation constant - Create a horizontal SplitBar
37176 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37179 * Placement constant - The resizing element is to the left of the splitter element
37183 Roo.bootstrap.SplitBar.LEFT = 1;
37186 * Placement constant - The resizing element is to the right of the splitter element
37190 Roo.bootstrap.SplitBar.RIGHT = 2;
37193 * Placement constant - The resizing element is positioned above the splitter element
37197 Roo.bootstrap.SplitBar.TOP = 3;
37200 * Placement constant - The resizing element is positioned under splitter element
37204 Roo.bootstrap.SplitBar.BOTTOM = 4;
37205 Roo.namespace("Roo.bootstrap.layout");/*
37207 * Ext JS Library 1.1.1
37208 * Copyright(c) 2006-2007, Ext JS, LLC.
37210 * Originally Released Under LGPL - original licence link has changed is not relivant.
37213 * <script type="text/javascript">
37217 * @class Roo.bootstrap.layout.Manager
37218 * @extends Roo.bootstrap.Component
37219 * Base class for layout managers.
37221 Roo.bootstrap.layout.Manager = function(config)
37223 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37229 /** false to disable window resize monitoring @type Boolean */
37230 this.monitorWindowResize = true;
37235 * Fires when a layout is performed.
37236 * @param {Roo.LayoutManager} this
37240 * @event regionresized
37241 * Fires when the user resizes a region.
37242 * @param {Roo.LayoutRegion} region The resized region
37243 * @param {Number} newSize The new size (width for east/west, height for north/south)
37245 "regionresized" : true,
37247 * @event regioncollapsed
37248 * Fires when a region is collapsed.
37249 * @param {Roo.LayoutRegion} region The collapsed region
37251 "regioncollapsed" : true,
37253 * @event regionexpanded
37254 * Fires when a region is expanded.
37255 * @param {Roo.LayoutRegion} region The expanded region
37257 "regionexpanded" : true
37259 this.updating = false;
37262 this.el = Roo.get(config.el);
37268 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37273 monitorWindowResize : true,
37279 onRender : function(ct, position)
37282 this.el = Roo.get(ct);
37285 //this.fireEvent('render',this);
37289 initEvents: function()
37293 // ie scrollbar fix
37294 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37295 document.body.scroll = "no";
37296 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37297 this.el.position('relative');
37299 this.id = this.el.id;
37300 this.el.addClass("roo-layout-container");
37301 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37302 if(this.el.dom != document.body ) {
37303 this.el.on('resize', this.layout,this);
37304 this.el.on('show', this.layout,this);
37310 * Returns true if this layout is currently being updated
37311 * @return {Boolean}
37313 isUpdating : function(){
37314 return this.updating;
37318 * Suspend the LayoutManager from doing auto-layouts while
37319 * making multiple add or remove calls
37321 beginUpdate : function(){
37322 this.updating = true;
37326 * Restore auto-layouts and optionally disable the manager from performing a layout
37327 * @param {Boolean} noLayout true to disable a layout update
37329 endUpdate : function(noLayout){
37330 this.updating = false;
37336 layout: function(){
37340 onRegionResized : function(region, newSize){
37341 this.fireEvent("regionresized", region, newSize);
37345 onRegionCollapsed : function(region){
37346 this.fireEvent("regioncollapsed", region);
37349 onRegionExpanded : function(region){
37350 this.fireEvent("regionexpanded", region);
37354 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37355 * performs box-model adjustments.
37356 * @return {Object} The size as an object {width: (the width), height: (the height)}
37358 getViewSize : function()
37361 if(this.el.dom != document.body){
37362 size = this.el.getSize();
37364 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37366 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37367 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37372 * Returns the Element this layout is bound to.
37373 * @return {Roo.Element}
37375 getEl : function(){
37380 * Returns the specified region.
37381 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37382 * @return {Roo.LayoutRegion}
37384 getRegion : function(target){
37385 return this.regions[target.toLowerCase()];
37388 onWindowResize : function(){
37389 if(this.monitorWindowResize){
37396 * Ext JS Library 1.1.1
37397 * Copyright(c) 2006-2007, Ext JS, LLC.
37399 * Originally Released Under LGPL - original licence link has changed is not relivant.
37402 * <script type="text/javascript">
37405 * @class Roo.bootstrap.layout.Border
37406 * @extends Roo.bootstrap.layout.Manager
37407 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37408 * please see: examples/bootstrap/nested.html<br><br>
37410 <b>The container the layout is rendered into can be either the body element or any other element.
37411 If it is not the body element, the container needs to either be an absolute positioned element,
37412 or you will need to add "position:relative" to the css of the container. You will also need to specify
37413 the container size if it is not the body element.</b>
37416 * Create a new Border
37417 * @param {Object} config Configuration options
37419 Roo.bootstrap.layout.Border = function(config){
37420 config = config || {};
37421 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37425 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37426 if(config[region]){
37427 config[region].region = region;
37428 this.addRegion(config[region]);
37434 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37436 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37438 parent : false, // this might point to a 'nest' or a ???
37441 * Creates and adds a new region if it doesn't already exist.
37442 * @param {String} target The target region key (north, south, east, west or center).
37443 * @param {Object} config The regions config object
37444 * @return {BorderLayoutRegion} The new region
37446 addRegion : function(config)
37448 if(!this.regions[config.region]){
37449 var r = this.factory(config);
37450 this.bindRegion(r);
37452 return this.regions[config.region];
37456 bindRegion : function(r){
37457 this.regions[r.config.region] = r;
37459 r.on("visibilitychange", this.layout, this);
37460 r.on("paneladded", this.layout, this);
37461 r.on("panelremoved", this.layout, this);
37462 r.on("invalidated", this.layout, this);
37463 r.on("resized", this.onRegionResized, this);
37464 r.on("collapsed", this.onRegionCollapsed, this);
37465 r.on("expanded", this.onRegionExpanded, this);
37469 * Performs a layout update.
37471 layout : function()
37473 if(this.updating) {
37477 // render all the rebions if they have not been done alreayd?
37478 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37479 if(this.regions[region] && !this.regions[region].bodyEl){
37480 this.regions[region].onRender(this.el)
37484 var size = this.getViewSize();
37485 var w = size.width;
37486 var h = size.height;
37491 //var x = 0, y = 0;
37493 var rs = this.regions;
37494 var north = rs["north"];
37495 var south = rs["south"];
37496 var west = rs["west"];
37497 var east = rs["east"];
37498 var center = rs["center"];
37499 //if(this.hideOnLayout){ // not supported anymore
37500 //c.el.setStyle("display", "none");
37502 if(north && north.isVisible()){
37503 var b = north.getBox();
37504 var m = north.getMargins();
37505 b.width = w - (m.left+m.right);
37508 centerY = b.height + b.y + m.bottom;
37509 centerH -= centerY;
37510 north.updateBox(this.safeBox(b));
37512 if(south && south.isVisible()){
37513 var b = south.getBox();
37514 var m = south.getMargins();
37515 b.width = w - (m.left+m.right);
37517 var totalHeight = (b.height + m.top + m.bottom);
37518 b.y = h - totalHeight + m.top;
37519 centerH -= totalHeight;
37520 south.updateBox(this.safeBox(b));
37522 if(west && west.isVisible()){
37523 var b = west.getBox();
37524 var m = west.getMargins();
37525 b.height = centerH - (m.top+m.bottom);
37527 b.y = centerY + m.top;
37528 var totalWidth = (b.width + m.left + m.right);
37529 centerX += totalWidth;
37530 centerW -= totalWidth;
37531 west.updateBox(this.safeBox(b));
37533 if(east && east.isVisible()){
37534 var b = east.getBox();
37535 var m = east.getMargins();
37536 b.height = centerH - (m.top+m.bottom);
37537 var totalWidth = (b.width + m.left + m.right);
37538 b.x = w - totalWidth + m.left;
37539 b.y = centerY + m.top;
37540 centerW -= totalWidth;
37541 east.updateBox(this.safeBox(b));
37544 var m = center.getMargins();
37546 x: centerX + m.left,
37547 y: centerY + m.top,
37548 width: centerW - (m.left+m.right),
37549 height: centerH - (m.top+m.bottom)
37551 //if(this.hideOnLayout){
37552 //center.el.setStyle("display", "block");
37554 center.updateBox(this.safeBox(centerBox));
37557 this.fireEvent("layout", this);
37561 safeBox : function(box){
37562 box.width = Math.max(0, box.width);
37563 box.height = Math.max(0, box.height);
37568 * Adds a ContentPanel (or subclass) to this layout.
37569 * @param {String} target The target region key (north, south, east, west or center).
37570 * @param {Roo.ContentPanel} panel The panel to add
37571 * @return {Roo.ContentPanel} The added panel
37573 add : function(target, panel){
37575 target = target.toLowerCase();
37576 return this.regions[target].add(panel);
37580 * Remove a ContentPanel (or subclass) to this layout.
37581 * @param {String} target The target region key (north, south, east, west or center).
37582 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37583 * @return {Roo.ContentPanel} The removed panel
37585 remove : function(target, panel){
37586 target = target.toLowerCase();
37587 return this.regions[target].remove(panel);
37591 * Searches all regions for a panel with the specified id
37592 * @param {String} panelId
37593 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37595 findPanel : function(panelId){
37596 var rs = this.regions;
37597 for(var target in rs){
37598 if(typeof rs[target] != "function"){
37599 var p = rs[target].getPanel(panelId);
37609 * Searches all regions for a panel with the specified id and activates (shows) it.
37610 * @param {String/ContentPanel} panelId The panels id or the panel itself
37611 * @return {Roo.ContentPanel} The shown panel or null
37613 showPanel : function(panelId) {
37614 var rs = this.regions;
37615 for(var target in rs){
37616 var r = rs[target];
37617 if(typeof r != "function"){
37618 if(r.hasPanel(panelId)){
37619 return r.showPanel(panelId);
37627 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37628 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37631 restoreState : function(provider){
37633 provider = Roo.state.Manager;
37635 var sm = new Roo.LayoutStateManager();
37636 sm.init(this, provider);
37642 * Adds a xtype elements to the layout.
37646 xtype : 'ContentPanel',
37653 xtype : 'NestedLayoutPanel',
37659 items : [ ... list of content panels or nested layout panels.. ]
37663 * @param {Object} cfg Xtype definition of item to add.
37665 addxtype : function(cfg)
37667 // basically accepts a pannel...
37668 // can accept a layout region..!?!?
37669 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37672 // theory? children can only be panels??
37674 //if (!cfg.xtype.match(/Panel$/)) {
37679 if (typeof(cfg.region) == 'undefined') {
37680 Roo.log("Failed to add Panel, region was not set");
37684 var region = cfg.region;
37690 xitems = cfg.items;
37695 if ( region == 'center') {
37696 Roo.log("Center: " + cfg.title);
37702 case 'Content': // ContentPanel (el, cfg)
37703 case 'Scroll': // ContentPanel (el, cfg)
37705 cfg.autoCreate = cfg.autoCreate || true;
37706 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37708 // var el = this.el.createChild();
37709 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37712 this.add(region, ret);
37716 case 'TreePanel': // our new panel!
37717 cfg.el = this.el.createChild();
37718 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37719 this.add(region, ret);
37724 // create a new Layout (which is a Border Layout...
37726 var clayout = cfg.layout;
37727 clayout.el = this.el.createChild();
37728 clayout.items = clayout.items || [];
37732 // replace this exitems with the clayout ones..
37733 xitems = clayout.items;
37735 // force background off if it's in center...
37736 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37737 cfg.background = false;
37739 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37742 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37743 //console.log('adding nested layout panel ' + cfg.toSource());
37744 this.add(region, ret);
37745 nb = {}; /// find first...
37750 // needs grid and region
37752 //var el = this.getRegion(region).el.createChild();
37754 *var el = this.el.createChild();
37755 // create the grid first...
37756 cfg.grid.container = el;
37757 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37760 if (region == 'center' && this.active ) {
37761 cfg.background = false;
37764 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37766 this.add(region, ret);
37768 if (cfg.background) {
37769 // render grid on panel activation (if panel background)
37770 ret.on('activate', function(gp) {
37771 if (!gp.grid.rendered) {
37772 // gp.grid.render(el);
37776 // cfg.grid.render(el);
37782 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37783 // it was the old xcomponent building that caused this before.
37784 // espeically if border is the top element in the tree.
37794 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37796 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37797 this.add(region, ret);
37801 throw "Can not add '" + cfg.xtype + "' to Border";
37807 this.beginUpdate();
37811 Roo.each(xitems, function(i) {
37812 region = nb && i.region ? i.region : false;
37814 var add = ret.addxtype(i);
37817 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37818 if (!i.background) {
37819 abn[region] = nb[region] ;
37826 // make the last non-background panel active..
37827 //if (nb) { Roo.log(abn); }
37830 for(var r in abn) {
37831 region = this.getRegion(r);
37833 // tried using nb[r], but it does not work..
37835 region.showPanel(abn[r]);
37846 factory : function(cfg)
37849 var validRegions = Roo.bootstrap.layout.Border.regions;
37851 var target = cfg.region;
37854 var r = Roo.bootstrap.layout;
37858 return new r.North(cfg);
37860 return new r.South(cfg);
37862 return new r.East(cfg);
37864 return new r.West(cfg);
37866 return new r.Center(cfg);
37868 throw 'Layout region "'+target+'" not supported.';
37875 * Ext JS Library 1.1.1
37876 * Copyright(c) 2006-2007, Ext JS, LLC.
37878 * Originally Released Under LGPL - original licence link has changed is not relivant.
37881 * <script type="text/javascript">
37885 * @class Roo.bootstrap.layout.Basic
37886 * @extends Roo.util.Observable
37887 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37888 * and does not have a titlebar, tabs or any other features. All it does is size and position
37889 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37890 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37891 * @cfg {string} region the region that it inhabits..
37892 * @cfg {bool} skipConfig skip config?
37896 Roo.bootstrap.layout.Basic = function(config){
37898 this.mgr = config.mgr;
37900 this.position = config.region;
37902 var skipConfig = config.skipConfig;
37906 * @scope Roo.BasicLayoutRegion
37910 * @event beforeremove
37911 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37912 * @param {Roo.LayoutRegion} this
37913 * @param {Roo.ContentPanel} panel The panel
37914 * @param {Object} e The cancel event object
37916 "beforeremove" : true,
37918 * @event invalidated
37919 * Fires when the layout for this region is changed.
37920 * @param {Roo.LayoutRegion} this
37922 "invalidated" : true,
37924 * @event visibilitychange
37925 * Fires when this region is shown or hidden
37926 * @param {Roo.LayoutRegion} this
37927 * @param {Boolean} visibility true or false
37929 "visibilitychange" : true,
37931 * @event paneladded
37932 * Fires when a panel is added.
37933 * @param {Roo.LayoutRegion} this
37934 * @param {Roo.ContentPanel} panel The panel
37936 "paneladded" : true,
37938 * @event panelremoved
37939 * Fires when a panel is removed.
37940 * @param {Roo.LayoutRegion} this
37941 * @param {Roo.ContentPanel} panel The panel
37943 "panelremoved" : true,
37945 * @event beforecollapse
37946 * Fires when this region before collapse.
37947 * @param {Roo.LayoutRegion} this
37949 "beforecollapse" : true,
37952 * Fires when this region is collapsed.
37953 * @param {Roo.LayoutRegion} this
37955 "collapsed" : true,
37958 * Fires when this region is expanded.
37959 * @param {Roo.LayoutRegion} this
37964 * Fires when this region is slid into view.
37965 * @param {Roo.LayoutRegion} this
37967 "slideshow" : true,
37970 * Fires when this region slides out of view.
37971 * @param {Roo.LayoutRegion} this
37973 "slidehide" : true,
37975 * @event panelactivated
37976 * Fires when a panel is activated.
37977 * @param {Roo.LayoutRegion} this
37978 * @param {Roo.ContentPanel} panel The activated panel
37980 "panelactivated" : true,
37983 * Fires when the user resizes this region.
37984 * @param {Roo.LayoutRegion} this
37985 * @param {Number} newSize The new size (width for east/west, height for north/south)
37989 /** A collection of panels in this region. @type Roo.util.MixedCollection */
37990 this.panels = new Roo.util.MixedCollection();
37991 this.panels.getKey = this.getPanelId.createDelegate(this);
37993 this.activePanel = null;
37994 // ensure listeners are added...
37996 if (config.listeners || config.events) {
37997 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37998 listeners : config.listeners || {},
37999 events : config.events || {}
38003 if(skipConfig !== true){
38004 this.applyConfig(config);
38008 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38010 getPanelId : function(p){
38014 applyConfig : function(config){
38015 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38016 this.config = config;
38021 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38022 * the width, for horizontal (north, south) the height.
38023 * @param {Number} newSize The new width or height
38025 resizeTo : function(newSize){
38026 var el = this.el ? this.el :
38027 (this.activePanel ? this.activePanel.getEl() : null);
38029 switch(this.position){
38032 el.setWidth(newSize);
38033 this.fireEvent("resized", this, newSize);
38037 el.setHeight(newSize);
38038 this.fireEvent("resized", this, newSize);
38044 getBox : function(){
38045 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38048 getMargins : function(){
38049 return this.margins;
38052 updateBox : function(box){
38054 var el = this.activePanel.getEl();
38055 el.dom.style.left = box.x + "px";
38056 el.dom.style.top = box.y + "px";
38057 this.activePanel.setSize(box.width, box.height);
38061 * Returns the container element for this region.
38062 * @return {Roo.Element}
38064 getEl : function(){
38065 return this.activePanel;
38069 * Returns true if this region is currently visible.
38070 * @return {Boolean}
38072 isVisible : function(){
38073 return this.activePanel ? true : false;
38076 setActivePanel : function(panel){
38077 panel = this.getPanel(panel);
38078 if(this.activePanel && this.activePanel != panel){
38079 this.activePanel.setActiveState(false);
38080 this.activePanel.getEl().setLeftTop(-10000,-10000);
38082 this.activePanel = panel;
38083 panel.setActiveState(true);
38085 panel.setSize(this.box.width, this.box.height);
38087 this.fireEvent("panelactivated", this, panel);
38088 this.fireEvent("invalidated");
38092 * Show the specified panel.
38093 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38094 * @return {Roo.ContentPanel} The shown panel or null
38096 showPanel : function(panel){
38097 panel = this.getPanel(panel);
38099 this.setActivePanel(panel);
38105 * Get the active panel for this region.
38106 * @return {Roo.ContentPanel} The active panel or null
38108 getActivePanel : function(){
38109 return this.activePanel;
38113 * Add the passed ContentPanel(s)
38114 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38115 * @return {Roo.ContentPanel} The panel added (if only one was added)
38117 add : function(panel){
38118 if(arguments.length > 1){
38119 for(var i = 0, len = arguments.length; i < len; i++) {
38120 this.add(arguments[i]);
38124 if(this.hasPanel(panel)){
38125 this.showPanel(panel);
38128 var el = panel.getEl();
38129 if(el.dom.parentNode != this.mgr.el.dom){
38130 this.mgr.el.dom.appendChild(el.dom);
38132 if(panel.setRegion){
38133 panel.setRegion(this);
38135 this.panels.add(panel);
38136 el.setStyle("position", "absolute");
38137 if(!panel.background){
38138 this.setActivePanel(panel);
38139 if(this.config.initialSize && this.panels.getCount()==1){
38140 this.resizeTo(this.config.initialSize);
38143 this.fireEvent("paneladded", this, panel);
38148 * Returns true if the panel is in this region.
38149 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38150 * @return {Boolean}
38152 hasPanel : function(panel){
38153 if(typeof panel == "object"){ // must be panel obj
38154 panel = panel.getId();
38156 return this.getPanel(panel) ? true : false;
38160 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38161 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38162 * @param {Boolean} preservePanel Overrides the config preservePanel option
38163 * @return {Roo.ContentPanel} The panel that was removed
38165 remove : function(panel, preservePanel){
38166 panel = this.getPanel(panel);
38171 this.fireEvent("beforeremove", this, panel, e);
38172 if(e.cancel === true){
38175 var panelId = panel.getId();
38176 this.panels.removeKey(panelId);
38181 * Returns the panel specified or null if it's not in this region.
38182 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38183 * @return {Roo.ContentPanel}
38185 getPanel : function(id){
38186 if(typeof id == "object"){ // must be panel obj
38189 return this.panels.get(id);
38193 * Returns this regions position (north/south/east/west/center).
38196 getPosition: function(){
38197 return this.position;
38201 * Ext JS Library 1.1.1
38202 * Copyright(c) 2006-2007, Ext JS, LLC.
38204 * Originally Released Under LGPL - original licence link has changed is not relivant.
38207 * <script type="text/javascript">
38211 * @class Roo.bootstrap.layout.Region
38212 * @extends Roo.bootstrap.layout.Basic
38213 * This class represents a region in a layout manager.
38215 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38216 * @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})
38217 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38218 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38219 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38220 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38221 * @cfg {String} title The title for the region (overrides panel titles)
38222 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38223 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38224 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38225 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38226 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38227 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38228 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38229 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38230 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38231 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38233 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38234 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38235 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38236 * @cfg {Number} width For East/West panels
38237 * @cfg {Number} height For North/South panels
38238 * @cfg {Boolean} split To show the splitter
38239 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38241 * @cfg {string} cls Extra CSS classes to add to region
38243 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38244 * @cfg {string} region the region that it inhabits..
38247 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38248 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38250 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38251 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38252 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38254 Roo.bootstrap.layout.Region = function(config)
38256 this.applyConfig(config);
38258 var mgr = config.mgr;
38259 var pos = config.region;
38260 config.skipConfig = true;
38261 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38264 this.onRender(mgr.el);
38267 this.visible = true;
38268 this.collapsed = false;
38269 this.unrendered_panels = [];
38272 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38274 position: '', // set by wrapper (eg. north/south etc..)
38275 unrendered_panels : null, // unrendered panels.
38277 tabPosition : false,
38279 mgr: false, // points to 'Border'
38282 createBody : function(){
38283 /** This region's body element
38284 * @type Roo.Element */
38285 this.bodyEl = this.el.createChild({
38287 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38291 onRender: function(ctr, pos)
38293 var dh = Roo.DomHelper;
38294 /** This region's container element
38295 * @type Roo.Element */
38296 this.el = dh.append(ctr.dom, {
38298 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38300 /** This region's title element
38301 * @type Roo.Element */
38303 this.titleEl = dh.append(this.el.dom, {
38305 unselectable: "on",
38306 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38308 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38309 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38313 this.titleEl.enableDisplayMode();
38314 /** This region's title text element
38315 * @type HTMLElement */
38316 this.titleTextEl = this.titleEl.dom.firstChild;
38317 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38319 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38320 this.closeBtn.enableDisplayMode();
38321 this.closeBtn.on("click", this.closeClicked, this);
38322 this.closeBtn.hide();
38324 this.createBody(this.config);
38325 if(this.config.hideWhenEmpty){
38327 this.on("paneladded", this.validateVisibility, this);
38328 this.on("panelremoved", this.validateVisibility, this);
38330 if(this.autoScroll){
38331 this.bodyEl.setStyle("overflow", "auto");
38333 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38335 //if(c.titlebar !== false){
38336 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38337 this.titleEl.hide();
38339 this.titleEl.show();
38340 if(this.config.title){
38341 this.titleTextEl.innerHTML = this.config.title;
38345 if(this.config.collapsed){
38346 this.collapse(true);
38348 if(this.config.hidden){
38352 if (this.unrendered_panels && this.unrendered_panels.length) {
38353 for (var i =0;i< this.unrendered_panels.length; i++) {
38354 this.add(this.unrendered_panels[i]);
38356 this.unrendered_panels = null;
38362 applyConfig : function(c)
38365 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38366 var dh = Roo.DomHelper;
38367 if(c.titlebar !== false){
38368 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38369 this.collapseBtn.on("click", this.collapse, this);
38370 this.collapseBtn.enableDisplayMode();
38372 if(c.showPin === true || this.showPin){
38373 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38374 this.stickBtn.enableDisplayMode();
38375 this.stickBtn.on("click", this.expand, this);
38376 this.stickBtn.hide();
38381 /** This region's collapsed element
38382 * @type Roo.Element */
38385 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38386 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38389 if(c.floatable !== false){
38390 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38391 this.collapsedEl.on("click", this.collapseClick, this);
38394 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38395 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38396 id: "message", unselectable: "on", style:{"float":"left"}});
38397 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38399 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38400 this.expandBtn.on("click", this.expand, this);
38404 if(this.collapseBtn){
38405 this.collapseBtn.setVisible(c.collapsible == true);
38408 this.cmargins = c.cmargins || this.cmargins ||
38409 (this.position == "west" || this.position == "east" ?
38410 {top: 0, left: 2, right:2, bottom: 0} :
38411 {top: 2, left: 0, right:0, bottom: 2});
38413 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38416 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38418 this.autoScroll = c.autoScroll || false;
38423 this.duration = c.duration || .30;
38424 this.slideDuration = c.slideDuration || .45;
38429 * Returns true if this region is currently visible.
38430 * @return {Boolean}
38432 isVisible : function(){
38433 return this.visible;
38437 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38438 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38440 //setCollapsedTitle : function(title){
38441 // title = title || " ";
38442 // if(this.collapsedTitleTextEl){
38443 // this.collapsedTitleTextEl.innerHTML = title;
38447 getBox : function(){
38449 // if(!this.collapsed){
38450 b = this.el.getBox(false, true);
38452 // b = this.collapsedEl.getBox(false, true);
38457 getMargins : function(){
38458 return this.margins;
38459 //return this.collapsed ? this.cmargins : this.margins;
38462 highlight : function(){
38463 this.el.addClass("x-layout-panel-dragover");
38466 unhighlight : function(){
38467 this.el.removeClass("x-layout-panel-dragover");
38470 updateBox : function(box)
38472 if (!this.bodyEl) {
38473 return; // not rendered yet..
38477 if(!this.collapsed){
38478 this.el.dom.style.left = box.x + "px";
38479 this.el.dom.style.top = box.y + "px";
38480 this.updateBody(box.width, box.height);
38482 this.collapsedEl.dom.style.left = box.x + "px";
38483 this.collapsedEl.dom.style.top = box.y + "px";
38484 this.collapsedEl.setSize(box.width, box.height);
38487 this.tabs.autoSizeTabs();
38491 updateBody : function(w, h)
38494 this.el.setWidth(w);
38495 w -= this.el.getBorderWidth("rl");
38496 if(this.config.adjustments){
38497 w += this.config.adjustments[0];
38500 if(h !== null && h > 0){
38501 this.el.setHeight(h);
38502 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38503 h -= this.el.getBorderWidth("tb");
38504 if(this.config.adjustments){
38505 h += this.config.adjustments[1];
38507 this.bodyEl.setHeight(h);
38509 h = this.tabs.syncHeight(h);
38512 if(this.panelSize){
38513 w = w !== null ? w : this.panelSize.width;
38514 h = h !== null ? h : this.panelSize.height;
38516 if(this.activePanel){
38517 var el = this.activePanel.getEl();
38518 w = w !== null ? w : el.getWidth();
38519 h = h !== null ? h : el.getHeight();
38520 this.panelSize = {width: w, height: h};
38521 this.activePanel.setSize(w, h);
38523 if(Roo.isIE && this.tabs){
38524 this.tabs.el.repaint();
38529 * Returns the container element for this region.
38530 * @return {Roo.Element}
38532 getEl : function(){
38537 * Hides this region.
38540 //if(!this.collapsed){
38541 this.el.dom.style.left = "-2000px";
38544 // this.collapsedEl.dom.style.left = "-2000px";
38545 // this.collapsedEl.hide();
38547 this.visible = false;
38548 this.fireEvent("visibilitychange", this, false);
38552 * Shows this region if it was previously hidden.
38555 //if(!this.collapsed){
38558 // this.collapsedEl.show();
38560 this.visible = true;
38561 this.fireEvent("visibilitychange", this, true);
38564 closeClicked : function(){
38565 if(this.activePanel){
38566 this.remove(this.activePanel);
38570 collapseClick : function(e){
38572 e.stopPropagation();
38575 e.stopPropagation();
38581 * Collapses this region.
38582 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38585 collapse : function(skipAnim, skipCheck = false){
38586 if(this.collapsed) {
38590 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38592 this.collapsed = true;
38594 this.split.el.hide();
38596 if(this.config.animate && skipAnim !== true){
38597 this.fireEvent("invalidated", this);
38598 this.animateCollapse();
38600 this.el.setLocation(-20000,-20000);
38602 this.collapsedEl.show();
38603 this.fireEvent("collapsed", this);
38604 this.fireEvent("invalidated", this);
38610 animateCollapse : function(){
38615 * Expands this region if it was previously collapsed.
38616 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38617 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38620 expand : function(e, skipAnim){
38622 e.stopPropagation();
38624 if(!this.collapsed || this.el.hasActiveFx()) {
38628 this.afterSlideIn();
38631 this.collapsed = false;
38632 if(this.config.animate && skipAnim !== true){
38633 this.animateExpand();
38637 this.split.el.show();
38639 this.collapsedEl.setLocation(-2000,-2000);
38640 this.collapsedEl.hide();
38641 this.fireEvent("invalidated", this);
38642 this.fireEvent("expanded", this);
38646 animateExpand : function(){
38650 initTabs : function()
38652 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38654 var ts = new Roo.bootstrap.panel.Tabs({
38655 el: this.bodyEl.dom,
38657 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38658 disableTooltips: this.config.disableTabTips,
38659 toolbar : this.config.toolbar
38662 if(this.config.hideTabs){
38663 ts.stripWrap.setDisplayed(false);
38666 ts.resizeTabs = this.config.resizeTabs === true;
38667 ts.minTabWidth = this.config.minTabWidth || 40;
38668 ts.maxTabWidth = this.config.maxTabWidth || 250;
38669 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38670 ts.monitorResize = false;
38671 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38672 ts.bodyEl.addClass('roo-layout-tabs-body');
38673 this.panels.each(this.initPanelAsTab, this);
38676 initPanelAsTab : function(panel){
38677 var ti = this.tabs.addTab(
38681 this.config.closeOnTab && panel.isClosable(),
38684 if(panel.tabTip !== undefined){
38685 ti.setTooltip(panel.tabTip);
38687 ti.on("activate", function(){
38688 this.setActivePanel(panel);
38691 if(this.config.closeOnTab){
38692 ti.on("beforeclose", function(t, e){
38694 this.remove(panel);
38698 panel.tabItem = ti;
38703 updatePanelTitle : function(panel, title)
38705 if(this.activePanel == panel){
38706 this.updateTitle(title);
38709 var ti = this.tabs.getTab(panel.getEl().id);
38711 if(panel.tabTip !== undefined){
38712 ti.setTooltip(panel.tabTip);
38717 updateTitle : function(title){
38718 if(this.titleTextEl && !this.config.title){
38719 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38723 setActivePanel : function(panel)
38725 panel = this.getPanel(panel);
38726 if(this.activePanel && this.activePanel != panel){
38727 if(this.activePanel.setActiveState(false) === false){
38731 this.activePanel = panel;
38732 panel.setActiveState(true);
38733 if(this.panelSize){
38734 panel.setSize(this.panelSize.width, this.panelSize.height);
38737 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38739 this.updateTitle(panel.getTitle());
38741 this.fireEvent("invalidated", this);
38743 this.fireEvent("panelactivated", this, panel);
38747 * Shows the specified panel.
38748 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38749 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38751 showPanel : function(panel)
38753 panel = this.getPanel(panel);
38756 var tab = this.tabs.getTab(panel.getEl().id);
38757 if(tab.isHidden()){
38758 this.tabs.unhideTab(tab.id);
38762 this.setActivePanel(panel);
38769 * Get the active panel for this region.
38770 * @return {Roo.ContentPanel} The active panel or null
38772 getActivePanel : function(){
38773 return this.activePanel;
38776 validateVisibility : function(){
38777 if(this.panels.getCount() < 1){
38778 this.updateTitle(" ");
38779 this.closeBtn.hide();
38782 if(!this.isVisible()){
38789 * Adds the passed ContentPanel(s) to this region.
38790 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38791 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38793 add : function(panel)
38795 if(arguments.length > 1){
38796 for(var i = 0, len = arguments.length; i < len; i++) {
38797 this.add(arguments[i]);
38802 // if we have not been rendered yet, then we can not really do much of this..
38803 if (!this.bodyEl) {
38804 this.unrendered_panels.push(panel);
38811 if(this.hasPanel(panel)){
38812 this.showPanel(panel);
38815 panel.setRegion(this);
38816 this.panels.add(panel);
38817 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38818 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38819 // and hide them... ???
38820 this.bodyEl.dom.appendChild(panel.getEl().dom);
38821 if(panel.background !== true){
38822 this.setActivePanel(panel);
38824 this.fireEvent("paneladded", this, panel);
38831 this.initPanelAsTab(panel);
38835 if(panel.background !== true){
38836 this.tabs.activate(panel.getEl().id);
38838 this.fireEvent("paneladded", this, panel);
38843 * Hides the tab for the specified panel.
38844 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38846 hidePanel : function(panel){
38847 if(this.tabs && (panel = this.getPanel(panel))){
38848 this.tabs.hideTab(panel.getEl().id);
38853 * Unhides the tab for a previously hidden panel.
38854 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38856 unhidePanel : function(panel){
38857 if(this.tabs && (panel = this.getPanel(panel))){
38858 this.tabs.unhideTab(panel.getEl().id);
38862 clearPanels : function(){
38863 while(this.panels.getCount() > 0){
38864 this.remove(this.panels.first());
38869 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38870 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38871 * @param {Boolean} preservePanel Overrides the config preservePanel option
38872 * @return {Roo.ContentPanel} The panel that was removed
38874 remove : function(panel, preservePanel)
38876 panel = this.getPanel(panel);
38881 this.fireEvent("beforeremove", this, panel, e);
38882 if(e.cancel === true){
38885 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38886 var panelId = panel.getId();
38887 this.panels.removeKey(panelId);
38889 document.body.appendChild(panel.getEl().dom);
38892 this.tabs.removeTab(panel.getEl().id);
38893 }else if (!preservePanel){
38894 this.bodyEl.dom.removeChild(panel.getEl().dom);
38896 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38897 var p = this.panels.first();
38898 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38899 tempEl.appendChild(p.getEl().dom);
38900 this.bodyEl.update("");
38901 this.bodyEl.dom.appendChild(p.getEl().dom);
38903 this.updateTitle(p.getTitle());
38905 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38906 this.setActivePanel(p);
38908 panel.setRegion(null);
38909 if(this.activePanel == panel){
38910 this.activePanel = null;
38912 if(this.config.autoDestroy !== false && preservePanel !== true){
38913 try{panel.destroy();}catch(e){}
38915 this.fireEvent("panelremoved", this, panel);
38920 * Returns the TabPanel component used by this region
38921 * @return {Roo.TabPanel}
38923 getTabs : function(){
38927 createTool : function(parentEl, className){
38928 var btn = Roo.DomHelper.append(parentEl, {
38930 cls: "x-layout-tools-button",
38933 cls: "roo-layout-tools-button-inner " + className,
38937 btn.addClassOnOver("roo-layout-tools-button-over");
38942 * Ext JS Library 1.1.1
38943 * Copyright(c) 2006-2007, Ext JS, LLC.
38945 * Originally Released Under LGPL - original licence link has changed is not relivant.
38948 * <script type="text/javascript">
38954 * @class Roo.SplitLayoutRegion
38955 * @extends Roo.LayoutRegion
38956 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38958 Roo.bootstrap.layout.Split = function(config){
38959 this.cursor = config.cursor;
38960 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38963 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38965 splitTip : "Drag to resize.",
38966 collapsibleSplitTip : "Drag to resize. Double click to hide.",
38967 useSplitTips : false,
38969 applyConfig : function(config){
38970 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38973 onRender : function(ctr,pos) {
38975 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38976 if(!this.config.split){
38981 var splitEl = Roo.DomHelper.append(ctr.dom, {
38983 id: this.el.id + "-split",
38984 cls: "roo-layout-split roo-layout-split-"+this.position,
38987 /** The SplitBar for this region
38988 * @type Roo.SplitBar */
38989 // does not exist yet...
38990 Roo.log([this.position, this.orientation]);
38992 this.split = new Roo.bootstrap.SplitBar({
38993 dragElement : splitEl,
38994 resizingElement: this.el,
38995 orientation : this.orientation
38998 this.split.on("moved", this.onSplitMove, this);
38999 this.split.useShim = this.config.useShim === true;
39000 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39001 if(this.useSplitTips){
39002 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39004 //if(config.collapsible){
39005 // this.split.el.on("dblclick", this.collapse, this);
39008 if(typeof this.config.minSize != "undefined"){
39009 this.split.minSize = this.config.minSize;
39011 if(typeof this.config.maxSize != "undefined"){
39012 this.split.maxSize = this.config.maxSize;
39014 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39015 this.hideSplitter();
39020 getHMaxSize : function(){
39021 var cmax = this.config.maxSize || 10000;
39022 var center = this.mgr.getRegion("center");
39023 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39026 getVMaxSize : function(){
39027 var cmax = this.config.maxSize || 10000;
39028 var center = this.mgr.getRegion("center");
39029 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39032 onSplitMove : function(split, newSize){
39033 this.fireEvent("resized", this, newSize);
39037 * Returns the {@link Roo.SplitBar} for this region.
39038 * @return {Roo.SplitBar}
39040 getSplitBar : function(){
39045 this.hideSplitter();
39046 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39049 hideSplitter : function(){
39051 this.split.el.setLocation(-2000,-2000);
39052 this.split.el.hide();
39058 this.split.el.show();
39060 Roo.bootstrap.layout.Split.superclass.show.call(this);
39063 beforeSlide: function(){
39064 if(Roo.isGecko){// firefox overflow auto bug workaround
39065 this.bodyEl.clip();
39067 this.tabs.bodyEl.clip();
39069 if(this.activePanel){
39070 this.activePanel.getEl().clip();
39072 if(this.activePanel.beforeSlide){
39073 this.activePanel.beforeSlide();
39079 afterSlide : function(){
39080 if(Roo.isGecko){// firefox overflow auto bug workaround
39081 this.bodyEl.unclip();
39083 this.tabs.bodyEl.unclip();
39085 if(this.activePanel){
39086 this.activePanel.getEl().unclip();
39087 if(this.activePanel.afterSlide){
39088 this.activePanel.afterSlide();
39094 initAutoHide : function(){
39095 if(this.autoHide !== false){
39096 if(!this.autoHideHd){
39097 var st = new Roo.util.DelayedTask(this.slideIn, this);
39098 this.autoHideHd = {
39099 "mouseout": function(e){
39100 if(!e.within(this.el, true)){
39104 "mouseover" : function(e){
39110 this.el.on(this.autoHideHd);
39114 clearAutoHide : function(){
39115 if(this.autoHide !== false){
39116 this.el.un("mouseout", this.autoHideHd.mouseout);
39117 this.el.un("mouseover", this.autoHideHd.mouseover);
39121 clearMonitor : function(){
39122 Roo.get(document).un("click", this.slideInIf, this);
39125 // these names are backwards but not changed for compat
39126 slideOut : function(){
39127 if(this.isSlid || this.el.hasActiveFx()){
39130 this.isSlid = true;
39131 if(this.collapseBtn){
39132 this.collapseBtn.hide();
39134 this.closeBtnState = this.closeBtn.getStyle('display');
39135 this.closeBtn.hide();
39137 this.stickBtn.show();
39140 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39141 this.beforeSlide();
39142 this.el.setStyle("z-index", 10001);
39143 this.el.slideIn(this.getSlideAnchor(), {
39144 callback: function(){
39146 this.initAutoHide();
39147 Roo.get(document).on("click", this.slideInIf, this);
39148 this.fireEvent("slideshow", this);
39155 afterSlideIn : function(){
39156 this.clearAutoHide();
39157 this.isSlid = false;
39158 this.clearMonitor();
39159 this.el.setStyle("z-index", "");
39160 if(this.collapseBtn){
39161 this.collapseBtn.show();
39163 this.closeBtn.setStyle('display', this.closeBtnState);
39165 this.stickBtn.hide();
39167 this.fireEvent("slidehide", this);
39170 slideIn : function(cb){
39171 if(!this.isSlid || this.el.hasActiveFx()){
39175 this.isSlid = false;
39176 this.beforeSlide();
39177 this.el.slideOut(this.getSlideAnchor(), {
39178 callback: function(){
39179 this.el.setLeftTop(-10000, -10000);
39181 this.afterSlideIn();
39189 slideInIf : function(e){
39190 if(!e.within(this.el)){
39195 animateCollapse : function(){
39196 this.beforeSlide();
39197 this.el.setStyle("z-index", 20000);
39198 var anchor = this.getSlideAnchor();
39199 this.el.slideOut(anchor, {
39200 callback : function(){
39201 this.el.setStyle("z-index", "");
39202 this.collapsedEl.slideIn(anchor, {duration:.3});
39204 this.el.setLocation(-10000,-10000);
39206 this.fireEvent("collapsed", this);
39213 animateExpand : function(){
39214 this.beforeSlide();
39215 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39216 this.el.setStyle("z-index", 20000);
39217 this.collapsedEl.hide({
39220 this.el.slideIn(this.getSlideAnchor(), {
39221 callback : function(){
39222 this.el.setStyle("z-index", "");
39225 this.split.el.show();
39227 this.fireEvent("invalidated", this);
39228 this.fireEvent("expanded", this);
39256 getAnchor : function(){
39257 return this.anchors[this.position];
39260 getCollapseAnchor : function(){
39261 return this.canchors[this.position];
39264 getSlideAnchor : function(){
39265 return this.sanchors[this.position];
39268 getAlignAdj : function(){
39269 var cm = this.cmargins;
39270 switch(this.position){
39286 getExpandAdj : function(){
39287 var c = this.collapsedEl, cm = this.cmargins;
39288 switch(this.position){
39290 return [-(cm.right+c.getWidth()+cm.left), 0];
39293 return [cm.right+c.getWidth()+cm.left, 0];
39296 return [0, -(cm.top+cm.bottom+c.getHeight())];
39299 return [0, cm.top+cm.bottom+c.getHeight()];
39305 * Ext JS Library 1.1.1
39306 * Copyright(c) 2006-2007, Ext JS, LLC.
39308 * Originally Released Under LGPL - original licence link has changed is not relivant.
39311 * <script type="text/javascript">
39314 * These classes are private internal classes
39316 Roo.bootstrap.layout.Center = function(config){
39317 config.region = "center";
39318 Roo.bootstrap.layout.Region.call(this, config);
39319 this.visible = true;
39320 this.minWidth = config.minWidth || 20;
39321 this.minHeight = config.minHeight || 20;
39324 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39326 // center panel can't be hidden
39330 // center panel can't be hidden
39333 getMinWidth: function(){
39334 return this.minWidth;
39337 getMinHeight: function(){
39338 return this.minHeight;
39352 Roo.bootstrap.layout.North = function(config)
39354 config.region = 'north';
39355 config.cursor = 'n-resize';
39357 Roo.bootstrap.layout.Split.call(this, config);
39361 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39362 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39363 this.split.el.addClass("roo-layout-split-v");
39365 //var size = config.initialSize || config.height;
39366 //if(this.el && typeof size != "undefined"){
39367 // this.el.setHeight(size);
39370 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39372 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39375 onRender : function(ctr, pos)
39377 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39378 var size = this.config.initialSize || this.config.height;
39379 if(this.el && typeof size != "undefined"){
39380 this.el.setHeight(size);
39385 getBox : function(){
39386 if(this.collapsed){
39387 return this.collapsedEl.getBox();
39389 var box = this.el.getBox();
39391 box.height += this.split.el.getHeight();
39396 updateBox : function(box){
39397 if(this.split && !this.collapsed){
39398 box.height -= this.split.el.getHeight();
39399 this.split.el.setLeft(box.x);
39400 this.split.el.setTop(box.y+box.height);
39401 this.split.el.setWidth(box.width);
39403 if(this.collapsed){
39404 this.updateBody(box.width, null);
39406 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39414 Roo.bootstrap.layout.South = function(config){
39415 config.region = 'south';
39416 config.cursor = 's-resize';
39417 Roo.bootstrap.layout.Split.call(this, config);
39419 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39420 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39421 this.split.el.addClass("roo-layout-split-v");
39426 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39427 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39429 onRender : function(ctr, pos)
39431 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39432 var size = this.config.initialSize || this.config.height;
39433 if(this.el && typeof size != "undefined"){
39434 this.el.setHeight(size);
39439 getBox : function(){
39440 if(this.collapsed){
39441 return this.collapsedEl.getBox();
39443 var box = this.el.getBox();
39445 var sh = this.split.el.getHeight();
39452 updateBox : function(box){
39453 if(this.split && !this.collapsed){
39454 var sh = this.split.el.getHeight();
39457 this.split.el.setLeft(box.x);
39458 this.split.el.setTop(box.y-sh);
39459 this.split.el.setWidth(box.width);
39461 if(this.collapsed){
39462 this.updateBody(box.width, null);
39464 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39468 Roo.bootstrap.layout.East = function(config){
39469 config.region = "east";
39470 config.cursor = "e-resize";
39471 Roo.bootstrap.layout.Split.call(this, config);
39473 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39474 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39475 this.split.el.addClass("roo-layout-split-h");
39479 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39480 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39482 onRender : function(ctr, pos)
39484 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39485 var size = this.config.initialSize || this.config.width;
39486 if(this.el && typeof size != "undefined"){
39487 this.el.setWidth(size);
39492 getBox : function(){
39493 if(this.collapsed){
39494 return this.collapsedEl.getBox();
39496 var box = this.el.getBox();
39498 var sw = this.split.el.getWidth();
39505 updateBox : function(box){
39506 if(this.split && !this.collapsed){
39507 var sw = this.split.el.getWidth();
39509 this.split.el.setLeft(box.x);
39510 this.split.el.setTop(box.y);
39511 this.split.el.setHeight(box.height);
39514 if(this.collapsed){
39515 this.updateBody(null, box.height);
39517 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39521 Roo.bootstrap.layout.West = function(config){
39522 config.region = "west";
39523 config.cursor = "w-resize";
39525 Roo.bootstrap.layout.Split.call(this, config);
39527 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39528 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39529 this.split.el.addClass("roo-layout-split-h");
39533 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39534 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39536 onRender: function(ctr, pos)
39538 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39539 var size = this.config.initialSize || this.config.width;
39540 if(typeof size != "undefined"){
39541 this.el.setWidth(size);
39545 getBox : function(){
39546 if(this.collapsed){
39547 return this.collapsedEl.getBox();
39549 var box = this.el.getBox();
39550 if (box.width == 0) {
39551 box.width = this.config.width; // kludge?
39554 box.width += this.split.el.getWidth();
39559 updateBox : function(box){
39560 if(this.split && !this.collapsed){
39561 var sw = this.split.el.getWidth();
39563 this.split.el.setLeft(box.x+box.width);
39564 this.split.el.setTop(box.y);
39565 this.split.el.setHeight(box.height);
39567 if(this.collapsed){
39568 this.updateBody(null, box.height);
39570 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39572 });Roo.namespace("Roo.bootstrap.panel");/*
39574 * Ext JS Library 1.1.1
39575 * Copyright(c) 2006-2007, Ext JS, LLC.
39577 * Originally Released Under LGPL - original licence link has changed is not relivant.
39580 * <script type="text/javascript">
39583 * @class Roo.ContentPanel
39584 * @extends Roo.util.Observable
39585 * A basic ContentPanel element.
39586 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39587 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39588 * @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
39589 * @cfg {Boolean} closable True if the panel can be closed/removed
39590 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39591 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39592 * @cfg {Toolbar} toolbar A toolbar for this panel
39593 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39594 * @cfg {String} title The title for this panel
39595 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39596 * @cfg {String} url Calls {@link #setUrl} with this value
39597 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39598 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39599 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39600 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39601 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39602 * @cfg {Boolean} badges render the badges
39603 * @cfg {String} cls extra classes to use
39604 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39607 * Create a new ContentPanel.
39608 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39609 * @param {String/Object} config A string to set only the title or a config object
39610 * @param {String} content (optional) Set the HTML content for this panel
39611 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39613 Roo.bootstrap.panel.Content = function( config){
39615 this.tpl = config.tpl || false;
39617 var el = config.el;
39618 var content = config.content;
39620 if(config.autoCreate){ // xtype is available if this is called from factory
39623 this.el = Roo.get(el);
39624 if(!this.el && config && config.autoCreate){
39625 if(typeof config.autoCreate == "object"){
39626 if(!config.autoCreate.id){
39627 config.autoCreate.id = config.id||el;
39629 this.el = Roo.DomHelper.append(document.body,
39630 config.autoCreate, true);
39634 cls: (config.cls || '') +
39635 (config.background ? ' bg-' + config.background : '') +
39636 " roo-layout-inactive-content",
39639 if (config.iframe) {
39643 style : 'border: 0px',
39644 src : 'about:blank'
39650 elcfg.html = config.html;
39654 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39655 if (config.iframe) {
39656 this.iframeEl = this.el.select('iframe',true).first();
39661 this.closable = false;
39662 this.loaded = false;
39663 this.active = false;
39666 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39668 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39670 this.wrapEl = this.el; //this.el.wrap();
39672 if (config.toolbar.items) {
39673 ti = config.toolbar.items ;
39674 delete config.toolbar.items ;
39678 this.toolbar.render(this.wrapEl, 'before');
39679 for(var i =0;i < ti.length;i++) {
39680 // Roo.log(['add child', items[i]]);
39681 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39683 this.toolbar.items = nitems;
39684 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39685 delete config.toolbar;
39689 // xtype created footer. - not sure if will work as we normally have to render first..
39690 if (this.footer && !this.footer.el && this.footer.xtype) {
39691 if (!this.wrapEl) {
39692 this.wrapEl = this.el.wrap();
39695 this.footer.container = this.wrapEl.createChild();
39697 this.footer = Roo.factory(this.footer, Roo);
39702 if(typeof config == "string"){
39703 this.title = config;
39705 Roo.apply(this, config);
39709 this.resizeEl = Roo.get(this.resizeEl, true);
39711 this.resizeEl = this.el;
39713 // handle view.xtype
39721 * Fires when this panel is activated.
39722 * @param {Roo.ContentPanel} this
39726 * @event deactivate
39727 * Fires when this panel is activated.
39728 * @param {Roo.ContentPanel} this
39730 "deactivate" : true,
39734 * Fires when this panel is resized if fitToFrame is true.
39735 * @param {Roo.ContentPanel} this
39736 * @param {Number} width The width after any component adjustments
39737 * @param {Number} height The height after any component adjustments
39743 * Fires when this tab is created
39744 * @param {Roo.ContentPanel} this
39755 if(this.autoScroll && !this.iframe){
39756 this.resizeEl.setStyle("overflow", "auto");
39758 // fix randome scrolling
39759 //this.el.on('scroll', function() {
39760 // Roo.log('fix random scolling');
39761 // this.scrollTo('top',0);
39764 content = content || this.content;
39766 this.setContent(content);
39768 if(config && config.url){
39769 this.setUrl(this.url, this.params, this.loadOnce);
39774 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39776 if (this.view && typeof(this.view.xtype) != 'undefined') {
39777 this.view.el = this.el.appendChild(document.createElement("div"));
39778 this.view = Roo.factory(this.view);
39779 this.view.render && this.view.render(false, '');
39783 this.fireEvent('render', this);
39786 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39796 setRegion : function(region){
39797 this.region = region;
39798 this.setActiveClass(region && !this.background);
39802 setActiveClass: function(state)
39805 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39806 this.el.setStyle('position','relative');
39808 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39809 this.el.setStyle('position', 'absolute');
39814 * Returns the toolbar for this Panel if one was configured.
39815 * @return {Roo.Toolbar}
39817 getToolbar : function(){
39818 return this.toolbar;
39821 setActiveState : function(active)
39823 this.active = active;
39824 this.setActiveClass(active);
39826 if(this.fireEvent("deactivate", this) === false){
39831 this.fireEvent("activate", this);
39835 * Updates this panel's element (not for iframe)
39836 * @param {String} content The new content
39837 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39839 setContent : function(content, loadScripts){
39844 this.el.update(content, loadScripts);
39847 ignoreResize : function(w, h){
39848 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39851 this.lastSize = {width: w, height: h};
39856 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39857 * @return {Roo.UpdateManager} The UpdateManager
39859 getUpdateManager : function(){
39863 return this.el.getUpdateManager();
39866 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39867 * Does not work with IFRAME contents
39868 * @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:
39871 url: "your-url.php",
39872 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39873 callback: yourFunction,
39874 scope: yourObject, //(optional scope)
39877 text: "Loading...",
39883 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39884 * 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.
39885 * @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}
39886 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39887 * @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.
39888 * @return {Roo.ContentPanel} this
39896 var um = this.el.getUpdateManager();
39897 um.update.apply(um, arguments);
39903 * 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.
39904 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39905 * @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)
39906 * @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)
39907 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39909 setUrl : function(url, params, loadOnce){
39911 this.iframeEl.dom.src = url;
39915 if(this.refreshDelegate){
39916 this.removeListener("activate", this.refreshDelegate);
39918 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39919 this.on("activate", this.refreshDelegate);
39920 return this.el.getUpdateManager();
39923 _handleRefresh : function(url, params, loadOnce){
39924 if(!loadOnce || !this.loaded){
39925 var updater = this.el.getUpdateManager();
39926 updater.update(url, params, this._setLoaded.createDelegate(this));
39930 _setLoaded : function(){
39931 this.loaded = true;
39935 * Returns this panel's id
39938 getId : function(){
39943 * Returns this panel's element - used by regiosn to add.
39944 * @return {Roo.Element}
39946 getEl : function(){
39947 return this.wrapEl || this.el;
39952 adjustForComponents : function(width, height)
39954 //Roo.log('adjustForComponents ');
39955 if(this.resizeEl != this.el){
39956 width -= this.el.getFrameWidth('lr');
39957 height -= this.el.getFrameWidth('tb');
39960 var te = this.toolbar.getEl();
39961 te.setWidth(width);
39962 height -= te.getHeight();
39965 var te = this.footer.getEl();
39966 te.setWidth(width);
39967 height -= te.getHeight();
39971 if(this.adjustments){
39972 width += this.adjustments[0];
39973 height += this.adjustments[1];
39975 return {"width": width, "height": height};
39978 setSize : function(width, height){
39979 if(this.fitToFrame && !this.ignoreResize(width, height)){
39980 if(this.fitContainer && this.resizeEl != this.el){
39981 this.el.setSize(width, height);
39983 var size = this.adjustForComponents(width, height);
39985 this.iframeEl.setSize(width,height);
39988 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39989 this.fireEvent('resize', this, size.width, size.height);
39996 * Returns this panel's title
39999 getTitle : function(){
40001 if (typeof(this.title) != 'object') {
40006 for (var k in this.title) {
40007 if (!this.title.hasOwnProperty(k)) {
40011 if (k.indexOf('-') >= 0) {
40012 var s = k.split('-');
40013 for (var i = 0; i<s.length; i++) {
40014 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40017 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40024 * Set this panel's title
40025 * @param {String} title
40027 setTitle : function(title){
40028 this.title = title;
40030 this.region.updatePanelTitle(this, title);
40035 * Returns true is this panel was configured to be closable
40036 * @return {Boolean}
40038 isClosable : function(){
40039 return this.closable;
40042 beforeSlide : function(){
40044 this.resizeEl.clip();
40047 afterSlide : function(){
40049 this.resizeEl.unclip();
40053 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40054 * Will fail silently if the {@link #setUrl} method has not been called.
40055 * This does not activate the panel, just updates its content.
40057 refresh : function(){
40058 if(this.refreshDelegate){
40059 this.loaded = false;
40060 this.refreshDelegate();
40065 * Destroys this panel
40067 destroy : function(){
40068 this.el.removeAllListeners();
40069 var tempEl = document.createElement("span");
40070 tempEl.appendChild(this.el.dom);
40071 tempEl.innerHTML = "";
40077 * form - if the content panel contains a form - this is a reference to it.
40078 * @type {Roo.form.Form}
40082 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40083 * This contains a reference to it.
40089 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40099 * @param {Object} cfg Xtype definition of item to add.
40103 getChildContainer: function () {
40104 return this.getEl();
40109 var ret = new Roo.factory(cfg);
40114 if (cfg.xtype.match(/^Form$/)) {
40117 //if (this.footer) {
40118 // el = this.footer.container.insertSibling(false, 'before');
40120 el = this.el.createChild();
40123 this.form = new Roo.form.Form(cfg);
40126 if ( this.form.allItems.length) {
40127 this.form.render(el.dom);
40131 // should only have one of theses..
40132 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40133 // views.. should not be just added - used named prop 'view''
40135 cfg.el = this.el.appendChild(document.createElement("div"));
40138 var ret = new Roo.factory(cfg);
40140 ret.render && ret.render(false, ''); // render blank..
40150 * @class Roo.bootstrap.panel.Grid
40151 * @extends Roo.bootstrap.panel.Content
40153 * Create a new GridPanel.
40154 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40155 * @param {Object} config A the config object
40161 Roo.bootstrap.panel.Grid = function(config)
40165 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40166 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40168 config.el = this.wrapper;
40169 //this.el = this.wrapper;
40171 if (config.container) {
40172 // ctor'ed from a Border/panel.grid
40175 this.wrapper.setStyle("overflow", "hidden");
40176 this.wrapper.addClass('roo-grid-container');
40181 if(config.toolbar){
40182 var tool_el = this.wrapper.createChild();
40183 this.toolbar = Roo.factory(config.toolbar);
40185 if (config.toolbar.items) {
40186 ti = config.toolbar.items ;
40187 delete config.toolbar.items ;
40191 this.toolbar.render(tool_el);
40192 for(var i =0;i < ti.length;i++) {
40193 // Roo.log(['add child', items[i]]);
40194 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40196 this.toolbar.items = nitems;
40198 delete config.toolbar;
40201 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40202 config.grid.scrollBody = true;;
40203 config.grid.monitorWindowResize = false; // turn off autosizing
40204 config.grid.autoHeight = false;
40205 config.grid.autoWidth = false;
40207 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40209 if (config.background) {
40210 // render grid on panel activation (if panel background)
40211 this.on('activate', function(gp) {
40212 if (!gp.grid.rendered) {
40213 gp.grid.render(this.wrapper);
40214 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40219 this.grid.render(this.wrapper);
40220 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40223 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40224 // ??? needed ??? config.el = this.wrapper;
40229 // xtype created footer. - not sure if will work as we normally have to render first..
40230 if (this.footer && !this.footer.el && this.footer.xtype) {
40232 var ctr = this.grid.getView().getFooterPanel(true);
40233 this.footer.dataSource = this.grid.dataSource;
40234 this.footer = Roo.factory(this.footer, Roo);
40235 this.footer.render(ctr);
40245 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40246 getId : function(){
40247 return this.grid.id;
40251 * Returns the grid for this panel
40252 * @return {Roo.bootstrap.Table}
40254 getGrid : function(){
40258 setSize : function(width, height){
40259 if(!this.ignoreResize(width, height)){
40260 var grid = this.grid;
40261 var size = this.adjustForComponents(width, height);
40262 // tfoot is not a footer?
40265 var gridel = grid.getGridEl();
40266 gridel.setSize(size.width, size.height);
40268 var tbd = grid.getGridEl().select('tbody', true).first();
40269 var thd = grid.getGridEl().select('thead',true).first();
40270 var tbf= grid.getGridEl().select('tfoot', true).first();
40273 size.height -= tbf.getHeight();
40276 size.height -= thd.getHeight();
40279 tbd.setSize(size.width, size.height );
40280 // this is for the account management tab -seems to work there.
40281 var thd = grid.getGridEl().select('thead',true).first();
40283 // tbd.setSize(size.width, size.height - thd.getHeight());
40292 beforeSlide : function(){
40293 this.grid.getView().scroller.clip();
40296 afterSlide : function(){
40297 this.grid.getView().scroller.unclip();
40300 destroy : function(){
40301 this.grid.destroy();
40303 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40308 * @class Roo.bootstrap.panel.Nest
40309 * @extends Roo.bootstrap.panel.Content
40311 * Create a new Panel, that can contain a layout.Border.
40314 * @param {Roo.BorderLayout} layout The layout for this panel
40315 * @param {String/Object} config A string to set only the title or a config object
40317 Roo.bootstrap.panel.Nest = function(config)
40319 // construct with only one argument..
40320 /* FIXME - implement nicer consturctors
40321 if (layout.layout) {
40323 layout = config.layout;
40324 delete config.layout;
40326 if (layout.xtype && !layout.getEl) {
40327 // then layout needs constructing..
40328 layout = Roo.factory(layout, Roo);
40332 config.el = config.layout.getEl();
40334 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40336 config.layout.monitorWindowResize = false; // turn off autosizing
40337 this.layout = config.layout;
40338 this.layout.getEl().addClass("roo-layout-nested-layout");
40339 this.layout.parent = this;
40346 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40348 setSize : function(width, height){
40349 if(!this.ignoreResize(width, height)){
40350 var size = this.adjustForComponents(width, height);
40351 var el = this.layout.getEl();
40352 if (size.height < 1) {
40353 el.setWidth(size.width);
40355 el.setSize(size.width, size.height);
40357 var touch = el.dom.offsetWidth;
40358 this.layout.layout();
40359 // ie requires a double layout on the first pass
40360 if(Roo.isIE && !this.initialized){
40361 this.initialized = true;
40362 this.layout.layout();
40367 // activate all subpanels if not currently active..
40369 setActiveState : function(active){
40370 this.active = active;
40371 this.setActiveClass(active);
40374 this.fireEvent("deactivate", this);
40378 this.fireEvent("activate", this);
40379 // not sure if this should happen before or after..
40380 if (!this.layout) {
40381 return; // should not happen..
40384 for (var r in this.layout.regions) {
40385 reg = this.layout.getRegion(r);
40386 if (reg.getActivePanel()) {
40387 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40388 reg.setActivePanel(reg.getActivePanel());
40391 if (!reg.panels.length) {
40394 reg.showPanel(reg.getPanel(0));
40403 * Returns the nested BorderLayout for this panel
40404 * @return {Roo.BorderLayout}
40406 getLayout : function(){
40407 return this.layout;
40411 * Adds a xtype elements to the layout of the nested panel
40415 xtype : 'ContentPanel',
40422 xtype : 'NestedLayoutPanel',
40428 items : [ ... list of content panels or nested layout panels.. ]
40432 * @param {Object} cfg Xtype definition of item to add.
40434 addxtype : function(cfg) {
40435 return this.layout.addxtype(cfg);
40440 * Ext JS Library 1.1.1
40441 * Copyright(c) 2006-2007, Ext JS, LLC.
40443 * Originally Released Under LGPL - original licence link has changed is not relivant.
40446 * <script type="text/javascript">
40449 * @class Roo.TabPanel
40450 * @extends Roo.util.Observable
40451 * A lightweight tab container.
40455 // basic tabs 1, built from existing content
40456 var tabs = new Roo.TabPanel("tabs1");
40457 tabs.addTab("script", "View Script");
40458 tabs.addTab("markup", "View Markup");
40459 tabs.activate("script");
40461 // more advanced tabs, built from javascript
40462 var jtabs = new Roo.TabPanel("jtabs");
40463 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40465 // set up the UpdateManager
40466 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40467 var updater = tab2.getUpdateManager();
40468 updater.setDefaultUrl("ajax1.htm");
40469 tab2.on('activate', updater.refresh, updater, true);
40471 // Use setUrl for Ajax loading
40472 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40473 tab3.setUrl("ajax2.htm", null, true);
40476 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40479 jtabs.activate("jtabs-1");
40482 * Create a new TabPanel.
40483 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40484 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40486 Roo.bootstrap.panel.Tabs = function(config){
40488 * The container element for this TabPanel.
40489 * @type Roo.Element
40491 this.el = Roo.get(config.el);
40494 if(typeof config == "boolean"){
40495 this.tabPosition = config ? "bottom" : "top";
40497 Roo.apply(this, config);
40501 if(this.tabPosition == "bottom"){
40502 // if tabs are at the bottom = create the body first.
40503 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40504 this.el.addClass("roo-tabs-bottom");
40506 // next create the tabs holders
40508 if (this.tabPosition == "west"){
40510 var reg = this.region; // fake it..
40512 if (!reg.mgr.parent) {
40515 reg = reg.mgr.parent.region;
40517 Roo.log("got nest?");
40519 if (reg.mgr.getRegion('west')) {
40520 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40521 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40522 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40523 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40524 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40532 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40533 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40534 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40535 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40540 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40543 // finally - if tabs are at the top, then create the body last..
40544 if(this.tabPosition != "bottom"){
40545 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40546 * @type Roo.Element
40548 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40549 this.el.addClass("roo-tabs-top");
40553 this.bodyEl.setStyle("position", "relative");
40555 this.active = null;
40556 this.activateDelegate = this.activate.createDelegate(this);
40561 * Fires when the active tab changes
40562 * @param {Roo.TabPanel} this
40563 * @param {Roo.TabPanelItem} activePanel The new active tab
40567 * @event beforetabchange
40568 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40569 * @param {Roo.TabPanel} this
40570 * @param {Object} e Set cancel to true on this object to cancel the tab change
40571 * @param {Roo.TabPanelItem} tab The tab being changed to
40573 "beforetabchange" : true
40576 Roo.EventManager.onWindowResize(this.onResize, this);
40577 this.cpad = this.el.getPadding("lr");
40578 this.hiddenCount = 0;
40581 // toolbar on the tabbar support...
40582 if (this.toolbar) {
40583 alert("no toolbar support yet");
40584 this.toolbar = false;
40586 var tcfg = this.toolbar;
40587 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40588 this.toolbar = new Roo.Toolbar(tcfg);
40589 if (Roo.isSafari) {
40590 var tbl = tcfg.container.child('table', true);
40591 tbl.setAttribute('width', '100%');
40599 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40602 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40604 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40606 tabPosition : "top",
40608 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40610 currentTabWidth : 0,
40612 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40616 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40620 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40622 preferredTabWidth : 175,
40624 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40626 resizeTabs : false,
40628 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40630 monitorResize : true,
40632 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40634 toolbar : false, // set by caller..
40636 region : false, /// set by caller
40638 disableTooltips : true, // not used yet...
40641 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40642 * @param {String} id The id of the div to use <b>or create</b>
40643 * @param {String} text The text for the tab
40644 * @param {String} content (optional) Content to put in the TabPanelItem body
40645 * @param {Boolean} closable (optional) True to create a close icon on the tab
40646 * @return {Roo.TabPanelItem} The created TabPanelItem
40648 addTab : function(id, text, content, closable, tpl)
40650 var item = new Roo.bootstrap.panel.TabItem({
40654 closable : closable,
40657 this.addTabItem(item);
40659 item.setContent(content);
40665 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40666 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40667 * @return {Roo.TabPanelItem}
40669 getTab : function(id){
40670 return this.items[id];
40674 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40675 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40677 hideTab : function(id){
40678 var t = this.items[id];
40681 this.hiddenCount++;
40682 this.autoSizeTabs();
40687 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40688 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40690 unhideTab : function(id){
40691 var t = this.items[id];
40693 t.setHidden(false);
40694 this.hiddenCount--;
40695 this.autoSizeTabs();
40700 * Adds an existing {@link Roo.TabPanelItem}.
40701 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40703 addTabItem : function(item)
40705 this.items[item.id] = item;
40706 this.items.push(item);
40707 this.autoSizeTabs();
40708 // if(this.resizeTabs){
40709 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40710 // this.autoSizeTabs();
40712 // item.autoSize();
40717 * Removes a {@link Roo.TabPanelItem}.
40718 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40720 removeTab : function(id){
40721 var items = this.items;
40722 var tab = items[id];
40723 if(!tab) { return; }
40724 var index = items.indexOf(tab);
40725 if(this.active == tab && items.length > 1){
40726 var newTab = this.getNextAvailable(index);
40731 this.stripEl.dom.removeChild(tab.pnode.dom);
40732 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40733 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40735 items.splice(index, 1);
40736 delete this.items[tab.id];
40737 tab.fireEvent("close", tab);
40738 tab.purgeListeners();
40739 this.autoSizeTabs();
40742 getNextAvailable : function(start){
40743 var items = this.items;
40745 // look for a next tab that will slide over to
40746 // replace the one being removed
40747 while(index < items.length){
40748 var item = items[++index];
40749 if(item && !item.isHidden()){
40753 // if one isn't found select the previous tab (on the left)
40756 var item = items[--index];
40757 if(item && !item.isHidden()){
40765 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40766 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40768 disableTab : function(id){
40769 var tab = this.items[id];
40770 if(tab && this.active != tab){
40776 * Enables a {@link Roo.TabPanelItem} that is disabled.
40777 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40779 enableTab : function(id){
40780 var tab = this.items[id];
40785 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40786 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40787 * @return {Roo.TabPanelItem} The TabPanelItem.
40789 activate : function(id)
40791 //Roo.log('activite:' + id);
40793 var tab = this.items[id];
40797 if(tab == this.active || tab.disabled){
40801 this.fireEvent("beforetabchange", this, e, tab);
40802 if(e.cancel !== true && !tab.disabled){
40804 this.active.hide();
40806 this.active = this.items[id];
40807 this.active.show();
40808 this.fireEvent("tabchange", this, this.active);
40814 * Gets the active {@link Roo.TabPanelItem}.
40815 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40817 getActiveTab : function(){
40818 return this.active;
40822 * Updates the tab body element to fit the height of the container element
40823 * for overflow scrolling
40824 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40826 syncHeight : function(targetHeight){
40827 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40828 var bm = this.bodyEl.getMargins();
40829 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40830 this.bodyEl.setHeight(newHeight);
40834 onResize : function(){
40835 if(this.monitorResize){
40836 this.autoSizeTabs();
40841 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40843 beginUpdate : function(){
40844 this.updating = true;
40848 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40850 endUpdate : function(){
40851 this.updating = false;
40852 this.autoSizeTabs();
40856 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40858 autoSizeTabs : function()
40860 var count = this.items.length;
40861 var vcount = count - this.hiddenCount;
40864 this.stripEl.hide();
40866 this.stripEl.show();
40869 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40874 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40875 var availWidth = Math.floor(w / vcount);
40876 var b = this.stripBody;
40877 if(b.getWidth() > w){
40878 var tabs = this.items;
40879 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40880 if(availWidth < this.minTabWidth){
40881 /*if(!this.sleft){ // incomplete scrolling code
40882 this.createScrollButtons();
40885 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40888 if(this.currentTabWidth < this.preferredTabWidth){
40889 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40895 * Returns the number of tabs in this TabPanel.
40898 getCount : function(){
40899 return this.items.length;
40903 * Resizes all the tabs to the passed width
40904 * @param {Number} The new width
40906 setTabWidth : function(width){
40907 this.currentTabWidth = width;
40908 for(var i = 0, len = this.items.length; i < len; i++) {
40909 if(!this.items[i].isHidden()) {
40910 this.items[i].setWidth(width);
40916 * Destroys this TabPanel
40917 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40919 destroy : function(removeEl){
40920 Roo.EventManager.removeResizeListener(this.onResize, this);
40921 for(var i = 0, len = this.items.length; i < len; i++){
40922 this.items[i].purgeListeners();
40924 if(removeEl === true){
40925 this.el.update("");
40930 createStrip : function(container)
40932 var strip = document.createElement("nav");
40933 strip.className = Roo.bootstrap.version == 4 ?
40934 "navbar-light bg-light" :
40935 "navbar navbar-default"; //"x-tabs-wrap";
40936 container.appendChild(strip);
40940 createStripList : function(strip)
40942 // div wrapper for retard IE
40943 // returns the "tr" element.
40944 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40945 //'<div class="x-tabs-strip-wrap">'+
40946 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40947 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40948 return strip.firstChild; //.firstChild.firstChild.firstChild;
40950 createBody : function(container)
40952 var body = document.createElement("div");
40953 Roo.id(body, "tab-body");
40954 //Roo.fly(body).addClass("x-tabs-body");
40955 Roo.fly(body).addClass("tab-content");
40956 container.appendChild(body);
40959 createItemBody :function(bodyEl, id){
40960 var body = Roo.getDom(id);
40962 body = document.createElement("div");
40965 //Roo.fly(body).addClass("x-tabs-item-body");
40966 Roo.fly(body).addClass("tab-pane");
40967 bodyEl.insertBefore(body, bodyEl.firstChild);
40971 createStripElements : function(stripEl, text, closable, tpl)
40973 var td = document.createElement("li"); // was td..
40974 td.className = 'nav-item';
40976 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40979 stripEl.appendChild(td);
40981 td.className = "x-tabs-closable";
40982 if(!this.closeTpl){
40983 this.closeTpl = new Roo.Template(
40984 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40985 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40986 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
40989 var el = this.closeTpl.overwrite(td, {"text": text});
40990 var close = el.getElementsByTagName("div")[0];
40991 var inner = el.getElementsByTagName("em")[0];
40992 return {"el": el, "close": close, "inner": inner};
40995 // not sure what this is..
40996 // if(!this.tabTpl){
40997 //this.tabTpl = new Roo.Template(
40998 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40999 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41001 // this.tabTpl = new Roo.Template(
41002 // '<a href="#">' +
41003 // '<span unselectable="on"' +
41004 // (this.disableTooltips ? '' : ' title="{text}"') +
41005 // ' >{text}</span></a>'
41011 var template = tpl || this.tabTpl || false;
41014 template = new Roo.Template(
41015 Roo.bootstrap.version == 4 ?
41017 '<a class="nav-link" href="#" unselectable="on"' +
41018 (this.disableTooltips ? '' : ' title="{text}"') +
41021 '<a class="nav-link" href="#">' +
41022 '<span unselectable="on"' +
41023 (this.disableTooltips ? '' : ' title="{text}"') +
41024 ' >{text}</span></a>'
41029 switch (typeof(template)) {
41033 template = new Roo.Template(template);
41039 var el = template.overwrite(td, {"text": text});
41041 var inner = el.getElementsByTagName("span")[0];
41043 return {"el": el, "inner": inner};
41051 * @class Roo.TabPanelItem
41052 * @extends Roo.util.Observable
41053 * Represents an individual item (tab plus body) in a TabPanel.
41054 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41055 * @param {String} id The id of this TabPanelItem
41056 * @param {String} text The text for the tab of this TabPanelItem
41057 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41059 Roo.bootstrap.panel.TabItem = function(config){
41061 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41062 * @type Roo.TabPanel
41064 this.tabPanel = config.panel;
41066 * The id for this TabPanelItem
41069 this.id = config.id;
41071 this.disabled = false;
41073 this.text = config.text;
41075 this.loaded = false;
41076 this.closable = config.closable;
41079 * The body element for this TabPanelItem.
41080 * @type Roo.Element
41082 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41083 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41084 this.bodyEl.setStyle("display", "block");
41085 this.bodyEl.setStyle("zoom", "1");
41086 //this.hideAction();
41088 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41090 this.el = Roo.get(els.el);
41091 this.inner = Roo.get(els.inner, true);
41092 this.textEl = Roo.bootstrap.version == 4 ?
41093 this.el : Roo.get(this.el.dom.firstChild, true);
41095 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41096 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41099 // this.el.on("mousedown", this.onTabMouseDown, this);
41100 this.el.on("click", this.onTabClick, this);
41102 if(config.closable){
41103 var c = Roo.get(els.close, true);
41104 c.dom.title = this.closeText;
41105 c.addClassOnOver("close-over");
41106 c.on("click", this.closeClick, this);
41112 * Fires when this tab becomes the active tab.
41113 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41114 * @param {Roo.TabPanelItem} this
41118 * @event beforeclose
41119 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41120 * @param {Roo.TabPanelItem} this
41121 * @param {Object} e Set cancel to true on this object to cancel the close.
41123 "beforeclose": true,
41126 * Fires when this tab is closed.
41127 * @param {Roo.TabPanelItem} this
41131 * @event deactivate
41132 * Fires when this tab is no longer the active tab.
41133 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41134 * @param {Roo.TabPanelItem} this
41136 "deactivate" : true
41138 this.hidden = false;
41140 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41143 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41145 purgeListeners : function(){
41146 Roo.util.Observable.prototype.purgeListeners.call(this);
41147 this.el.removeAllListeners();
41150 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41153 this.status_node.addClass("active");
41156 this.tabPanel.stripWrap.repaint();
41158 this.fireEvent("activate", this.tabPanel, this);
41162 * Returns true if this tab is the active tab.
41163 * @return {Boolean}
41165 isActive : function(){
41166 return this.tabPanel.getActiveTab() == this;
41170 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41173 this.status_node.removeClass("active");
41175 this.fireEvent("deactivate", this.tabPanel, this);
41178 hideAction : function(){
41179 this.bodyEl.hide();
41180 this.bodyEl.setStyle("position", "absolute");
41181 this.bodyEl.setLeft("-20000px");
41182 this.bodyEl.setTop("-20000px");
41185 showAction : function(){
41186 this.bodyEl.setStyle("position", "relative");
41187 this.bodyEl.setTop("");
41188 this.bodyEl.setLeft("");
41189 this.bodyEl.show();
41193 * Set the tooltip for the tab.
41194 * @param {String} tooltip The tab's tooltip
41196 setTooltip : function(text){
41197 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41198 this.textEl.dom.qtip = text;
41199 this.textEl.dom.removeAttribute('title');
41201 this.textEl.dom.title = text;
41205 onTabClick : function(e){
41206 e.preventDefault();
41207 this.tabPanel.activate(this.id);
41210 onTabMouseDown : function(e){
41211 e.preventDefault();
41212 this.tabPanel.activate(this.id);
41215 getWidth : function(){
41216 return this.inner.getWidth();
41219 setWidth : function(width){
41220 var iwidth = width - this.linode.getPadding("lr");
41221 this.inner.setWidth(iwidth);
41222 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41223 this.linode.setWidth(width);
41227 * Show or hide the tab
41228 * @param {Boolean} hidden True to hide or false to show.
41230 setHidden : function(hidden){
41231 this.hidden = hidden;
41232 this.linode.setStyle("display", hidden ? "none" : "");
41236 * Returns true if this tab is "hidden"
41237 * @return {Boolean}
41239 isHidden : function(){
41240 return this.hidden;
41244 * Returns the text for this tab
41247 getText : function(){
41251 autoSize : function(){
41252 //this.el.beginMeasure();
41253 this.textEl.setWidth(1);
41255 * #2804 [new] Tabs in Roojs
41256 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41258 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41259 //this.el.endMeasure();
41263 * Sets the text for the tab (Note: this also sets the tooltip text)
41264 * @param {String} text The tab's text and tooltip
41266 setText : function(text){
41268 this.textEl.update(text);
41269 this.setTooltip(text);
41270 //if(!this.tabPanel.resizeTabs){
41271 // this.autoSize();
41275 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41277 activate : function(){
41278 this.tabPanel.activate(this.id);
41282 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41284 disable : function(){
41285 if(this.tabPanel.active != this){
41286 this.disabled = true;
41287 this.status_node.addClass("disabled");
41292 * Enables this TabPanelItem if it was previously disabled.
41294 enable : function(){
41295 this.disabled = false;
41296 this.status_node.removeClass("disabled");
41300 * Sets the content for this TabPanelItem.
41301 * @param {String} content The content
41302 * @param {Boolean} loadScripts true to look for and load scripts
41304 setContent : function(content, loadScripts){
41305 this.bodyEl.update(content, loadScripts);
41309 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41310 * @return {Roo.UpdateManager} The UpdateManager
41312 getUpdateManager : function(){
41313 return this.bodyEl.getUpdateManager();
41317 * Set a URL to be used to load the content for this TabPanelItem.
41318 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41319 * @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)
41320 * @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)
41321 * @return {Roo.UpdateManager} The UpdateManager
41323 setUrl : function(url, params, loadOnce){
41324 if(this.refreshDelegate){
41325 this.un('activate', this.refreshDelegate);
41327 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41328 this.on("activate", this.refreshDelegate);
41329 return this.bodyEl.getUpdateManager();
41333 _handleRefresh : function(url, params, loadOnce){
41334 if(!loadOnce || !this.loaded){
41335 var updater = this.bodyEl.getUpdateManager();
41336 updater.update(url, params, this._setLoaded.createDelegate(this));
41341 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41342 * Will fail silently if the setUrl method has not been called.
41343 * This does not activate the panel, just updates its content.
41345 refresh : function(){
41346 if(this.refreshDelegate){
41347 this.loaded = false;
41348 this.refreshDelegate();
41353 _setLoaded : function(){
41354 this.loaded = true;
41358 closeClick : function(e){
41361 this.fireEvent("beforeclose", this, o);
41362 if(o.cancel !== true){
41363 this.tabPanel.removeTab(this.id);
41367 * The text displayed in the tooltip for the close icon.
41370 closeText : "Close this tab"
41373 * This script refer to:
41374 * Title: International Telephone Input
41375 * Author: Jack O'Connor
41376 * Code version: v12.1.12
41377 * Availability: https://github.com/jackocnr/intl-tel-input.git
41380 Roo.bootstrap.PhoneInputData = function() {
41383 "Afghanistan (افغانستان)",
41388 "Albania (Shqipëri)",
41393 "Algeria (الجزائر)",
41418 "Antigua and Barbuda",
41428 "Armenia (Հայաստան)",
41444 "Austria (Österreich)",
41449 "Azerbaijan (Azərbaycan)",
41459 "Bahrain (البحرين)",
41464 "Bangladesh (বাংলাদেশ)",
41474 "Belarus (Беларусь)",
41479 "Belgium (België)",
41509 "Bosnia and Herzegovina (Босна и Херцеговина)",
41524 "British Indian Ocean Territory",
41529 "British Virgin Islands",
41539 "Bulgaria (България)",
41549 "Burundi (Uburundi)",
41554 "Cambodia (កម្ពុជា)",
41559 "Cameroon (Cameroun)",
41568 ["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"]
41571 "Cape Verde (Kabu Verdi)",
41576 "Caribbean Netherlands",
41587 "Central African Republic (République centrafricaine)",
41607 "Christmas Island",
41613 "Cocos (Keeling) Islands",
41624 "Comoros (جزر القمر)",
41629 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41634 "Congo (Republic) (Congo-Brazzaville)",
41654 "Croatia (Hrvatska)",
41675 "Czech Republic (Česká republika)",
41680 "Denmark (Danmark)",
41695 "Dominican Republic (República Dominicana)",
41699 ["809", "829", "849"]
41717 "Equatorial Guinea (Guinea Ecuatorial)",
41737 "Falkland Islands (Islas Malvinas)",
41742 "Faroe Islands (Føroyar)",
41763 "French Guiana (Guyane française)",
41768 "French Polynesia (Polynésie française)",
41783 "Georgia (საქართველო)",
41788 "Germany (Deutschland)",
41808 "Greenland (Kalaallit Nunaat)",
41845 "Guinea-Bissau (Guiné Bissau)",
41870 "Hungary (Magyarország)",
41875 "Iceland (Ísland)",
41895 "Iraq (العراق)",
41911 "Israel (ישראל)",
41938 "Jordan (الأردن)",
41943 "Kazakhstan (Казахстан)",
41964 "Kuwait (الكويت)",
41969 "Kyrgyzstan (Кыргызстан)",
41979 "Latvia (Latvija)",
41984 "Lebanon (لبنان)",
41999 "Libya (ليبيا)",
42009 "Lithuania (Lietuva)",
42024 "Macedonia (FYROM) (Македонија)",
42029 "Madagascar (Madagasikara)",
42059 "Marshall Islands",
42069 "Mauritania (موريتانيا)",
42074 "Mauritius (Moris)",
42095 "Moldova (Republica Moldova)",
42105 "Mongolia (Монгол)",
42110 "Montenegro (Crna Gora)",
42120 "Morocco (المغرب)",
42126 "Mozambique (Moçambique)",
42131 "Myanmar (Burma) (မြန်မာ)",
42136 "Namibia (Namibië)",
42151 "Netherlands (Nederland)",
42156 "New Caledonia (Nouvelle-Calédonie)",
42191 "North Korea (조선 민주주의 인민 공화국)",
42196 "Northern Mariana Islands",
42212 "Pakistan (پاکستان)",
42222 "Palestine (فلسطين)",
42232 "Papua New Guinea",
42274 "Réunion (La Réunion)",
42280 "Romania (România)",
42296 "Saint Barthélemy",
42307 "Saint Kitts and Nevis",
42317 "Saint Martin (Saint-Martin (partie française))",
42323 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42328 "Saint Vincent and the Grenadines",
42343 "São Tomé and Príncipe (São Tomé e Príncipe)",
42348 "Saudi Arabia (المملكة العربية السعودية)",
42353 "Senegal (Sénégal)",
42383 "Slovakia (Slovensko)",
42388 "Slovenia (Slovenija)",
42398 "Somalia (Soomaaliya)",
42408 "South Korea (대한민국)",
42413 "South Sudan (جنوب السودان)",
42423 "Sri Lanka (ශ්රී ලංකාව)",
42428 "Sudan (السودان)",
42438 "Svalbard and Jan Mayen",
42449 "Sweden (Sverige)",
42454 "Switzerland (Schweiz)",
42459 "Syria (سوريا)",
42504 "Trinidad and Tobago",
42509 "Tunisia (تونس)",
42514 "Turkey (Türkiye)",
42524 "Turks and Caicos Islands",
42534 "U.S. Virgin Islands",
42544 "Ukraine (Україна)",
42549 "United Arab Emirates (الإمارات العربية المتحدة)",
42571 "Uzbekistan (Oʻzbekiston)",
42581 "Vatican City (Città del Vaticano)",
42592 "Vietnam (Việt Nam)",
42597 "Wallis and Futuna (Wallis-et-Futuna)",
42602 "Western Sahara (الصحراء الغربية)",
42608 "Yemen (اليمن)",
42632 * This script refer to:
42633 * Title: International Telephone Input
42634 * Author: Jack O'Connor
42635 * Code version: v12.1.12
42636 * Availability: https://github.com/jackocnr/intl-tel-input.git
42640 * @class Roo.bootstrap.PhoneInput
42641 * @extends Roo.bootstrap.TriggerField
42642 * An input with International dial-code selection
42644 * @cfg {String} defaultDialCode default '+852'
42645 * @cfg {Array} preferedCountries default []
42648 * Create a new PhoneInput.
42649 * @param {Object} config Configuration options
42652 Roo.bootstrap.PhoneInput = function(config) {
42653 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42656 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42658 listWidth: undefined,
42660 selectedClass: 'active',
42662 invalidClass : "has-warning",
42664 validClass: 'has-success',
42666 allowed: '0123456789',
42671 * @cfg {String} defaultDialCode The default dial code when initializing the input
42673 defaultDialCode: '+852',
42676 * @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
42678 preferedCountries: false,
42680 getAutoCreate : function()
42682 var data = Roo.bootstrap.PhoneInputData();
42683 var align = this.labelAlign || this.parentLabelAlign();
42686 this.allCountries = [];
42687 this.dialCodeMapping = [];
42689 for (var i = 0; i < data.length; i++) {
42691 this.allCountries[i] = {
42695 priority: c[3] || 0,
42696 areaCodes: c[4] || null
42698 this.dialCodeMapping[c[2]] = {
42701 priority: c[3] || 0,
42702 areaCodes: c[4] || null
42714 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42715 maxlength: this.max_length,
42716 cls : 'form-control tel-input',
42717 autocomplete: 'new-password'
42720 var hiddenInput = {
42723 cls: 'hidden-tel-input'
42727 hiddenInput.name = this.name;
42730 if (this.disabled) {
42731 input.disabled = true;
42734 var flag_container = {
42751 cls: this.hasFeedback ? 'has-feedback' : '',
42757 cls: 'dial-code-holder',
42764 cls: 'roo-select2-container input-group',
42771 if (this.fieldLabel.length) {
42774 tooltip: 'This field is required'
42780 cls: 'control-label',
42786 html: this.fieldLabel
42789 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42795 if(this.indicatorpos == 'right') {
42796 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42803 if(align == 'left') {
42811 if(this.labelWidth > 12){
42812 label.style = "width: " + this.labelWidth + 'px';
42814 if(this.labelWidth < 13 && this.labelmd == 0){
42815 this.labelmd = this.labelWidth;
42817 if(this.labellg > 0){
42818 label.cls += ' col-lg-' + this.labellg;
42819 input.cls += ' col-lg-' + (12 - this.labellg);
42821 if(this.labelmd > 0){
42822 label.cls += ' col-md-' + this.labelmd;
42823 container.cls += ' col-md-' + (12 - this.labelmd);
42825 if(this.labelsm > 0){
42826 label.cls += ' col-sm-' + this.labelsm;
42827 container.cls += ' col-sm-' + (12 - this.labelsm);
42829 if(this.labelxs > 0){
42830 label.cls += ' col-xs-' + this.labelxs;
42831 container.cls += ' col-xs-' + (12 - this.labelxs);
42841 var settings = this;
42843 ['xs','sm','md','lg'].map(function(size){
42844 if (settings[size]) {
42845 cfg.cls += ' col-' + size + '-' + settings[size];
42849 this.store = new Roo.data.Store({
42850 proxy : new Roo.data.MemoryProxy({}),
42851 reader : new Roo.data.JsonReader({
42862 'name' : 'dialCode',
42866 'name' : 'priority',
42870 'name' : 'areaCodes',
42877 if(!this.preferedCountries) {
42878 this.preferedCountries = [
42885 var p = this.preferedCountries.reverse();
42888 for (var i = 0; i < p.length; i++) {
42889 for (var j = 0; j < this.allCountries.length; j++) {
42890 if(this.allCountries[j].iso2 == p[i]) {
42891 var t = this.allCountries[j];
42892 this.allCountries.splice(j,1);
42893 this.allCountries.unshift(t);
42899 this.store.proxy.data = {
42901 data: this.allCountries
42907 initEvents : function()
42910 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42912 this.indicator = this.indicatorEl();
42913 this.flag = this.flagEl();
42914 this.dialCodeHolder = this.dialCodeHolderEl();
42916 this.trigger = this.el.select('div.flag-box',true).first();
42917 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42922 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42923 _this.list.setWidth(lw);
42926 this.list.on('mouseover', this.onViewOver, this);
42927 this.list.on('mousemove', this.onViewMove, this);
42928 this.inputEl().on("keyup", this.onKeyUp, this);
42929 this.inputEl().on("keypress", this.onKeyPress, this);
42931 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42933 this.view = new Roo.View(this.list, this.tpl, {
42934 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42937 this.view.on('click', this.onViewClick, this);
42938 this.setValue(this.defaultDialCode);
42941 onTriggerClick : function(e)
42943 Roo.log('trigger click');
42948 if(this.isExpanded()){
42950 this.hasFocus = false;
42952 this.store.load({});
42953 this.hasFocus = true;
42958 isExpanded : function()
42960 return this.list.isVisible();
42963 collapse : function()
42965 if(!this.isExpanded()){
42969 Roo.get(document).un('mousedown', this.collapseIf, this);
42970 Roo.get(document).un('mousewheel', this.collapseIf, this);
42971 this.fireEvent('collapse', this);
42975 expand : function()
42979 if(this.isExpanded() || !this.hasFocus){
42983 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42984 this.list.setWidth(lw);
42987 this.restrictHeight();
42989 Roo.get(document).on('mousedown', this.collapseIf, this);
42990 Roo.get(document).on('mousewheel', this.collapseIf, this);
42992 this.fireEvent('expand', this);
42995 restrictHeight : function()
42997 this.list.alignTo(this.inputEl(), this.listAlign);
42998 this.list.alignTo(this.inputEl(), this.listAlign);
43001 onViewOver : function(e, t)
43003 if(this.inKeyMode){
43006 var item = this.view.findItemFromChild(t);
43009 var index = this.view.indexOf(item);
43010 this.select(index, false);
43015 onViewClick : function(view, doFocus, el, e)
43017 var index = this.view.getSelectedIndexes()[0];
43019 var r = this.store.getAt(index);
43022 this.onSelect(r, index);
43024 if(doFocus !== false && !this.blockFocus){
43025 this.inputEl().focus();
43029 onViewMove : function(e, t)
43031 this.inKeyMode = false;
43034 select : function(index, scrollIntoView)
43036 this.selectedIndex = index;
43037 this.view.select(index);
43038 if(scrollIntoView !== false){
43039 var el = this.view.getNode(index);
43041 this.list.scrollChildIntoView(el, false);
43046 createList : function()
43048 this.list = Roo.get(document.body).createChild({
43050 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43051 style: 'display:none'
43054 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43057 collapseIf : function(e)
43059 var in_combo = e.within(this.el);
43060 var in_list = e.within(this.list);
43061 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43063 if (in_combo || in_list || is_list) {
43069 onSelect : function(record, index)
43071 if(this.fireEvent('beforeselect', this, record, index) !== false){
43073 this.setFlagClass(record.data.iso2);
43074 this.setDialCode(record.data.dialCode);
43075 this.hasFocus = false;
43077 this.fireEvent('select', this, record, index);
43081 flagEl : function()
43083 var flag = this.el.select('div.flag',true).first();
43090 dialCodeHolderEl : function()
43092 var d = this.el.select('input.dial-code-holder',true).first();
43099 setDialCode : function(v)
43101 this.dialCodeHolder.dom.value = '+'+v;
43104 setFlagClass : function(n)
43106 this.flag.dom.className = 'flag '+n;
43109 getValue : function()
43111 var v = this.inputEl().getValue();
43112 if(this.dialCodeHolder) {
43113 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43118 setValue : function(v)
43120 var d = this.getDialCode(v);
43122 //invalid dial code
43123 if(v.length == 0 || !d || d.length == 0) {
43125 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43126 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43132 this.setFlagClass(this.dialCodeMapping[d].iso2);
43133 this.setDialCode(d);
43134 this.inputEl().dom.value = v.replace('+'+d,'');
43135 this.hiddenEl().dom.value = this.getValue();
43140 getDialCode : function(v)
43144 if (v.length == 0) {
43145 return this.dialCodeHolder.dom.value;
43149 if (v.charAt(0) != "+") {
43152 var numericChars = "";
43153 for (var i = 1; i < v.length; i++) {
43154 var c = v.charAt(i);
43157 if (this.dialCodeMapping[numericChars]) {
43158 dialCode = v.substr(1, i);
43160 if (numericChars.length == 4) {
43170 this.setValue(this.defaultDialCode);
43174 hiddenEl : function()
43176 return this.el.select('input.hidden-tel-input',true).first();
43179 // after setting val
43180 onKeyUp : function(e){
43181 this.setValue(this.getValue());
43184 onKeyPress : function(e){
43185 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43192 * @class Roo.bootstrap.MoneyField
43193 * @extends Roo.bootstrap.ComboBox
43194 * Bootstrap MoneyField class
43197 * Create a new MoneyField.
43198 * @param {Object} config Configuration options
43201 Roo.bootstrap.MoneyField = function(config) {
43203 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43207 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43210 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43212 allowDecimals : true,
43214 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43216 decimalSeparator : ".",
43218 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43220 decimalPrecision : 0,
43222 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43224 allowNegative : true,
43226 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43230 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43232 minValue : Number.NEGATIVE_INFINITY,
43234 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43236 maxValue : Number.MAX_VALUE,
43238 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43240 minText : "The minimum value for this field is {0}",
43242 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43244 maxText : "The maximum value for this field is {0}",
43246 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43247 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43249 nanText : "{0} is not a valid number",
43251 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43255 * @cfg {String} defaults currency of the MoneyField
43256 * value should be in lkey
43258 defaultCurrency : false,
43260 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43262 thousandsDelimiter : false,
43264 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43275 getAutoCreate : function()
43277 var align = this.labelAlign || this.parentLabelAlign();
43289 cls : 'form-control roo-money-amount-input',
43290 autocomplete: 'new-password'
43293 var hiddenInput = {
43297 cls: 'hidden-number-input'
43300 if(this.max_length) {
43301 input.maxlength = this.max_length;
43305 hiddenInput.name = this.name;
43308 if (this.disabled) {
43309 input.disabled = true;
43312 var clg = 12 - this.inputlg;
43313 var cmd = 12 - this.inputmd;
43314 var csm = 12 - this.inputsm;
43315 var cxs = 12 - this.inputxs;
43319 cls : 'row roo-money-field',
43323 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43327 cls: 'roo-select2-container input-group',
43331 cls : 'form-control roo-money-currency-input',
43332 autocomplete: 'new-password',
43334 name : this.currencyName
43338 cls : 'input-group-addon',
43352 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43356 cls: this.hasFeedback ? 'has-feedback' : '',
43367 if (this.fieldLabel.length) {
43370 tooltip: 'This field is required'
43376 cls: 'control-label',
43382 html: this.fieldLabel
43385 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43391 if(this.indicatorpos == 'right') {
43392 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43399 if(align == 'left') {
43407 if(this.labelWidth > 12){
43408 label.style = "width: " + this.labelWidth + 'px';
43410 if(this.labelWidth < 13 && this.labelmd == 0){
43411 this.labelmd = this.labelWidth;
43413 if(this.labellg > 0){
43414 label.cls += ' col-lg-' + this.labellg;
43415 input.cls += ' col-lg-' + (12 - this.labellg);
43417 if(this.labelmd > 0){
43418 label.cls += ' col-md-' + this.labelmd;
43419 container.cls += ' col-md-' + (12 - this.labelmd);
43421 if(this.labelsm > 0){
43422 label.cls += ' col-sm-' + this.labelsm;
43423 container.cls += ' col-sm-' + (12 - this.labelsm);
43425 if(this.labelxs > 0){
43426 label.cls += ' col-xs-' + this.labelxs;
43427 container.cls += ' col-xs-' + (12 - this.labelxs);
43438 var settings = this;
43440 ['xs','sm','md','lg'].map(function(size){
43441 if (settings[size]) {
43442 cfg.cls += ' col-' + size + '-' + settings[size];
43449 initEvents : function()
43451 this.indicator = this.indicatorEl();
43453 this.initCurrencyEvent();
43455 this.initNumberEvent();
43458 initCurrencyEvent : function()
43461 throw "can not find store for combo";
43464 this.store = Roo.factory(this.store, Roo.data);
43465 this.store.parent = this;
43469 this.triggerEl = this.el.select('.input-group-addon', true).first();
43471 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43476 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43477 _this.list.setWidth(lw);
43480 this.list.on('mouseover', this.onViewOver, this);
43481 this.list.on('mousemove', this.onViewMove, this);
43482 this.list.on('scroll', this.onViewScroll, this);
43485 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43488 this.view = new Roo.View(this.list, this.tpl, {
43489 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43492 this.view.on('click', this.onViewClick, this);
43494 this.store.on('beforeload', this.onBeforeLoad, this);
43495 this.store.on('load', this.onLoad, this);
43496 this.store.on('loadexception', this.onLoadException, this);
43498 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43499 "up" : function(e){
43500 this.inKeyMode = true;
43504 "down" : function(e){
43505 if(!this.isExpanded()){
43506 this.onTriggerClick();
43508 this.inKeyMode = true;
43513 "enter" : function(e){
43516 if(this.fireEvent("specialkey", this, e)){
43517 this.onViewClick(false);
43523 "esc" : function(e){
43527 "tab" : function(e){
43530 if(this.fireEvent("specialkey", this, e)){
43531 this.onViewClick(false);
43539 doRelay : function(foo, bar, hname){
43540 if(hname == 'down' || this.scope.isExpanded()){
43541 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43549 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43553 initNumberEvent : function(e)
43555 this.inputEl().on("keydown" , this.fireKey, this);
43556 this.inputEl().on("focus", this.onFocus, this);
43557 this.inputEl().on("blur", this.onBlur, this);
43559 this.inputEl().relayEvent('keyup', this);
43561 if(this.indicator){
43562 this.indicator.addClass('invisible');
43565 this.originalValue = this.getValue();
43567 if(this.validationEvent == 'keyup'){
43568 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43569 this.inputEl().on('keyup', this.filterValidation, this);
43571 else if(this.validationEvent !== false){
43572 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43575 if(this.selectOnFocus){
43576 this.on("focus", this.preFocus, this);
43579 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43580 this.inputEl().on("keypress", this.filterKeys, this);
43582 this.inputEl().relayEvent('keypress', this);
43585 var allowed = "0123456789";
43587 if(this.allowDecimals){
43588 allowed += this.decimalSeparator;
43591 if(this.allowNegative){
43595 if(this.thousandsDelimiter) {
43599 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43601 var keyPress = function(e){
43603 var k = e.getKey();
43605 var c = e.getCharCode();
43608 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43609 allowed.indexOf(String.fromCharCode(c)) === -1
43615 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43619 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43624 this.inputEl().on("keypress", keyPress, this);
43628 onTriggerClick : function(e)
43635 this.loadNext = false;
43637 if(this.isExpanded()){
43642 this.hasFocus = true;
43644 if(this.triggerAction == 'all') {
43645 this.doQuery(this.allQuery, true);
43649 this.doQuery(this.getRawValue());
43652 getCurrency : function()
43654 var v = this.currencyEl().getValue();
43659 restrictHeight : function()
43661 this.list.alignTo(this.currencyEl(), this.listAlign);
43662 this.list.alignTo(this.currencyEl(), this.listAlign);
43665 onViewClick : function(view, doFocus, el, e)
43667 var index = this.view.getSelectedIndexes()[0];
43669 var r = this.store.getAt(index);
43672 this.onSelect(r, index);
43676 onSelect : function(record, index){
43678 if(this.fireEvent('beforeselect', this, record, index) !== false){
43680 this.setFromCurrencyData(index > -1 ? record.data : false);
43684 this.fireEvent('select', this, record, index);
43688 setFromCurrencyData : function(o)
43692 this.lastCurrency = o;
43694 if (this.currencyField) {
43695 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43697 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43700 this.lastSelectionText = currency;
43702 //setting default currency
43703 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43704 this.setCurrency(this.defaultCurrency);
43708 this.setCurrency(currency);
43711 setFromData : function(o)
43715 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43717 this.setFromCurrencyData(c);
43722 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43724 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43727 this.setValue(value);
43731 setCurrency : function(v)
43733 this.currencyValue = v;
43736 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43741 setValue : function(v)
43743 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43749 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43751 this.inputEl().dom.value = (v == '') ? '' :
43752 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43754 if(!this.allowZero && v === '0') {
43755 this.hiddenEl().dom.value = '';
43756 this.inputEl().dom.value = '';
43763 getRawValue : function()
43765 var v = this.inputEl().getValue();
43770 getValue : function()
43772 return this.fixPrecision(this.parseValue(this.getRawValue()));
43775 parseValue : function(value)
43777 if(this.thousandsDelimiter) {
43779 r = new RegExp(",", "g");
43780 value = value.replace(r, "");
43783 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43784 return isNaN(value) ? '' : value;
43788 fixPrecision : function(value)
43790 if(this.thousandsDelimiter) {
43792 r = new RegExp(",", "g");
43793 value = value.replace(r, "");
43796 var nan = isNaN(value);
43798 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43799 return nan ? '' : value;
43801 return parseFloat(value).toFixed(this.decimalPrecision);
43804 decimalPrecisionFcn : function(v)
43806 return Math.floor(v);
43809 validateValue : function(value)
43811 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43815 var num = this.parseValue(value);
43818 this.markInvalid(String.format(this.nanText, value));
43822 if(num < this.minValue){
43823 this.markInvalid(String.format(this.minText, this.minValue));
43827 if(num > this.maxValue){
43828 this.markInvalid(String.format(this.maxText, this.maxValue));
43835 validate : function()
43837 if(this.disabled || this.allowBlank){
43842 var currency = this.getCurrency();
43844 if(this.validateValue(this.getRawValue()) && currency.length){
43849 this.markInvalid();
43853 getName: function()
43858 beforeBlur : function()
43864 var v = this.parseValue(this.getRawValue());
43871 onBlur : function()
43875 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43876 //this.el.removeClass(this.focusClass);
43879 this.hasFocus = false;
43881 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43885 var v = this.getValue();
43887 if(String(v) !== String(this.startValue)){
43888 this.fireEvent('change', this, v, this.startValue);
43891 this.fireEvent("blur", this);
43894 inputEl : function()
43896 return this.el.select('.roo-money-amount-input', true).first();
43899 currencyEl : function()
43901 return this.el.select('.roo-money-currency-input', true).first();
43904 hiddenEl : function()
43906 return this.el.select('input.hidden-number-input',true).first();
43910 * @class Roo.bootstrap.BezierSignature
43911 * @extends Roo.bootstrap.Component
43912 * Bootstrap BezierSignature class
43913 * This script refer to:
43914 * Title: Signature Pad
43916 * Availability: https://github.com/szimek/signature_pad
43919 * Create a new BezierSignature
43920 * @param {Object} config The config object
43923 Roo.bootstrap.BezierSignature = function(config){
43924 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43930 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43937 mouse_btn_down: true,
43940 * @cfg {int} canvas height
43942 canvas_height: '200px',
43945 * @cfg {float|function} Radius of a single dot.
43950 * @cfg {float} Minimum width of a line. Defaults to 0.5.
43955 * @cfg {float} Maximum width of a line. Defaults to 2.5.
43960 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43965 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43970 * @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.
43972 bg_color: 'rgba(0, 0, 0, 0)',
43975 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43977 dot_color: 'black',
43980 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43982 velocity_filter_weight: 0.7,
43985 * @cfg {function} Callback when stroke begin.
43990 * @cfg {function} Callback when stroke end.
43994 getAutoCreate : function()
43996 var cls = 'roo-signature column';
43999 cls += ' ' + this.cls;
44009 for(var i = 0; i < col_sizes.length; i++) {
44010 if(this[col_sizes[i]]) {
44011 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44021 cls: 'roo-signature-body',
44025 cls: 'roo-signature-body-canvas',
44026 height: this.canvas_height,
44027 width: this.canvas_width
44034 style: 'display: none'
44042 initEvents: function()
44044 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44046 var canvas = this.canvasEl();
44048 // mouse && touch event swapping...
44049 canvas.dom.style.touchAction = 'none';
44050 canvas.dom.style.msTouchAction = 'none';
44052 this.mouse_btn_down = false;
44053 canvas.on('mousedown', this._handleMouseDown, this);
44054 canvas.on('mousemove', this._handleMouseMove, this);
44055 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44057 if (window.PointerEvent) {
44058 canvas.on('pointerdown', this._handleMouseDown, this);
44059 canvas.on('pointermove', this._handleMouseMove, this);
44060 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44063 if ('ontouchstart' in window) {
44064 canvas.on('touchstart', this._handleTouchStart, this);
44065 canvas.on('touchmove', this._handleTouchMove, this);
44066 canvas.on('touchend', this._handleTouchEnd, this);
44069 Roo.EventManager.onWindowResize(this.resize, this, true);
44071 // file input event
44072 this.fileEl().on('change', this.uploadImage, this);
44079 resize: function(){
44081 var canvas = this.canvasEl().dom;
44082 var ctx = this.canvasElCtx();
44083 var img_data = false;
44085 if(canvas.width > 0) {
44086 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44088 // setting canvas width will clean img data
44091 var style = window.getComputedStyle ?
44092 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44094 var padding_left = parseInt(style.paddingLeft) || 0;
44095 var padding_right = parseInt(style.paddingRight) || 0;
44097 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44100 ctx.putImageData(img_data, 0, 0);
44104 _handleMouseDown: function(e)
44106 if (e.browserEvent.which === 1) {
44107 this.mouse_btn_down = true;
44108 this.strokeBegin(e);
44112 _handleMouseMove: function (e)
44114 if (this.mouse_btn_down) {
44115 this.strokeMoveUpdate(e);
44119 _handleMouseUp: function (e)
44121 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44122 this.mouse_btn_down = false;
44127 _handleTouchStart: function (e) {
44129 e.preventDefault();
44130 if (e.browserEvent.targetTouches.length === 1) {
44131 // var touch = e.browserEvent.changedTouches[0];
44132 // this.strokeBegin(touch);
44134 this.strokeBegin(e); // assume e catching the correct xy...
44138 _handleTouchMove: function (e) {
44139 e.preventDefault();
44140 // var touch = event.targetTouches[0];
44141 // _this._strokeMoveUpdate(touch);
44142 this.strokeMoveUpdate(e);
44145 _handleTouchEnd: function (e) {
44146 var wasCanvasTouched = e.target === this.canvasEl().dom;
44147 if (wasCanvasTouched) {
44148 e.preventDefault();
44149 // var touch = event.changedTouches[0];
44150 // _this._strokeEnd(touch);
44155 reset: function () {
44156 this._lastPoints = [];
44157 this._lastVelocity = 0;
44158 this._lastWidth = (this.min_width + this.max_width) / 2;
44159 this.canvasElCtx().fillStyle = this.dot_color;
44162 strokeMoveUpdate: function(e)
44164 this.strokeUpdate(e);
44166 if (this.throttle) {
44167 this.throttleStroke(this.strokeUpdate, this.throttle);
44170 this.strokeUpdate(e);
44174 strokeBegin: function(e)
44176 var newPointGroup = {
44177 color: this.dot_color,
44181 if (typeof this.onBegin === 'function') {
44185 this.curve_data.push(newPointGroup);
44187 this.strokeUpdate(e);
44190 strokeUpdate: function(e)
44192 var rect = this.canvasEl().dom.getBoundingClientRect();
44193 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44194 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44195 var lastPoints = lastPointGroup.points;
44196 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44197 var isLastPointTooClose = lastPoint
44198 ? point.distanceTo(lastPoint) <= this.min_distance
44200 var color = lastPointGroup.color;
44201 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44202 var curve = this.addPoint(point);
44204 this.drawDot({color: color, point: point});
44207 this.drawCurve({color: color, curve: curve});
44217 strokeEnd: function(e)
44219 this.strokeUpdate(e);
44220 if (typeof this.onEnd === 'function') {
44225 addPoint: function (point) {
44226 var _lastPoints = this._lastPoints;
44227 _lastPoints.push(point);
44228 if (_lastPoints.length > 2) {
44229 if (_lastPoints.length === 3) {
44230 _lastPoints.unshift(_lastPoints[0]);
44232 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44233 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44234 _lastPoints.shift();
44240 calculateCurveWidths: function (startPoint, endPoint) {
44241 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44242 (1 - this.velocity_filter_weight) * this._lastVelocity;
44244 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44247 start: this._lastWidth
44250 this._lastVelocity = velocity;
44251 this._lastWidth = newWidth;
44255 drawDot: function (_a) {
44256 var color = _a.color, point = _a.point;
44257 var ctx = this.canvasElCtx();
44258 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44260 this.drawCurveSegment(point.x, point.y, width);
44262 ctx.fillStyle = color;
44266 drawCurve: function (_a) {
44267 var color = _a.color, curve = _a.curve;
44268 var ctx = this.canvasElCtx();
44269 var widthDelta = curve.endWidth - curve.startWidth;
44270 var drawSteps = Math.floor(curve.length()) * 2;
44272 ctx.fillStyle = color;
44273 for (var i = 0; i < drawSteps; i += 1) {
44274 var t = i / drawSteps;
44280 var x = uuu * curve.startPoint.x;
44281 x += 3 * uu * t * curve.control1.x;
44282 x += 3 * u * tt * curve.control2.x;
44283 x += ttt * curve.endPoint.x;
44284 var y = uuu * curve.startPoint.y;
44285 y += 3 * uu * t * curve.control1.y;
44286 y += 3 * u * tt * curve.control2.y;
44287 y += ttt * curve.endPoint.y;
44288 var width = curve.startWidth + ttt * widthDelta;
44289 this.drawCurveSegment(x, y, width);
44295 drawCurveSegment: function (x, y, width) {
44296 var ctx = this.canvasElCtx();
44298 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44299 this.is_empty = false;
44304 var ctx = this.canvasElCtx();
44305 var canvas = this.canvasEl().dom;
44306 ctx.fillStyle = this.bg_color;
44307 ctx.clearRect(0, 0, canvas.width, canvas.height);
44308 ctx.fillRect(0, 0, canvas.width, canvas.height);
44309 this.curve_data = [];
44311 this.is_empty = true;
44316 return this.el.select('input',true).first();
44319 canvasEl: function()
44321 return this.el.select('canvas',true).first();
44324 canvasElCtx: function()
44326 return this.el.select('canvas',true).first().dom.getContext('2d');
44329 getImage: function(type)
44331 if(this.is_empty) {
44336 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44339 drawFromImage: function(img_src)
44341 var img = new Image();
44343 img.onload = function(){
44344 this.canvasElCtx().drawImage(img, 0, 0);
44349 this.is_empty = false;
44352 selectImage: function()
44354 this.fileEl().dom.click();
44357 uploadImage: function(e)
44359 var reader = new FileReader();
44361 reader.onload = function(e){
44362 var img = new Image();
44363 img.onload = function(){
44365 this.canvasElCtx().drawImage(img, 0, 0);
44367 img.src = e.target.result;
44370 reader.readAsDataURL(e.target.files[0]);
44373 // Bezier Point Constructor
44374 Point: (function () {
44375 function Point(x, y, time) {
44378 this.time = time || Date.now();
44380 Point.prototype.distanceTo = function (start) {
44381 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44383 Point.prototype.equals = function (other) {
44384 return this.x === other.x && this.y === other.y && this.time === other.time;
44386 Point.prototype.velocityFrom = function (start) {
44387 return this.time !== start.time
44388 ? this.distanceTo(start) / (this.time - start.time)
44395 // Bezier Constructor
44396 Bezier: (function () {
44397 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44398 this.startPoint = startPoint;
44399 this.control2 = control2;
44400 this.control1 = control1;
44401 this.endPoint = endPoint;
44402 this.startWidth = startWidth;
44403 this.endWidth = endWidth;
44405 Bezier.fromPoints = function (points, widths, scope) {
44406 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44407 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44408 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44410 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44411 var dx1 = s1.x - s2.x;
44412 var dy1 = s1.y - s2.y;
44413 var dx2 = s2.x - s3.x;
44414 var dy2 = s2.y - s3.y;
44415 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44416 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44417 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44418 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44419 var dxm = m1.x - m2.x;
44420 var dym = m1.y - m2.y;
44421 var k = l2 / (l1 + l2);
44422 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44423 var tx = s2.x - cm.x;
44424 var ty = s2.y - cm.y;
44426 c1: new scope.Point(m1.x + tx, m1.y + ty),
44427 c2: new scope.Point(m2.x + tx, m2.y + ty)
44430 Bezier.prototype.length = function () {
44435 for (var i = 0; i <= steps; i += 1) {
44437 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44438 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44440 var xdiff = cx - px;
44441 var ydiff = cy - py;
44442 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44449 Bezier.prototype.point = function (t, start, c1, c2, end) {
44450 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44451 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44452 + (3.0 * c2 * (1.0 - t) * t * t)
44453 + (end * t * t * t);
44458 throttleStroke: function(fn, wait) {
44459 if (wait === void 0) { wait = 250; }
44461 var timeout = null;
44465 var later = function () {
44466 previous = Date.now();
44468 result = fn.apply(storedContext, storedArgs);
44470 storedContext = null;
44474 return function wrapper() {
44476 for (var _i = 0; _i < arguments.length; _i++) {
44477 args[_i] = arguments[_i];
44479 var now = Date.now();
44480 var remaining = wait - (now - previous);
44481 storedContext = this;
44483 if (remaining <= 0 || remaining > wait) {
44485 clearTimeout(timeout);
44489 result = fn.apply(storedContext, storedArgs);
44491 storedContext = null;
44495 else if (!timeout) {
44496 timeout = window.setTimeout(later, remaining);