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');
1357 this.el.dom.removeAttribute("disabled");
1361 * Disable this button
1363 disable : function()
1365 this.disabled = true;
1366 this.el.addClass('disabled');
1367 this.el.attr("disabled", "disabled")
1370 * sets the active state on/off,
1371 * @param {Boolean} state (optional) Force a particular state
1373 setActive : function(v) {
1375 this.el[v ? 'addClass' : 'removeClass']('active');
1379 * toggles the current active state
1381 toggleActive : function(e)
1383 this.setActive(!this.pressed); // this modifies pressed...
1384 this.fireEvent('toggle', this, e, this.pressed);
1387 * get the current active state
1388 * @return {boolean} true if it's active
1390 isActive : function()
1392 return this.el.hasClass('active');
1395 * set the text of the first selected button
1397 setText : function(str)
1399 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1402 * get the text of the first selected button
1404 getText : function()
1406 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1409 setWeight : function(str)
1411 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1412 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1414 var outline = this.outline ? 'outline-' : '';
1415 if (str == 'default') {
1416 this.el.addClass('btn-default btn-outline-secondary');
1419 this.el.addClass('btn-' + outline + str);
1424 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1426 Roo.bootstrap.Button.weights = [
1446 * @class Roo.bootstrap.Column
1447 * @extends Roo.bootstrap.Component
1448 * Bootstrap Column class
1449 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1450 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1451 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1452 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1453 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1454 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1455 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1456 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1459 * @cfg {Boolean} hidden (true|false) hide the element
1460 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1461 * @cfg {String} fa (ban|check|...) font awesome icon
1462 * @cfg {Number} fasize (1|2|....) font awsome size
1464 * @cfg {String} icon (info-sign|check|...) glyphicon name
1466 * @cfg {String} html content of column.
1469 * Create a new Column
1470 * @param {Object} config The config object
1473 Roo.bootstrap.Column = function(config){
1474 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1477 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1495 getAutoCreate : function(){
1496 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1504 var sizes = ['xs','sm','md','lg'];
1505 sizes.map(function(size ,ix){
1506 //Roo.log( size + ':' + settings[size]);
1508 if (settings[size+'off'] !== false) {
1509 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1512 if (settings[size] === false) {
1516 if (!settings[size]) { // 0 = hidden
1517 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1519 for (var i = ix; i > -1; i--) {
1520 cfg.cls += ' d-' + sizes[i] + '-none';
1526 cfg.cls += ' col-' + size + '-' + settings[size] + (
1527 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1533 cfg.cls += ' hidden';
1536 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1537 cfg.cls +=' alert alert-' + this.alert;
1541 if (this.html.length) {
1542 cfg.html = this.html;
1546 if (this.fasize > 1) {
1547 fasize = ' fa-' + this.fasize + 'x';
1549 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1554 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1573 * @class Roo.bootstrap.Container
1574 * @extends Roo.bootstrap.Component
1575 * Bootstrap Container class
1576 * @cfg {Boolean} jumbotron is it a jumbotron element
1577 * @cfg {String} html content of element
1578 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1579 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1580 * @cfg {String} header content of header (for panel)
1581 * @cfg {String} footer content of footer (for panel)
1582 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1583 * @cfg {String} tag (header|aside|section) type of HTML tag.
1584 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1585 * @cfg {String} fa font awesome icon
1586 * @cfg {String} icon (info-sign|check|...) glyphicon name
1587 * @cfg {Boolean} hidden (true|false) hide the element
1588 * @cfg {Boolean} expandable (true|false) default false
1589 * @cfg {Boolean} expanded (true|false) default true
1590 * @cfg {String} rheader contet on the right of header
1591 * @cfg {Boolean} clickable (true|false) default false
1595 * Create a new Container
1596 * @param {Object} config The config object
1599 Roo.bootstrap.Container = function(config){
1600 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1606 * After the panel has been expand
1608 * @param {Roo.bootstrap.Container} this
1613 * After the panel has been collapsed
1615 * @param {Roo.bootstrap.Container} this
1620 * When a element is chick
1621 * @param {Roo.bootstrap.Container} this
1622 * @param {Roo.EventObject} e
1628 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1646 getChildContainer : function() {
1652 if (this.panel.length) {
1653 return this.el.select('.panel-body',true).first();
1660 getAutoCreate : function(){
1663 tag : this.tag || 'div',
1667 if (this.jumbotron) {
1668 cfg.cls = 'jumbotron';
1673 // - this is applied by the parent..
1675 // cfg.cls = this.cls + '';
1678 if (this.sticky.length) {
1680 var bd = Roo.get(document.body);
1681 if (!bd.hasClass('bootstrap-sticky')) {
1682 bd.addClass('bootstrap-sticky');
1683 Roo.select('html',true).setStyle('height', '100%');
1686 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1690 if (this.well.length) {
1691 switch (this.well) {
1694 cfg.cls +=' well well-' +this.well;
1703 cfg.cls += ' hidden';
1707 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1708 cfg.cls +=' alert alert-' + this.alert;
1713 if (this.panel.length) {
1714 cfg.cls += ' panel panel-' + this.panel;
1716 if (this.header.length) {
1720 if(this.expandable){
1722 cfg.cls = cfg.cls + ' expandable';
1726 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1734 cls : 'panel-title',
1735 html : (this.expandable ? ' ' : '') + this.header
1739 cls: 'panel-header-right',
1745 cls : 'panel-heading',
1746 style : this.expandable ? 'cursor: pointer' : '',
1754 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1759 if (this.footer.length) {
1761 cls : 'panel-footer',
1770 body.html = this.html || cfg.html;
1771 // prefix with the icons..
1773 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1776 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1781 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1782 cfg.cls = 'container';
1788 initEvents: function()
1790 if(this.expandable){
1791 var headerEl = this.headerEl();
1794 headerEl.on('click', this.onToggleClick, this);
1799 this.el.on('click', this.onClick, this);
1804 onToggleClick : function()
1806 var headerEl = this.headerEl();
1822 if(this.fireEvent('expand', this)) {
1824 this.expanded = true;
1826 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1828 this.el.select('.panel-body',true).first().removeClass('hide');
1830 var toggleEl = this.toggleEl();
1836 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1841 collapse : function()
1843 if(this.fireEvent('collapse', this)) {
1845 this.expanded = false;
1847 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1848 this.el.select('.panel-body',true).first().addClass('hide');
1850 var toggleEl = this.toggleEl();
1856 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1860 toggleEl : function()
1862 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1866 return this.el.select('.panel-heading .fa',true).first();
1869 headerEl : function()
1871 if(!this.el || !this.panel.length || !this.header.length){
1875 return this.el.select('.panel-heading',true).first()
1880 if(!this.el || !this.panel.length){
1884 return this.el.select('.panel-body',true).first()
1887 titleEl : function()
1889 if(!this.el || !this.panel.length || !this.header.length){
1893 return this.el.select('.panel-title',true).first();
1896 setTitle : function(v)
1898 var titleEl = this.titleEl();
1904 titleEl.dom.innerHTML = v;
1907 getTitle : function()
1910 var titleEl = this.titleEl();
1916 return titleEl.dom.innerHTML;
1919 setRightTitle : function(v)
1921 var t = this.el.select('.panel-header-right',true).first();
1927 t.dom.innerHTML = v;
1930 onClick : function(e)
1934 this.fireEvent('click', this, e);
1941 * This is BS4's Card element.. - similar to our containers probably..
1945 * @class Roo.bootstrap.Card
1946 * @extends Roo.bootstrap.Component
1947 * Bootstrap Card class
1950 * possible... may not be implemented..
1951 * @cfg {String} header_image src url of image.
1952 * @cfg {String|Object} header
1953 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1954 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1956 * @cfg {String} title
1957 * @cfg {String} subtitle
1958 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1959 * @cfg {String} footer
1961 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1963 * @cfg {String} margin (0|1|2|3|4|5|auto)
1964 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1965 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1966 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1967 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1968 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1969 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1971 * @cfg {String} padding (0|1|2|3|4|5)
1972 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1973 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1974 * @cfg {String} padding_left (0|1|2|3|4|5)
1975 * @cfg {String} padding_right (0|1|2|3|4|5)
1976 * @cfg {String} padding_x (0|1|2|3|4|5)
1977 * @cfg {String} padding_y (0|1|2|3|4|5)
1979 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1983 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1985 * @config {Boolean} dragable if this card can be dragged.
1986 * @config {String} drag_group group for drag
1987 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1988 * @config {String} drop_group group for drag
1990 * @config {Boolean} collapsable can the body be collapsed.
1991 * @config {Boolean} collapsed is the body collapsed when rendered...
1992 * @config {Boolean} rotateable can the body be rotated by clicking on it..
1993 * @config {Boolean} rotated is the body rotated when rendered...
1996 * Create a new Container
1997 * @param {Object} config The config object
2000 Roo.bootstrap.Card = function(config){
2001 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2007 * When a element a card is dropped
2008 * @param {Roo.bootstrap.Card} this
2011 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2012 * @param {String} position 'above' or 'below'
2013 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2019 * When a element a card is rotate
2020 * @param {Roo.bootstrap.Card} this
2021 * @param {Roo.Element} n the node being dropped?
2022 * @param {Boolean} rotate status
2027 * When a card element is dragged over ready to drop (return false to block dropable)
2028 * @param {Roo.bootstrap.Card} this
2029 * @param {Object} data from dragdrop
2037 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2042 margin: '', /// may be better in component?
2072 collapsable : false,
2081 childContainer : false,
2082 dropEl : false, /// the dom placeholde element that indicates drop location.
2083 containerEl: false, // body container
2084 bodyEl: false, // card-body
2085 headerContainerEl : false, //
2087 header_imageEl : false,
2089 layoutCls : function()
2093 Roo.log(this.margin_bottom.length);
2094 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2095 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2097 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2098 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2100 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2101 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2105 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2106 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2107 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2111 // more generic support?
2119 // Roo.log("Call onRender: " + this.xtype);
2120 /* We are looking at something like this.
2122 <img src="..." class="card-img-top" alt="...">
2123 <div class="card-body">
2124 <h5 class="card-title">Card title</h5>
2125 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2127 >> this bit is really the body...
2128 <div> << we will ad dthis in hopefully it will not break shit.
2130 ** card text does not actually have any styling...
2132 <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>
2135 <a href="#" class="card-link">Card link</a>
2138 <div class="card-footer">
2139 <small class="text-muted">Last updated 3 mins ago</small>
2143 getAutoCreate : function(){
2151 if (this.weight.length && this.weight != 'light') {
2152 cfg.cls += ' text-white';
2154 cfg.cls += ' text-dark'; // need as it's nested..
2156 if (this.weight.length) {
2157 cfg.cls += ' bg-' + this.weight;
2160 cfg.cls += ' ' + this.layoutCls();
2163 var hdr_ctr = false;
2164 if (this.header.length) {
2166 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2167 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2175 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2181 if (this.collapsable) {
2184 cls : 'd-block user-select-none',
2188 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2193 hdr.cn.push(hdr_ctr);
2198 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2203 if (this.header_image.length) {
2206 cls : 'card-img-top',
2207 src: this.header_image // escape?
2212 cls : 'card-img-top d-none'
2218 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2222 if (this.collapsable || this.rotateable) {
2225 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2232 if (this.title.length) {
2236 src: this.title // escape?
2240 if (this.subtitle.length) {
2244 src: this.subtitle // escape?
2250 cls : 'roo-card-body-ctr'
2253 if (this.html.length) {
2259 // fixme ? handle objects?
2261 if (this.footer.length) {
2264 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2269 cfg.cn.push({cls : 'card-footer d-none'});
2278 getCardHeader : function()
2280 var ret = this.el.select('.card-header',true).first();
2281 if (ret.hasClass('d-none')) {
2282 ret.removeClass('d-none');
2287 getCardFooter : function()
2289 var ret = this.el.select('.card-footer',true).first();
2290 if (ret.hasClass('d-none')) {
2291 ret.removeClass('d-none');
2296 getCardImageTop : function()
2298 var ret = this.header_imageEl;
2299 if (ret.hasClass('d-none')) {
2300 ret.removeClass('d-none');
2306 getChildContainer : function()
2312 return this.el.select('.roo-card-body-ctr',true).first();
2315 initEvents: function()
2317 this.bodyEl = this.el.select('.card-body',true).first();
2318 this.containerEl = this.getChildContainer();
2320 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2321 containerScroll: true,
2322 ddGroup: this.drag_group || 'default_card_drag_group'
2324 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2326 if (this.dropable) {
2327 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2328 containerScroll: true,
2329 ddGroup: this.drop_group || 'default_card_drag_group'
2331 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2332 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2333 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2334 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2335 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2338 if (this.collapsable) {
2339 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2341 if (this.rotateable) {
2342 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2344 this.collapsableEl = this.el.select('.roo-collapsable').first();
2346 this.footerEl = this.el.select('.card-footer').first();
2347 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2348 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2349 this.headerEl = this.el.select('.card-header',true).first();
2352 this.el.addClass('roo-card-rotated');
2353 this.fireEvent('rotate', this, true);
2355 this.header_imageEl = this.el.select('.card-img-top',true).first();
2356 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2359 getDragData : function(e)
2361 var target = this.getEl();
2363 //this.handleSelection(e);
2368 nodes: this.getEl(),
2373 dragData.ddel = target.dom ; // the div element
2374 Roo.log(target.getWidth( ));
2375 dragData.ddel.style.width = target.getWidth() + 'px';
2382 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2383 * whole Element becomes the target, and this causes the drop gesture to append.
2385 * Returns an object:
2388 position : 'below' or 'above'
2389 card : relateive to card OBJECT (or true for no cards listed)
2390 items_n : relative to nth item in list
2391 card_n : relative to nth card in list
2396 getTargetFromEvent : function(e, dragged_card_el)
2398 var target = e.getTarget();
2399 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2400 target = target.parentNode;
2411 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2412 // see if target is one of the 'cards'...
2415 //Roo.log(this.items.length);
2418 var last_card_n = 0;
2420 for (var i = 0;i< this.items.length;i++) {
2422 if (!this.items[i].el.hasClass('card')) {
2425 pos = this.getDropPoint(e, this.items[i].el.dom);
2427 cards_len = ret.cards.length;
2428 //Roo.log(this.items[i].el.dom.id);
2429 ret.cards.push(this.items[i]);
2431 if (ret.card_n < 0 && pos == 'above') {
2432 ret.position = cards_len > 0 ? 'below' : pos;
2433 ret.items_n = i > 0 ? i - 1 : 0;
2434 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2435 ret.card = ret.cards[ret.card_n];
2438 if (!ret.cards.length) {
2440 ret.position = 'below';
2444 // could not find a card.. stick it at the end..
2445 if (ret.card_n < 0) {
2446 ret.card_n = last_card_n;
2447 ret.card = ret.cards[last_card_n];
2448 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2449 ret.position = 'below';
2452 if (this.items[ret.items_n].el == dragged_card_el) {
2456 if (ret.position == 'below') {
2457 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2459 if (card_after && card_after.el == dragged_card_el) {
2466 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2468 if (card_before && card_before.el == dragged_card_el) {
2475 onNodeEnter : function(n, dd, e, data){
2478 onNodeOver : function(n, dd, e, data)
2481 var target_info = this.getTargetFromEvent(e,data.source.el);
2482 if (target_info === false) {
2483 this.dropPlaceHolder('hide');
2486 Roo.log(['getTargetFromEvent', target_info ]);
2489 if (this.fireEvent('cardover', this, [ data ]) === false) {
2493 this.dropPlaceHolder('show', target_info,data);
2497 onNodeOut : function(n, dd, e, data){
2498 this.dropPlaceHolder('hide');
2501 onNodeDrop : function(n, dd, e, data)
2504 // call drop - return false if
2506 // this could actually fail - if the Network drops..
2507 // we will ignore this at present..- client should probably reload
2508 // the whole set of cards if stuff like that fails.
2511 var info = this.getTargetFromEvent(e,data.source.el);
2512 if (info === false) {
2515 this.dropPlaceHolder('hide');
2519 this.acceptCard(data.source, info.position, info.card, info.items_n);
2523 firstChildCard : function()
2525 for (var i = 0;i< this.items.length;i++) {
2527 if (!this.items[i].el.hasClass('card')) {
2530 return this.items[i];
2532 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2537 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2539 acceptCard : function(move_card, position, next_to_card )
2541 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2545 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2547 move_card.parent().removeCard(move_card);
2550 var dom = move_card.el.dom;
2551 dom.style.width = ''; // clear with - which is set by drag.
2553 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2554 var cardel = next_to_card.el.dom;
2556 if (position == 'above' ) {
2557 cardel.parentNode.insertBefore(dom, cardel);
2558 } else if (cardel.nextSibling) {
2559 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2561 cardel.parentNode.append(dom);
2564 // card container???
2565 this.containerEl.dom.append(dom);
2568 //FIXME HANDLE card = true
2570 // add this to the correct place in items.
2572 // remove Card from items.
2575 if (this.items.length) {
2577 //Roo.log([info.items_n, info.position, this.items.length]);
2578 for (var i =0; i < this.items.length; i++) {
2579 if (i == to_items_n && position == 'above') {
2580 nitems.push(move_card);
2582 nitems.push(this.items[i]);
2583 if (i == to_items_n && position == 'below') {
2584 nitems.push(move_card);
2587 this.items = nitems;
2588 Roo.log(this.items);
2590 this.items.push(move_card);
2593 move_card.parentId = this.id;
2599 removeCard : function(c)
2601 this.items = this.items.filter(function(e) { return e != c });
2604 dom.parentNode.removeChild(dom);
2605 dom.style.width = ''; // clear with - which is set by drag.
2610 /** Decide whether to drop above or below a View node. */
2611 getDropPoint : function(e, n, dd)
2616 if (n == this.containerEl.dom) {
2619 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2620 var c = t + (b - t) / 2;
2621 var y = Roo.lib.Event.getPageY(e);
2628 onToggleCollapse : function(e)
2630 if (this.collapsed) {
2631 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2632 this.collapsableEl.addClass('show');
2633 this.collapsed = false;
2636 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2637 this.collapsableEl.removeClass('show');
2638 this.collapsed = true;
2643 onToggleRotate : function(e)
2645 this.collapsableEl.removeClass('show');
2646 this.footerEl.removeClass('d-none');
2647 this.el.removeClass('roo-card-rotated');
2648 this.el.removeClass('d-none');
2651 this.collapsableEl.addClass('show');
2652 this.rotated = false;
2653 this.fireEvent('rotate', this, this.rotated);
2656 this.el.addClass('roo-card-rotated');
2657 this.footerEl.addClass('d-none');
2658 this.el.select('.roo-collapsable').removeClass('show');
2660 this.rotated = true;
2661 this.fireEvent('rotate', this, this.rotated);
2665 dropPlaceHolder: function (action, info, data)
2667 if (this.dropEl === false) {
2668 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2672 this.dropEl.removeClass(['d-none', 'd-block']);
2673 if (action == 'hide') {
2675 this.dropEl.addClass('d-none');
2678 // FIXME - info.card == true!!!
2679 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2681 if (info.card !== true) {
2682 var cardel = info.card.el.dom;
2684 if (info.position == 'above') {
2685 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2686 } else if (cardel.nextSibling) {
2687 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2689 cardel.parentNode.append(this.dropEl.dom);
2692 // card container???
2693 this.containerEl.dom.append(this.dropEl.dom);
2696 this.dropEl.addClass('d-block roo-card-dropzone');
2698 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2705 setHeaderText: function(html)
2708 if (this.headerContainerEl) {
2709 this.headerContainerEl.dom.innerHTML = html;
2712 onHeaderImageLoad : function(ev, he)
2714 if (!this.header_image_fit_square) {
2718 var hw = he.naturalHeight / he.naturalWidth;
2721 //var w = he.dom.naturalWidth;
2724 he.style.position = 'relative';
2726 var nw = (ww * (1/hw));
2727 Roo.get(he).setSize( ww * (1/hw), ww);
2728 he.style.left = ((ww - nw)/ 2) + 'px';
2729 he.style.position = 'relative';
2740 * Card header - holder for the card header elements.
2745 * @class Roo.bootstrap.CardHeader
2746 * @extends Roo.bootstrap.Element
2747 * Bootstrap CardHeader class
2749 * Create a new Card Header - that you can embed children into
2750 * @param {Object} config The config object
2753 Roo.bootstrap.CardHeader = function(config){
2754 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2757 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2760 container_method : 'getCardHeader'
2773 * Card footer - holder for the card footer elements.
2778 * @class Roo.bootstrap.CardFooter
2779 * @extends Roo.bootstrap.Element
2780 * Bootstrap CardFooter class
2782 * Create a new Card Footer - that you can embed children into
2783 * @param {Object} config The config object
2786 Roo.bootstrap.CardFooter = function(config){
2787 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2790 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2793 container_method : 'getCardFooter'
2806 * Card header - holder for the card header elements.
2811 * @class Roo.bootstrap.CardImageTop
2812 * @extends Roo.bootstrap.Element
2813 * Bootstrap CardImageTop class
2815 * Create a new Card Image Top container
2816 * @param {Object} config The config object
2819 Roo.bootstrap.CardImageTop = function(config){
2820 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2823 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2826 container_method : 'getCardImageTop'
2844 * @class Roo.bootstrap.Img
2845 * @extends Roo.bootstrap.Component
2846 * Bootstrap Img class
2847 * @cfg {Boolean} imgResponsive false | true
2848 * @cfg {String} border rounded | circle | thumbnail
2849 * @cfg {String} src image source
2850 * @cfg {String} alt image alternative text
2851 * @cfg {String} href a tag href
2852 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2853 * @cfg {String} xsUrl xs image source
2854 * @cfg {String} smUrl sm image source
2855 * @cfg {String} mdUrl md image source
2856 * @cfg {String} lgUrl lg image source
2859 * Create a new Input
2860 * @param {Object} config The config object
2863 Roo.bootstrap.Img = function(config){
2864 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2870 * The img click event for the img.
2871 * @param {Roo.EventObject} e
2877 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2879 imgResponsive: true,
2889 getAutoCreate : function()
2891 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2892 return this.createSingleImg();
2897 cls: 'roo-image-responsive-group',
2902 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2904 if(!_this[size + 'Url']){
2910 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2911 html: _this.html || cfg.html,
2912 src: _this[size + 'Url']
2915 img.cls += ' roo-image-responsive-' + size;
2917 var s = ['xs', 'sm', 'md', 'lg'];
2919 s.splice(s.indexOf(size), 1);
2921 Roo.each(s, function(ss){
2922 img.cls += ' hidden-' + ss;
2925 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2926 cfg.cls += ' img-' + _this.border;
2930 cfg.alt = _this.alt;
2943 a.target = _this.target;
2947 cfg.cn.push((_this.href) ? a : img);
2954 createSingleImg : function()
2958 cls: (this.imgResponsive) ? 'img-responsive' : '',
2960 src : 'about:blank' // just incase src get's set to undefined?!?
2963 cfg.html = this.html || cfg.html;
2965 cfg.src = this.src || cfg.src;
2967 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2968 cfg.cls += ' img-' + this.border;
2985 a.target = this.target;
2990 return (this.href) ? a : cfg;
2993 initEvents: function()
2996 this.el.on('click', this.onClick, this);
3001 onClick : function(e)
3003 Roo.log('img onclick');
3004 this.fireEvent('click', this, e);
3007 * Sets the url of the image - used to update it
3008 * @param {String} url the url of the image
3011 setSrc : function(url)
3015 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3016 this.el.dom.src = url;
3020 this.el.select('img', true).first().dom.src = url;
3036 * @class Roo.bootstrap.Link
3037 * @extends Roo.bootstrap.Component
3038 * Bootstrap Link Class
3039 * @cfg {String} alt image alternative text
3040 * @cfg {String} href a tag href
3041 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3042 * @cfg {String} html the content of the link.
3043 * @cfg {String} anchor name for the anchor link
3044 * @cfg {String} fa - favicon
3046 * @cfg {Boolean} preventDefault (true | false) default false
3050 * Create a new Input
3051 * @param {Object} config The config object
3054 Roo.bootstrap.Link = function(config){
3055 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3061 * The img click event for the img.
3062 * @param {Roo.EventObject} e
3068 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3072 preventDefault: false,
3078 getAutoCreate : function()
3080 var html = this.html || '';
3082 if (this.fa !== false) {
3083 html = '<i class="fa fa-' + this.fa + '"></i>';
3088 // anchor's do not require html/href...
3089 if (this.anchor === false) {
3091 cfg.href = this.href || '#';
3093 cfg.name = this.anchor;
3094 if (this.html !== false || this.fa !== false) {
3097 if (this.href !== false) {
3098 cfg.href = this.href;
3102 if(this.alt !== false){
3107 if(this.target !== false) {
3108 cfg.target = this.target;
3114 initEvents: function() {
3116 if(!this.href || this.preventDefault){
3117 this.el.on('click', this.onClick, this);
3121 onClick : function(e)
3123 if(this.preventDefault){
3126 //Roo.log('img onclick');
3127 this.fireEvent('click', this, e);
3140 * @class Roo.bootstrap.Header
3141 * @extends Roo.bootstrap.Component
3142 * Bootstrap Header class
3143 * @cfg {String} html content of header
3144 * @cfg {Number} level (1|2|3|4|5|6) default 1
3147 * Create a new Header
3148 * @param {Object} config The config object
3152 Roo.bootstrap.Header = function(config){
3153 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3156 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3164 getAutoCreate : function(){
3169 tag: 'h' + (1 *this.level),
3170 html: this.html || ''
3182 * Ext JS Library 1.1.1
3183 * Copyright(c) 2006-2007, Ext JS, LLC.
3185 * Originally Released Under LGPL - original licence link has changed is not relivant.
3188 * <script type="text/javascript">
3192 * @class Roo.bootstrap.MenuMgr
3193 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3196 Roo.bootstrap.MenuMgr = function(){
3197 var menus, active, groups = {}, attached = false, lastShow = new Date();
3199 // private - called when first menu is created
3202 active = new Roo.util.MixedCollection();
3203 Roo.get(document).addKeyListener(27, function(){
3204 if(active.length > 0){
3212 if(active && active.length > 0){
3213 var c = active.clone();
3223 if(active.length < 1){
3224 Roo.get(document).un("mouseup", onMouseDown);
3232 var last = active.last();
3233 lastShow = new Date();
3236 Roo.get(document).on("mouseup", onMouseDown);
3241 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3242 m.parentMenu.activeChild = m;
3243 }else if(last && last.isVisible()){
3244 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3249 function onBeforeHide(m){
3251 m.activeChild.hide();
3253 if(m.autoHideTimer){
3254 clearTimeout(m.autoHideTimer);
3255 delete m.autoHideTimer;
3260 function onBeforeShow(m){
3261 var pm = m.parentMenu;
3262 if(!pm && !m.allowOtherMenus){
3264 }else if(pm && pm.activeChild && active != m){
3265 pm.activeChild.hide();
3269 // private this should really trigger on mouseup..
3270 function onMouseDown(e){
3271 Roo.log("on Mouse Up");
3273 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3274 Roo.log("MenuManager hideAll");
3283 function onBeforeCheck(mi, state){
3285 var g = groups[mi.group];
3286 for(var i = 0, l = g.length; i < l; i++){
3288 g[i].setChecked(false);
3297 * Hides all menus that are currently visible
3299 hideAll : function(){
3304 register : function(menu){
3308 menus[menu.id] = menu;
3309 menu.on("beforehide", onBeforeHide);
3310 menu.on("hide", onHide);
3311 menu.on("beforeshow", onBeforeShow);
3312 menu.on("show", onShow);
3314 if(g && menu.events["checkchange"]){
3318 groups[g].push(menu);
3319 menu.on("checkchange", onCheck);
3324 * Returns a {@link Roo.menu.Menu} object
3325 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3326 * be used to generate and return a new Menu instance.
3328 get : function(menu){
3329 if(typeof menu == "string"){ // menu id
3331 }else if(menu.events){ // menu instance
3334 /*else if(typeof menu.length == 'number'){ // array of menu items?
3335 return new Roo.bootstrap.Menu({items:menu});
3336 }else{ // otherwise, must be a config
3337 return new Roo.bootstrap.Menu(menu);
3344 unregister : function(menu){
3345 delete menus[menu.id];
3346 menu.un("beforehide", onBeforeHide);
3347 menu.un("hide", onHide);
3348 menu.un("beforeshow", onBeforeShow);
3349 menu.un("show", onShow);
3351 if(g && menu.events["checkchange"]){
3352 groups[g].remove(menu);
3353 menu.un("checkchange", onCheck);
3358 registerCheckable : function(menuItem){
3359 var g = menuItem.group;
3364 groups[g].push(menuItem);
3365 menuItem.on("beforecheckchange", onBeforeCheck);
3370 unregisterCheckable : function(menuItem){
3371 var g = menuItem.group;
3373 groups[g].remove(menuItem);
3374 menuItem.un("beforecheckchange", onBeforeCheck);
3386 * @class Roo.bootstrap.Menu
3387 * @extends Roo.bootstrap.Component
3388 * Bootstrap Menu class - container for MenuItems
3389 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3390 * @cfg {bool} hidden if the menu should be hidden when rendered.
3391 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3392 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3396 * @param {Object} config The config object
3400 Roo.bootstrap.Menu = function(config){
3401 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3402 if (this.registerMenu && this.type != 'treeview') {
3403 Roo.bootstrap.MenuMgr.register(this);
3410 * Fires before this menu is displayed (return false to block)
3411 * @param {Roo.menu.Menu} this
3416 * Fires before this menu is hidden (return false to block)
3417 * @param {Roo.menu.Menu} this
3422 * Fires after this menu is displayed
3423 * @param {Roo.menu.Menu} this
3428 * Fires after this menu is hidden
3429 * @param {Roo.menu.Menu} this
3434 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3435 * @param {Roo.menu.Menu} this
3436 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3437 * @param {Roo.EventObject} e
3442 * Fires when the mouse is hovering over this menu
3443 * @param {Roo.menu.Menu} this
3444 * @param {Roo.EventObject} e
3445 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3450 * Fires when the mouse exits this menu
3451 * @param {Roo.menu.Menu} this
3452 * @param {Roo.EventObject} e
3453 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3458 * Fires when a menu item contained in this menu is clicked
3459 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3460 * @param {Roo.EventObject} e
3464 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3467 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3471 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3474 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3476 registerMenu : true,
3478 menuItems :false, // stores the menu items..
3488 getChildContainer : function() {
3492 getAutoCreate : function(){
3494 //if (['right'].indexOf(this.align)!==-1) {
3495 // cfg.cn[1].cls += ' pull-right'
3501 cls : 'dropdown-menu' ,
3502 style : 'z-index:1000'
3506 if (this.type === 'submenu') {
3507 cfg.cls = 'submenu active';
3509 if (this.type === 'treeview') {
3510 cfg.cls = 'treeview-menu';
3515 initEvents : function() {
3517 // Roo.log("ADD event");
3518 // Roo.log(this.triggerEl.dom);
3520 this.triggerEl.on('click', this.onTriggerClick, this);
3522 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3525 if (this.triggerEl.hasClass('nav-item')) {
3526 // dropdown toggle on the 'a' in BS4?
3527 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3529 this.triggerEl.addClass('dropdown-toggle');
3532 this.el.on('touchstart' , this.onTouch, this);
3534 this.el.on('click' , this.onClick, this);
3536 this.el.on("mouseover", this.onMouseOver, this);
3537 this.el.on("mouseout", this.onMouseOut, this);
3541 findTargetItem : function(e)
3543 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3547 //Roo.log(t); Roo.log(t.id);
3549 //Roo.log(this.menuitems);
3550 return this.menuitems.get(t.id);
3552 //return this.items.get(t.menuItemId);
3558 onTouch : function(e)
3560 Roo.log("menu.onTouch");
3561 //e.stopEvent(); this make the user popdown broken
3565 onClick : function(e)
3567 Roo.log("menu.onClick");
3569 var t = this.findTargetItem(e);
3570 if(!t || t.isContainer){
3575 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3576 if(t == this.activeItem && t.shouldDeactivate(e)){
3577 this.activeItem.deactivate();
3578 delete this.activeItem;
3582 this.setActiveItem(t, true);
3590 Roo.log('pass click event');
3594 this.fireEvent("click", this, t, e);
3598 if(!t.href.length || t.href == '#'){
3599 (function() { _this.hide(); }).defer(100);
3604 onMouseOver : function(e){
3605 var t = this.findTargetItem(e);
3608 // if(t.canActivate && !t.disabled){
3609 // this.setActiveItem(t, true);
3613 this.fireEvent("mouseover", this, e, t);
3615 isVisible : function(){
3616 return !this.hidden;
3618 onMouseOut : function(e){
3619 var t = this.findTargetItem(e);
3622 // if(t == this.activeItem && t.shouldDeactivate(e)){
3623 // this.activeItem.deactivate();
3624 // delete this.activeItem;
3627 this.fireEvent("mouseout", this, e, t);
3632 * Displays this menu relative to another element
3633 * @param {String/HTMLElement/Roo.Element} element The element to align to
3634 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3635 * the element (defaults to this.defaultAlign)
3636 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3638 show : function(el, pos, parentMenu)
3640 if (false === this.fireEvent("beforeshow", this)) {
3641 Roo.log("show canceled");
3644 this.parentMenu = parentMenu;
3649 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3652 * Displays this menu at a specific xy position
3653 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3654 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3656 showAt : function(xy, parentMenu, /* private: */_e){
3657 this.parentMenu = parentMenu;
3662 this.fireEvent("beforeshow", this);
3663 //xy = this.el.adjustForConstraints(xy);
3667 this.hideMenuItems();
3668 this.hidden = false;
3669 this.triggerEl.addClass('open');
3670 this.el.addClass('show');
3672 // reassign x when hitting right
3673 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3674 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3677 // reassign y when hitting bottom
3678 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3679 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3682 // but the list may align on trigger left or trigger top... should it be a properity?
3684 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3689 this.fireEvent("show", this);
3695 this.doFocus.defer(50, this);
3699 doFocus : function(){
3701 this.focusEl.focus();
3706 * Hides this menu and optionally all parent menus
3707 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3709 hide : function(deep)
3711 if (false === this.fireEvent("beforehide", this)) {
3712 Roo.log("hide canceled");
3715 this.hideMenuItems();
3716 if(this.el && this.isVisible()){
3718 if(this.activeItem){
3719 this.activeItem.deactivate();
3720 this.activeItem = null;
3722 this.triggerEl.removeClass('open');;
3723 this.el.removeClass('show');
3725 this.fireEvent("hide", this);
3727 if(deep === true && this.parentMenu){
3728 this.parentMenu.hide(true);
3732 onTriggerClick : function(e)
3734 Roo.log('trigger click');
3736 var target = e.getTarget();
3738 Roo.log(target.nodeName.toLowerCase());
3740 if(target.nodeName.toLowerCase() === 'i'){
3746 onTriggerPress : function(e)
3748 Roo.log('trigger press');
3749 //Roo.log(e.getTarget());
3750 // Roo.log(this.triggerEl.dom);
3752 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3753 var pel = Roo.get(e.getTarget());
3754 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3755 Roo.log('is treeview or dropdown?');
3759 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3763 if (this.isVisible()) {
3768 this.show(this.triggerEl, '?', false);
3771 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3778 hideMenuItems : function()
3780 Roo.log("hide Menu Items");
3785 this.el.select('.open',true).each(function(aa) {
3787 aa.removeClass('open');
3791 addxtypeChild : function (tree, cntr) {
3792 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3794 this.menuitems.add(comp);
3806 this.getEl().dom.innerHTML = '';
3807 this.menuitems.clear();
3821 * @class Roo.bootstrap.MenuItem
3822 * @extends Roo.bootstrap.Component
3823 * Bootstrap MenuItem class
3824 * @cfg {String} html the menu label
3825 * @cfg {String} href the link
3826 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3827 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3828 * @cfg {Boolean} active used on sidebars to highlight active itesm
3829 * @cfg {String} fa favicon to show on left of menu item.
3830 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3834 * Create a new MenuItem
3835 * @param {Object} config The config object
3839 Roo.bootstrap.MenuItem = function(config){
3840 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3845 * The raw click event for the entire grid.
3846 * @param {Roo.bootstrap.MenuItem} this
3847 * @param {Roo.EventObject} e
3853 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3857 preventDefault: false,
3858 isContainer : false,
3862 getAutoCreate : function(){
3864 if(this.isContainer){
3867 cls: 'dropdown-menu-item '
3877 cls : 'dropdown-item',
3882 if (this.fa !== false) {
3885 cls : 'fa fa-' + this.fa
3894 cls: 'dropdown-menu-item',
3897 if (this.parent().type == 'treeview') {
3898 cfg.cls = 'treeview-menu';
3901 cfg.cls += ' active';
3906 anc.href = this.href || cfg.cn[0].href ;
3907 ctag.html = this.html || cfg.cn[0].html ;
3911 initEvents: function()
3913 if (this.parent().type == 'treeview') {
3914 this.el.select('a').on('click', this.onClick, this);
3918 this.menu.parentType = this.xtype;
3919 this.menu.triggerEl = this.el;
3920 this.menu = this.addxtype(Roo.apply({}, this.menu));
3924 onClick : function(e)
3926 Roo.log('item on click ');
3928 if(this.preventDefault){
3931 //this.parent().hideMenuItems();
3933 this.fireEvent('click', this, e);
3952 * @class Roo.bootstrap.MenuSeparator
3953 * @extends Roo.bootstrap.Component
3954 * Bootstrap MenuSeparator class
3957 * Create a new MenuItem
3958 * @param {Object} config The config object
3962 Roo.bootstrap.MenuSeparator = function(config){
3963 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3966 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3968 getAutoCreate : function(){
3987 * @class Roo.bootstrap.Modal
3988 * @extends Roo.bootstrap.Component
3989 * Bootstrap Modal class
3990 * @cfg {String} title Title of dialog
3991 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3992 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
3993 * @cfg {Boolean} specificTitle default false
3994 * @cfg {Array} buttons Array of buttons or standard button set..
3995 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3996 * @cfg {Boolean} animate default true
3997 * @cfg {Boolean} allow_close default true
3998 * @cfg {Boolean} fitwindow default false
3999 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4000 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4001 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4002 * @cfg {String} size (sm|lg|xl) default empty
4003 * @cfg {Number} max_width set the max width of modal
4004 * @cfg {Boolean} editableTitle can the title be edited
4009 * Create a new Modal Dialog
4010 * @param {Object} config The config object
4013 Roo.bootstrap.Modal = function(config){
4014 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4019 * The raw btnclick event for the button
4020 * @param {Roo.EventObject} e
4025 * Fire when dialog resize
4026 * @param {Roo.bootstrap.Modal} this
4027 * @param {Roo.EventObject} e
4031 * @event titlechanged
4032 * Fire when the editable title has been changed
4033 * @param {Roo.bootstrap.Modal} this
4034 * @param {Roo.EventObject} value
4036 "titlechanged" : true
4039 this.buttons = this.buttons || [];
4042 this.tmpl = Roo.factory(this.tmpl);
4047 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4049 title : 'test dialog',
4059 specificTitle: false,
4061 buttonPosition: 'right',
4083 editableTitle : false,
4085 onRender : function(ct, position)
4087 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4090 var cfg = Roo.apply({}, this.getAutoCreate());
4093 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4095 //if (!cfg.name.length) {
4099 cfg.cls += ' ' + this.cls;
4102 cfg.style = this.style;
4104 this.el = Roo.get(document.body).createChild(cfg, position);
4106 //var type = this.el.dom.type;
4109 if(this.tabIndex !== undefined){
4110 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4113 this.dialogEl = this.el.select('.modal-dialog',true).first();
4114 this.bodyEl = this.el.select('.modal-body',true).first();
4115 this.closeEl = this.el.select('.modal-header .close', true).first();
4116 this.headerEl = this.el.select('.modal-header',true).first();
4117 this.titleEl = this.el.select('.modal-title',true).first();
4118 this.footerEl = this.el.select('.modal-footer',true).first();
4120 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4122 //this.el.addClass("x-dlg-modal");
4124 if (this.buttons.length) {
4125 Roo.each(this.buttons, function(bb) {
4126 var b = Roo.apply({}, bb);
4127 b.xns = b.xns || Roo.bootstrap;
4128 b.xtype = b.xtype || 'Button';
4129 if (typeof(b.listeners) == 'undefined') {
4130 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4133 var btn = Roo.factory(b);
4135 btn.render(this.getButtonContainer());
4139 // render the children.
4142 if(typeof(this.items) != 'undefined'){
4143 var items = this.items;
4146 for(var i =0;i < items.length;i++) {
4147 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4151 this.items = nitems;
4153 // where are these used - they used to be body/close/footer
4157 //this.el.addClass([this.fieldClass, this.cls]);
4161 getAutoCreate : function()
4163 // we will default to modal-body-overflow - might need to remove or make optional later.
4165 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4166 html : this.html || ''
4171 cls : 'modal-title',
4175 if(this.specificTitle){ // WTF is this?
4180 if (this.allow_close && Roo.bootstrap.version == 3) {
4190 if (this.editableTitle) {
4192 cls: 'form-control roo-editable-title d-none',
4198 if (this.allow_close && Roo.bootstrap.version == 4) {
4208 if(this.size.length){
4209 size = 'modal-' + this.size;
4212 var footer = Roo.bootstrap.version == 3 ?
4214 cls : 'modal-footer',
4218 cls: 'btn-' + this.buttonPosition
4223 { // BS4 uses mr-auto on left buttons....
4224 cls : 'modal-footer'
4235 cls: "modal-dialog " + size,
4238 cls : "modal-content",
4241 cls : 'modal-header',
4256 modal.cls += ' fade';
4262 getChildContainer : function() {
4267 getButtonContainer : function() {
4269 return Roo.bootstrap.version == 4 ?
4270 this.el.select('.modal-footer',true).first()
4271 : this.el.select('.modal-footer div',true).first();
4274 initEvents : function()
4276 if (this.allow_close) {
4277 this.closeEl.on('click', this.hide, this);
4279 Roo.EventManager.onWindowResize(this.resize, this, true);
4280 if (this.editableTitle) {
4281 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4282 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4283 this.headerEditEl.on('keyup', function(e) {
4284 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4285 this.toggleHeaderInput(false)
4288 this.headerEditEl.on('blur', function(e) {
4289 this.toggleHeaderInput(false)
4298 this.maskEl.setSize(
4299 Roo.lib.Dom.getViewWidth(true),
4300 Roo.lib.Dom.getViewHeight(true)
4303 if (this.fitwindow) {
4305 this.dialogEl.setStyle( { 'max-width' : '100%' });
4307 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4308 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4313 if(this.max_width !== 0) {
4315 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4318 this.setSize(w, this.height);
4322 if(this.max_height) {
4323 this.setSize(w,Math.min(
4325 Roo.lib.Dom.getViewportHeight(true) - 60
4331 if(!this.fit_content) {
4332 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4336 this.setSize(w, Math.min(
4338 this.headerEl.getHeight() +
4339 this.footerEl.getHeight() +
4340 this.getChildHeight(this.bodyEl.dom.childNodes),
4341 Roo.lib.Dom.getViewportHeight(true) - 60)
4347 setSize : function(w,h)
4358 if (!this.rendered) {
4361 this.toggleHeaderInput(false);
4362 //this.el.setStyle('display', 'block');
4363 this.el.removeClass('hideing');
4364 this.el.dom.style.display='block';
4366 Roo.get(document.body).addClass('modal-open');
4368 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4371 this.el.addClass('show');
4372 this.el.addClass('in');
4375 this.el.addClass('show');
4376 this.el.addClass('in');
4379 // not sure how we can show data in here..
4381 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4384 Roo.get(document.body).addClass("x-body-masked");
4386 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4387 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4388 this.maskEl.dom.style.display = 'block';
4389 this.maskEl.addClass('show');
4394 this.fireEvent('show', this);
4396 // set zindex here - otherwise it appears to be ignored...
4397 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4400 this.items.forEach( function(e) {
4401 e.layout ? e.layout() : false;
4409 if(this.fireEvent("beforehide", this) !== false){
4411 this.maskEl.removeClass('show');
4413 this.maskEl.dom.style.display = '';
4414 Roo.get(document.body).removeClass("x-body-masked");
4415 this.el.removeClass('in');
4416 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4418 if(this.animate){ // why
4419 this.el.addClass('hideing');
4420 this.el.removeClass('show');
4422 if (!this.el.hasClass('hideing')) {
4423 return; // it's been shown again...
4426 this.el.dom.style.display='';
4428 Roo.get(document.body).removeClass('modal-open');
4429 this.el.removeClass('hideing');
4433 this.el.removeClass('show');
4434 this.el.dom.style.display='';
4435 Roo.get(document.body).removeClass('modal-open');
4438 this.fireEvent('hide', this);
4441 isVisible : function()
4444 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4448 addButton : function(str, cb)
4452 var b = Roo.apply({}, { html : str } );
4453 b.xns = b.xns || Roo.bootstrap;
4454 b.xtype = b.xtype || 'Button';
4455 if (typeof(b.listeners) == 'undefined') {
4456 b.listeners = { click : cb.createDelegate(this) };
4459 var btn = Roo.factory(b);
4461 btn.render(this.getButtonContainer());
4467 setDefaultButton : function(btn)
4469 //this.el.select('.modal-footer').()
4472 resizeTo: function(w,h)
4474 this.dialogEl.setWidth(w);
4476 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4478 this.bodyEl.setHeight(h - diff);
4480 this.fireEvent('resize', this);
4483 setContentSize : function(w, h)
4487 onButtonClick: function(btn,e)
4490 this.fireEvent('btnclick', btn.name, e);
4493 * Set the title of the Dialog
4494 * @param {String} str new Title
4496 setTitle: function(str) {
4497 this.titleEl.dom.innerHTML = str;
4501 * Set the body of the Dialog
4502 * @param {String} str new Title
4504 setBody: function(str) {
4505 this.bodyEl.dom.innerHTML = str;
4508 * Set the body of the Dialog using the template
4509 * @param {Obj} data - apply this data to the template and replace the body contents.
4511 applyBody: function(obj)
4514 Roo.log("Error - using apply Body without a template");
4517 this.tmpl.overwrite(this.bodyEl, obj);
4520 getChildHeight : function(child_nodes)
4524 child_nodes.length == 0
4529 var child_height = 0;
4531 for(var i = 0; i < child_nodes.length; i++) {
4534 * for modal with tabs...
4535 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4537 var layout_childs = child_nodes[i].childNodes;
4539 for(var j = 0; j < layout_childs.length; j++) {
4541 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4543 var layout_body_childs = layout_childs[j].childNodes;
4545 for(var k = 0; k < layout_body_childs.length; k++) {
4547 if(layout_body_childs[k].classList.contains('navbar')) {
4548 child_height += layout_body_childs[k].offsetHeight;
4552 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4554 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4556 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4558 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4559 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4574 child_height += child_nodes[i].offsetHeight;
4575 // Roo.log(child_nodes[i].offsetHeight);
4578 return child_height;
4580 toggleHeaderInput : function(is_edit)
4582 if (!this.editableTitle) {
4583 return; // not editable.
4585 if (is_edit && this.is_header_editing) {
4586 return; // already editing..
4590 this.headerEditEl.dom.value = this.title;
4591 this.headerEditEl.removeClass('d-none');
4592 this.headerEditEl.dom.focus();
4593 this.titleEl.addClass('d-none');
4595 this.is_header_editing = true;
4598 // flip back to not editing.
4599 this.title = this.headerEditEl.dom.value;
4600 this.headerEditEl.addClass('d-none');
4601 this.titleEl.removeClass('d-none');
4602 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4603 this.is_header_editing = false;
4604 this.fireEvent('titlechanged', this, this.title);
4613 Roo.apply(Roo.bootstrap.Modal, {
4615 * Button config that displays a single OK button
4624 * Button config that displays Yes and No buttons
4640 * Button config that displays OK and Cancel buttons
4655 * Button config that displays Yes, No and Cancel buttons
4680 * messagebox - can be used as a replace
4684 * @class Roo.MessageBox
4685 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4689 Roo.Msg.alert('Status', 'Changes saved successfully.');
4691 // Prompt for user data:
4692 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4694 // process text value...
4698 // Show a dialog using config options:
4700 title:'Save Changes?',
4701 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4702 buttons: Roo.Msg.YESNOCANCEL,
4709 Roo.bootstrap.MessageBox = function(){
4710 var dlg, opt, mask, waitTimer;
4711 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4712 var buttons, activeTextEl, bwidth;
4716 var handleButton = function(button){
4718 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4722 var handleHide = function(){
4724 dlg.el.removeClass(opt.cls);
4727 // Roo.TaskMgr.stop(waitTimer);
4728 // waitTimer = null;
4733 var updateButtons = function(b){
4736 buttons["ok"].hide();
4737 buttons["cancel"].hide();
4738 buttons["yes"].hide();
4739 buttons["no"].hide();
4740 dlg.footerEl.hide();
4744 dlg.footerEl.show();
4745 for(var k in buttons){
4746 if(typeof buttons[k] != "function"){
4749 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4750 width += buttons[k].el.getWidth()+15;
4760 var handleEsc = function(d, k, e){
4761 if(opt && opt.closable !== false){
4771 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4772 * @return {Roo.BasicDialog} The BasicDialog element
4774 getDialog : function(){
4776 dlg = new Roo.bootstrap.Modal( {
4779 //constraintoviewport:false,
4781 //collapsible : false,
4786 //buttonAlign:"center",
4787 closeClick : function(){
4788 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4791 handleButton("cancel");
4796 dlg.on("hide", handleHide);
4798 //dlg.addKeyListener(27, handleEsc);
4800 this.buttons = buttons;
4801 var bt = this.buttonText;
4802 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4803 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4804 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4805 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4807 bodyEl = dlg.bodyEl.createChild({
4809 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4810 '<textarea class="roo-mb-textarea"></textarea>' +
4811 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4813 msgEl = bodyEl.dom.firstChild;
4814 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4815 textboxEl.enableDisplayMode();
4816 textboxEl.addKeyListener([10,13], function(){
4817 if(dlg.isVisible() && opt && opt.buttons){
4820 }else if(opt.buttons.yes){
4821 handleButton("yes");
4825 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4826 textareaEl.enableDisplayMode();
4827 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4828 progressEl.enableDisplayMode();
4830 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4831 var pf = progressEl.dom.firstChild;
4833 pp = Roo.get(pf.firstChild);
4834 pp.setHeight(pf.offsetHeight);
4842 * Updates the message box body text
4843 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4844 * the XHTML-compliant non-breaking space character '&#160;')
4845 * @return {Roo.MessageBox} This message box
4847 updateText : function(text)
4849 if(!dlg.isVisible() && !opt.width){
4850 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4851 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4853 msgEl.innerHTML = text || ' ';
4855 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4856 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4858 Math.min(opt.width || cw , this.maxWidth),
4859 Math.max(opt.minWidth || this.minWidth, bwidth)
4862 activeTextEl.setWidth(w);
4864 if(dlg.isVisible()){
4865 dlg.fixedcenter = false;
4867 // to big, make it scroll. = But as usual stupid IE does not support
4870 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4871 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4872 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4874 bodyEl.dom.style.height = '';
4875 bodyEl.dom.style.overflowY = '';
4878 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4880 bodyEl.dom.style.overflowX = '';
4883 dlg.setContentSize(w, bodyEl.getHeight());
4884 if(dlg.isVisible()){
4885 dlg.fixedcenter = true;
4891 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4892 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4893 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4894 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4895 * @return {Roo.MessageBox} This message box
4897 updateProgress : function(value, text){
4899 this.updateText(text);
4902 if (pp) { // weird bug on my firefox - for some reason this is not defined
4903 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4904 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4910 * Returns true if the message box is currently displayed
4911 * @return {Boolean} True if the message box is visible, else false
4913 isVisible : function(){
4914 return dlg && dlg.isVisible();
4918 * Hides the message box if it is displayed
4921 if(this.isVisible()){
4927 * Displays a new message box, or reinitializes an existing message box, based on the config options
4928 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4929 * The following config object properties are supported:
4931 Property Type Description
4932 ---------- --------------- ------------------------------------------------------------------------------------
4933 animEl String/Element An id or Element from which the message box should animate as it opens and
4934 closes (defaults to undefined)
4935 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4936 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4937 closable Boolean False to hide the top-right close button (defaults to true). Note that
4938 progress and wait dialogs will ignore this property and always hide the
4939 close button as they can only be closed programmatically.
4940 cls String A custom CSS class to apply to the message box element
4941 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4942 displayed (defaults to 75)
4943 fn Function A callback function to execute after closing the dialog. The arguments to the
4944 function will be btn (the name of the button that was clicked, if applicable,
4945 e.g. "ok"), and text (the value of the active text field, if applicable).
4946 Progress and wait dialogs will ignore this option since they do not respond to
4947 user actions and can only be closed programmatically, so any required function
4948 should be called by the same code after it closes the dialog.
4949 icon String A CSS class that provides a background image to be used as an icon for
4950 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4951 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4952 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4953 modal Boolean False to allow user interaction with the page while the message box is
4954 displayed (defaults to true)
4955 msg String A string that will replace the existing message box body text (defaults
4956 to the XHTML-compliant non-breaking space character ' ')
4957 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4958 progress Boolean True to display a progress bar (defaults to false)
4959 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4960 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4961 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4962 title String The title text
4963 value String The string value to set into the active textbox element if displayed
4964 wait Boolean True to display a progress bar (defaults to false)
4965 width Number The width of the dialog in pixels
4972 msg: 'Please enter your address:',
4974 buttons: Roo.MessageBox.OKCANCEL,
4977 animEl: 'addAddressBtn'
4980 * @param {Object} config Configuration options
4981 * @return {Roo.MessageBox} This message box
4983 show : function(options)
4986 // this causes nightmares if you show one dialog after another
4987 // especially on callbacks..
4989 if(this.isVisible()){
4992 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4993 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
4994 Roo.log("New Dialog Message:" + options.msg )
4995 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4996 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4999 var d = this.getDialog();
5001 d.setTitle(opt.title || " ");
5002 d.closeEl.setDisplayed(opt.closable !== false);
5003 activeTextEl = textboxEl;
5004 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5009 textareaEl.setHeight(typeof opt.multiline == "number" ?
5010 opt.multiline : this.defaultTextHeight);
5011 activeTextEl = textareaEl;
5020 progressEl.setDisplayed(opt.progress === true);
5022 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5024 this.updateProgress(0);
5025 activeTextEl.dom.value = opt.value || "";
5027 dlg.setDefaultButton(activeTextEl);
5029 var bs = opt.buttons;
5033 }else if(bs && bs.yes){
5034 db = buttons["yes"];
5036 dlg.setDefaultButton(db);
5038 bwidth = updateButtons(opt.buttons);
5039 this.updateText(opt.msg);
5041 d.el.addClass(opt.cls);
5043 d.proxyDrag = opt.proxyDrag === true;
5044 d.modal = opt.modal !== false;
5045 d.mask = opt.modal !== false ? mask : false;
5047 // force it to the end of the z-index stack so it gets a cursor in FF
5048 document.body.appendChild(dlg.el.dom);
5049 d.animateTarget = null;
5050 d.show(options.animEl);
5056 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5057 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5058 * and closing the message box when the process is complete.
5059 * @param {String} title The title bar text
5060 * @param {String} msg The message box body text
5061 * @return {Roo.MessageBox} This message box
5063 progress : function(title, msg){
5070 minWidth: this.minProgressWidth,
5077 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5078 * If a callback function is passed it will be called after the user clicks the button, and the
5079 * id of the button that was clicked will be passed as the only parameter to the callback
5080 * (could also be the top-right close button).
5081 * @param {String} title The title bar text
5082 * @param {String} msg The message box body text
5083 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5084 * @param {Object} scope (optional) The scope of the callback function
5085 * @return {Roo.MessageBox} This message box
5087 alert : function(title, msg, fn, scope)
5102 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5103 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5104 * You are responsible for closing the message box when the process is complete.
5105 * @param {String} msg The message box body text
5106 * @param {String} title (optional) The title bar text
5107 * @return {Roo.MessageBox} This message box
5109 wait : function(msg, title){
5120 waitTimer = Roo.TaskMgr.start({
5122 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5130 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5131 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5132 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5133 * @param {String} title The title bar text
5134 * @param {String} msg The message box body text
5135 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5136 * @param {Object} scope (optional) The scope of the callback function
5137 * @return {Roo.MessageBox} This message box
5139 confirm : function(title, msg, fn, scope){
5143 buttons: this.YESNO,
5152 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5153 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5154 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5155 * (could also be the top-right close button) and the text that was entered will be passed as the two
5156 * parameters to the callback.
5157 * @param {String} title The title bar text
5158 * @param {String} msg The message box body text
5159 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5160 * @param {Object} scope (optional) The scope of the callback function
5161 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5162 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5163 * @return {Roo.MessageBox} This message box
5165 prompt : function(title, msg, fn, scope, multiline){
5169 buttons: this.OKCANCEL,
5174 multiline: multiline,
5181 * Button config that displays a single OK button
5186 * Button config that displays Yes and No buttons
5189 YESNO : {yes:true, no:true},
5191 * Button config that displays OK and Cancel buttons
5194 OKCANCEL : {ok:true, cancel:true},
5196 * Button config that displays Yes, No and Cancel buttons
5199 YESNOCANCEL : {yes:true, no:true, cancel:true},
5202 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5205 defaultTextHeight : 75,
5207 * The maximum width in pixels of the message box (defaults to 600)
5212 * The minimum width in pixels of the message box (defaults to 100)
5217 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5218 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5221 minProgressWidth : 250,
5223 * An object containing the default button text strings that can be overriden for localized language support.
5224 * Supported properties are: ok, cancel, yes and no.
5225 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5238 * Shorthand for {@link Roo.MessageBox}
5240 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5241 Roo.Msg = Roo.Msg || Roo.MessageBox;
5250 * @class Roo.bootstrap.Navbar
5251 * @extends Roo.bootstrap.Component
5252 * Bootstrap Navbar class
5255 * Create a new Navbar
5256 * @param {Object} config The config object
5260 Roo.bootstrap.Navbar = function(config){
5261 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5265 * @event beforetoggle
5266 * Fire before toggle the menu
5267 * @param {Roo.EventObject} e
5269 "beforetoggle" : true
5273 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5282 getAutoCreate : function(){
5285 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5289 initEvents :function ()
5291 //Roo.log(this.el.select('.navbar-toggle',true));
5292 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5299 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5301 var size = this.el.getSize();
5302 this.maskEl.setSize(size.width, size.height);
5303 this.maskEl.enableDisplayMode("block");
5312 getChildContainer : function()
5314 if (this.el && this.el.select('.collapse').getCount()) {
5315 return this.el.select('.collapse',true).first();
5330 onToggle : function()
5333 if(this.fireEvent('beforetoggle', this) === false){
5336 var ce = this.el.select('.navbar-collapse',true).first();
5338 if (!ce.hasClass('show')) {
5348 * Expand the navbar pulldown
5350 expand : function ()
5353 var ce = this.el.select('.navbar-collapse',true).first();
5354 if (ce.hasClass('collapsing')) {
5357 ce.dom.style.height = '';
5359 ce.addClass('in'); // old...
5360 ce.removeClass('collapse');
5361 ce.addClass('show');
5362 var h = ce.getHeight();
5364 ce.removeClass('show');
5365 // at this point we should be able to see it..
5366 ce.addClass('collapsing');
5368 ce.setHeight(0); // resize it ...
5369 ce.on('transitionend', function() {
5370 //Roo.log('done transition');
5371 ce.removeClass('collapsing');
5372 ce.addClass('show');
5373 ce.removeClass('collapse');
5375 ce.dom.style.height = '';
5376 }, this, { single: true} );
5378 ce.dom.scrollTop = 0;
5381 * Collapse the navbar pulldown
5383 collapse : function()
5385 var ce = this.el.select('.navbar-collapse',true).first();
5387 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5388 // it's collapsed or collapsing..
5391 ce.removeClass('in'); // old...
5392 ce.setHeight(ce.getHeight());
5393 ce.removeClass('show');
5394 ce.addClass('collapsing');
5396 ce.on('transitionend', function() {
5397 ce.dom.style.height = '';
5398 ce.removeClass('collapsing');
5399 ce.addClass('collapse');
5400 }, this, { single: true} );
5420 * @class Roo.bootstrap.NavSimplebar
5421 * @extends Roo.bootstrap.Navbar
5422 * Bootstrap Sidebar class
5424 * @cfg {Boolean} inverse is inverted color
5426 * @cfg {String} type (nav | pills | tabs)
5427 * @cfg {Boolean} arrangement stacked | justified
5428 * @cfg {String} align (left | right) alignment
5430 * @cfg {Boolean} main (true|false) main nav bar? default false
5431 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5433 * @cfg {String} tag (header|footer|nav|div) default is nav
5435 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5439 * Create a new Sidebar
5440 * @param {Object} config The config object
5444 Roo.bootstrap.NavSimplebar = function(config){
5445 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5448 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5464 getAutoCreate : function(){
5468 tag : this.tag || 'div',
5469 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5471 if (['light','white'].indexOf(this.weight) > -1) {
5472 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5474 cfg.cls += ' bg-' + this.weight;
5477 cfg.cls += ' navbar-inverse';
5481 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5483 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5492 cls: 'nav nav-' + this.xtype,
5498 this.type = this.type || 'nav';
5499 if (['tabs','pills'].indexOf(this.type) != -1) {
5500 cfg.cn[0].cls += ' nav-' + this.type
5504 if (this.type!=='nav') {
5505 Roo.log('nav type must be nav/tabs/pills')
5507 cfg.cn[0].cls += ' navbar-nav'
5513 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5514 cfg.cn[0].cls += ' nav-' + this.arrangement;
5518 if (this.align === 'right') {
5519 cfg.cn[0].cls += ' navbar-right';
5544 * navbar-expand-md fixed-top
5548 * @class Roo.bootstrap.NavHeaderbar
5549 * @extends Roo.bootstrap.NavSimplebar
5550 * Bootstrap Sidebar class
5552 * @cfg {String} brand what is brand
5553 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5554 * @cfg {String} brand_href href of the brand
5555 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5556 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5557 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5558 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5561 * Create a new Sidebar
5562 * @param {Object} config The config object
5566 Roo.bootstrap.NavHeaderbar = function(config){
5567 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5571 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5578 desktopCenter : false,
5581 getAutoCreate : function(){
5584 tag: this.nav || 'nav',
5585 cls: 'navbar navbar-expand-md',
5591 if (this.desktopCenter) {
5592 cn.push({cls : 'container', cn : []});
5600 cls: 'navbar-toggle navbar-toggler',
5601 'data-toggle': 'collapse',
5606 html: 'Toggle navigation'
5610 cls: 'icon-bar navbar-toggler-icon'
5623 cn.push( Roo.bootstrap.version == 4 ? btn : {
5625 cls: 'navbar-header',
5634 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5638 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5640 if (['light','white'].indexOf(this.weight) > -1) {
5641 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5643 cfg.cls += ' bg-' + this.weight;
5646 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5647 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5649 // tag can override this..
5651 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5654 if (this.brand !== '') {
5655 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5656 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5658 href: this.brand_href ? this.brand_href : '#',
5659 cls: 'navbar-brand',
5667 cfg.cls += ' main-nav';
5675 getHeaderChildContainer : function()
5677 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5678 return this.el.select('.navbar-header',true).first();
5681 return this.getChildContainer();
5684 getChildContainer : function()
5687 return this.el.select('.roo-navbar-collapse',true).first();
5692 initEvents : function()
5694 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5696 if (this.autohide) {
5701 Roo.get(document).on('scroll',function(e) {
5702 var ns = Roo.get(document).getScroll().top;
5703 var os = prevScroll;
5707 ft.removeClass('slideDown');
5708 ft.addClass('slideUp');
5711 ft.removeClass('slideUp');
5712 ft.addClass('slideDown');
5733 * @class Roo.bootstrap.NavSidebar
5734 * @extends Roo.bootstrap.Navbar
5735 * Bootstrap Sidebar class
5738 * Create a new Sidebar
5739 * @param {Object} config The config object
5743 Roo.bootstrap.NavSidebar = function(config){
5744 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5747 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5749 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5751 getAutoCreate : function(){
5756 cls: 'sidebar sidebar-nav'
5778 * @class Roo.bootstrap.NavGroup
5779 * @extends Roo.bootstrap.Component
5780 * Bootstrap NavGroup class
5781 * @cfg {String} align (left|right)
5782 * @cfg {Boolean} inverse
5783 * @cfg {String} type (nav|pills|tab) default nav
5784 * @cfg {String} navId - reference Id for navbar.
5785 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5788 * Create a new nav group
5789 * @param {Object} config The config object
5792 Roo.bootstrap.NavGroup = function(config){
5793 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5796 Roo.bootstrap.NavGroup.register(this);
5800 * Fires when the active item changes
5801 * @param {Roo.bootstrap.NavGroup} this
5802 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5803 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5810 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5822 getAutoCreate : function()
5824 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5830 if (Roo.bootstrap.version == 4) {
5831 if (['tabs','pills'].indexOf(this.type) != -1) {
5832 cfg.cls += ' nav-' + this.type;
5834 // trying to remove so header bar can right align top?
5835 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5836 // do not use on header bar...
5837 cfg.cls += ' navbar-nav';
5842 if (['tabs','pills'].indexOf(this.type) != -1) {
5843 cfg.cls += ' nav-' + this.type
5845 if (this.type !== 'nav') {
5846 Roo.log('nav type must be nav/tabs/pills')
5848 cfg.cls += ' navbar-nav'
5852 if (this.parent() && this.parent().sidebar) {
5855 cls: 'dashboard-menu sidebar-menu'
5861 if (this.form === true) {
5864 cls: 'navbar-form form-inline'
5866 //nav navbar-right ml-md-auto
5867 if (this.align === 'right') {
5868 cfg.cls += ' navbar-right ml-md-auto';
5870 cfg.cls += ' navbar-left';
5874 if (this.align === 'right') {
5875 cfg.cls += ' navbar-right ml-md-auto';
5877 cfg.cls += ' mr-auto';
5881 cfg.cls += ' navbar-inverse';
5889 * sets the active Navigation item
5890 * @param {Roo.bootstrap.NavItem} the new current navitem
5892 setActiveItem : function(item)
5895 Roo.each(this.navItems, function(v){
5900 v.setActive(false, true);
5907 item.setActive(true, true);
5908 this.fireEvent('changed', this, item, prev);
5913 * gets the active Navigation item
5914 * @return {Roo.bootstrap.NavItem} the current navitem
5916 getActive : function()
5920 Roo.each(this.navItems, function(v){
5931 indexOfNav : function()
5935 Roo.each(this.navItems, function(v,i){
5946 * adds a Navigation item
5947 * @param {Roo.bootstrap.NavItem} the navitem to add
5949 addItem : function(cfg)
5951 if (this.form && Roo.bootstrap.version == 4) {
5954 var cn = new Roo.bootstrap.NavItem(cfg);
5956 cn.parentId = this.id;
5957 cn.onRender(this.el, null);
5961 * register a Navigation item
5962 * @param {Roo.bootstrap.NavItem} the navitem to add
5964 register : function(item)
5966 this.navItems.push( item);
5967 item.navId = this.navId;
5972 * clear all the Navigation item
5975 clearAll : function()
5978 this.el.dom.innerHTML = '';
5981 getNavItem: function(tabId)
5984 Roo.each(this.navItems, function(e) {
5985 if (e.tabId == tabId) {
5995 setActiveNext : function()
5997 var i = this.indexOfNav(this.getActive());
5998 if (i > this.navItems.length) {
6001 this.setActiveItem(this.navItems[i+1]);
6003 setActivePrev : function()
6005 var i = this.indexOfNav(this.getActive());
6009 this.setActiveItem(this.navItems[i-1]);
6011 clearWasActive : function(except) {
6012 Roo.each(this.navItems, function(e) {
6013 if (e.tabId != except.tabId && e.was_active) {
6014 e.was_active = false;
6021 getWasActive : function ()
6024 Roo.each(this.navItems, function(e) {
6039 Roo.apply(Roo.bootstrap.NavGroup, {
6043 * register a Navigation Group
6044 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6046 register : function(navgrp)
6048 this.groups[navgrp.navId] = navgrp;
6052 * fetch a Navigation Group based on the navigation ID
6053 * @param {string} the navgroup to add
6054 * @returns {Roo.bootstrap.NavGroup} the navgroup
6056 get: function(navId) {
6057 if (typeof(this.groups[navId]) == 'undefined') {
6059 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6061 return this.groups[navId] ;
6076 * @class Roo.bootstrap.NavItem
6077 * @extends Roo.bootstrap.Component
6078 * Bootstrap Navbar.NavItem class
6079 * @cfg {String} href link to
6080 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6081 * @cfg {Boolean} button_outline show and outlined button
6082 * @cfg {String} html content of button
6083 * @cfg {String} badge text inside badge
6084 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6085 * @cfg {String} glyphicon DEPRICATED - use fa
6086 * @cfg {String} icon DEPRICATED - use fa
6087 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6088 * @cfg {Boolean} active Is item active
6089 * @cfg {Boolean} disabled Is item disabled
6090 * @cfg {String} linkcls Link Class
6091 * @cfg {Boolean} preventDefault (true | false) default false
6092 * @cfg {String} tabId the tab that this item activates.
6093 * @cfg {String} tagtype (a|span) render as a href or span?
6094 * @cfg {Boolean} animateRef (true|false) link to element default false
6097 * Create a new Navbar Item
6098 * @param {Object} config The config object
6100 Roo.bootstrap.NavItem = function(config){
6101 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6106 * The raw click event for the entire grid.
6107 * @param {Roo.EventObject} e
6112 * Fires when the active item active state changes
6113 * @param {Roo.bootstrap.NavItem} this
6114 * @param {boolean} state the new state
6120 * Fires when scroll to element
6121 * @param {Roo.bootstrap.NavItem} this
6122 * @param {Object} options
6123 * @param {Roo.EventObject} e
6131 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6140 preventDefault : false,
6148 button_outline : false,
6152 getAutoCreate : function(){
6159 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6162 cfg.cls += ' active' ;
6164 if (this.disabled) {
6165 cfg.cls += ' disabled';
6169 if (this.button_weight.length) {
6170 cfg.tag = this.href ? 'a' : 'button';
6171 cfg.html = this.html || '';
6172 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6174 cfg.href = this.href;
6177 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6180 // menu .. should add dropdown-menu class - so no need for carat..
6182 if (this.badge !== '') {
6184 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6189 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6193 href : this.href || "#",
6194 html: this.html || ''
6197 if (this.tagtype == 'a') {
6198 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6202 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6205 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6207 if(this.glyphicon) {
6208 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6213 cfg.cn[0].html += " <span class='caret'></span>";
6217 if (this.badge !== '') {
6219 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6227 onRender : function(ct, position)
6229 // Roo.log("Call onRender: " + this.xtype);
6230 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6234 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6235 this.navLink = this.el.select('.nav-link',true).first();
6240 initEvents: function()
6242 if (typeof (this.menu) != 'undefined') {
6243 this.menu.parentType = this.xtype;
6244 this.menu.triggerEl = this.el;
6245 this.menu = this.addxtype(Roo.apply({}, this.menu));
6248 this.el.on('click', this.onClick, this);
6250 //if(this.tagtype == 'span'){
6251 // this.el.select('span',true).on('click', this.onClick, this);
6254 // at this point parent should be available..
6255 this.parent().register(this);
6258 onClick : function(e)
6260 if (e.getTarget('.dropdown-menu-item')) {
6261 // did you click on a menu itemm.... - then don't trigger onclick..
6266 this.preventDefault ||
6269 Roo.log("NavItem - prevent Default?");
6273 if (this.disabled) {
6277 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6278 if (tg && tg.transition) {
6279 Roo.log("waiting for the transitionend");
6285 //Roo.log("fire event clicked");
6286 if(this.fireEvent('click', this, e) === false){
6290 if(this.tagtype == 'span'){
6294 //Roo.log(this.href);
6295 var ael = this.el.select('a',true).first();
6298 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6299 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6300 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6301 return; // ignore... - it's a 'hash' to another page.
6303 Roo.log("NavItem - prevent Default?");
6305 this.scrollToElement(e);
6309 var p = this.parent();
6311 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6312 if (typeof(p.setActiveItem) !== 'undefined') {
6313 p.setActiveItem(this);
6317 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6318 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6319 // remove the collapsed menu expand...
6320 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6324 isActive: function () {
6327 setActive : function(state, fire, is_was_active)
6329 if (this.active && !state && this.navId) {
6330 this.was_active = true;
6331 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6333 nv.clearWasActive(this);
6337 this.active = state;
6340 this.el.removeClass('active');
6341 this.navLink ? this.navLink.removeClass('active') : false;
6342 } else if (!this.el.hasClass('active')) {
6344 this.el.addClass('active');
6345 if (Roo.bootstrap.version == 4 && this.navLink ) {
6346 this.navLink.addClass('active');
6351 this.fireEvent('changed', this, state);
6354 // show a panel if it's registered and related..
6356 if (!this.navId || !this.tabId || !state || is_was_active) {
6360 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6364 var pan = tg.getPanelByName(this.tabId);
6368 // if we can not flip to new panel - go back to old nav highlight..
6369 if (false == tg.showPanel(pan)) {
6370 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6372 var onav = nv.getWasActive();
6374 onav.setActive(true, false, true);
6383 // this should not be here...
6384 setDisabled : function(state)
6386 this.disabled = state;
6388 this.el.removeClass('disabled');
6389 } else if (!this.el.hasClass('disabled')) {
6390 this.el.addClass('disabled');
6396 * Fetch the element to display the tooltip on.
6397 * @return {Roo.Element} defaults to this.el
6399 tooltipEl : function()
6401 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6404 scrollToElement : function(e)
6406 var c = document.body;
6409 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6411 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6412 c = document.documentElement;
6415 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6421 var o = target.calcOffsetsTo(c);
6428 this.fireEvent('scrollto', this, options, e);
6430 Roo.get(c).scrollTo('top', options.value, true);
6443 * <span> icon </span>
6444 * <span> text </span>
6445 * <span>badge </span>
6449 * @class Roo.bootstrap.NavSidebarItem
6450 * @extends Roo.bootstrap.NavItem
6451 * Bootstrap Navbar.NavSidebarItem class
6452 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6453 * {Boolean} open is the menu open
6454 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6455 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6456 * {String} buttonSize (sm|md|lg)the extra classes for the button
6457 * {Boolean} showArrow show arrow next to the text (default true)
6459 * Create a new Navbar Button
6460 * @param {Object} config The config object
6462 Roo.bootstrap.NavSidebarItem = function(config){
6463 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6468 * The raw click event for the entire grid.
6469 * @param {Roo.EventObject} e
6474 * Fires when the active item active state changes
6475 * @param {Roo.bootstrap.NavSidebarItem} this
6476 * @param {boolean} state the new state
6484 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6486 badgeWeight : 'default',
6492 buttonWeight : 'default',
6498 getAutoCreate : function(){
6503 href : this.href || '#',
6509 if(this.buttonView){
6512 href : this.href || '#',
6513 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6526 cfg.cls += ' active';
6529 if (this.disabled) {
6530 cfg.cls += ' disabled';
6533 cfg.cls += ' open x-open';
6536 if (this.glyphicon || this.icon) {
6537 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6538 a.cn.push({ tag : 'i', cls : c }) ;
6541 if(!this.buttonView){
6544 html : this.html || ''
6551 if (this.badge !== '') {
6552 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6558 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6561 a.cls += ' dropdown-toggle treeview' ;
6567 initEvents : function()
6569 if (typeof (this.menu) != 'undefined') {
6570 this.menu.parentType = this.xtype;
6571 this.menu.triggerEl = this.el;
6572 this.menu = this.addxtype(Roo.apply({}, this.menu));
6575 this.el.on('click', this.onClick, this);
6577 if(this.badge !== ''){
6578 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6583 onClick : function(e)
6590 if(this.preventDefault){
6594 this.fireEvent('click', this, e);
6597 disable : function()
6599 this.setDisabled(true);
6604 this.setDisabled(false);
6607 setDisabled : function(state)
6609 if(this.disabled == state){
6613 this.disabled = state;
6616 this.el.addClass('disabled');
6620 this.el.removeClass('disabled');
6625 setActive : function(state)
6627 if(this.active == state){
6631 this.active = state;
6634 this.el.addClass('active');
6638 this.el.removeClass('active');
6643 isActive: function ()
6648 setBadge : function(str)
6654 this.badgeEl.dom.innerHTML = str;
6669 Roo.namespace('Roo.bootstrap.breadcrumb');
6673 * @class Roo.bootstrap.breadcrumb.Nav
6674 * @extends Roo.bootstrap.Component
6675 * Bootstrap Breadcrumb Nav Class
6677 * @children Roo.bootstrap.breadcrumb.Item
6680 * Create a new breadcrumb.Nav
6681 * @param {Object} config The config object
6685 Roo.bootstrap.breadcrumb.Nav = function(config){
6686 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6691 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6693 getAutoCreate : function()
6710 initEvents: function()
6712 this.olEl = this.el.select('ol',true).first();
6714 getChildContainer : function()
6730 * @class Roo.bootstrap.breadcrumb.Nav
6731 * @extends Roo.bootstrap.Component
6732 * Bootstrap Breadcrumb Nav Class
6734 * @children Roo.bootstrap.breadcrumb.Component
6735 * @cfg {String} html the content of the link.
6736 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6737 * @cfg {Boolean} active is it active
6741 * Create a new breadcrumb.Nav
6742 * @param {Object} config The config object
6745 Roo.bootstrap.breadcrumb.Item = function(config){
6746 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6751 * The img click event for the img.
6752 * @param {Roo.EventObject} e
6759 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6764 getAutoCreate : function()
6769 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6771 if (this.href !== false) {
6778 cfg.html = this.html;
6784 initEvents: function()
6787 this.el.select('a', true).first().on('click',this.onClick, this)
6791 onClick : function(e)
6794 this.fireEvent('click',this, e);
6807 * @class Roo.bootstrap.Row
6808 * @extends Roo.bootstrap.Component
6809 * Bootstrap Row class (contains columns...)
6813 * @param {Object} config The config object
6816 Roo.bootstrap.Row = function(config){
6817 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6820 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6822 getAutoCreate : function(){
6841 * @class Roo.bootstrap.Pagination
6842 * @extends Roo.bootstrap.Component
6843 * Bootstrap Pagination class
6844 * @cfg {String} size xs | sm | md | lg
6845 * @cfg {Boolean} inverse false | true
6848 * Create a new Pagination
6849 * @param {Object} config The config object
6852 Roo.bootstrap.Pagination = function(config){
6853 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6856 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6862 getAutoCreate : function(){
6868 cfg.cls += ' inverse';
6874 cfg.cls += " " + this.cls;
6892 * @class Roo.bootstrap.PaginationItem
6893 * @extends Roo.bootstrap.Component
6894 * Bootstrap PaginationItem class
6895 * @cfg {String} html text
6896 * @cfg {String} href the link
6897 * @cfg {Boolean} preventDefault (true | false) default true
6898 * @cfg {Boolean} active (true | false) default false
6899 * @cfg {Boolean} disabled default false
6903 * Create a new PaginationItem
6904 * @param {Object} config The config object
6908 Roo.bootstrap.PaginationItem = function(config){
6909 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6914 * The raw click event for the entire grid.
6915 * @param {Roo.EventObject} e
6921 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6925 preventDefault: true,
6930 getAutoCreate : function(){
6936 href : this.href ? this.href : '#',
6937 html : this.html ? this.html : ''
6947 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6951 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6957 initEvents: function() {
6959 this.el.on('click', this.onClick, this);
6962 onClick : function(e)
6964 Roo.log('PaginationItem on click ');
6965 if(this.preventDefault){
6973 this.fireEvent('click', this, e);
6989 * @class Roo.bootstrap.Slider
6990 * @extends Roo.bootstrap.Component
6991 * Bootstrap Slider class
6994 * Create a new Slider
6995 * @param {Object} config The config object
6998 Roo.bootstrap.Slider = function(config){
6999 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7002 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7004 getAutoCreate : function(){
7008 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7012 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7024 * Ext JS Library 1.1.1
7025 * Copyright(c) 2006-2007, Ext JS, LLC.
7027 * Originally Released Under LGPL - original licence link has changed is not relivant.
7030 * <script type="text/javascript">
7035 * @class Roo.grid.ColumnModel
7036 * @extends Roo.util.Observable
7037 * This is the default implementation of a ColumnModel used by the Grid. It defines
7038 * the columns in the grid.
7041 var colModel = new Roo.grid.ColumnModel([
7042 {header: "Ticker", width: 60, sortable: true, locked: true},
7043 {header: "Company Name", width: 150, sortable: true},
7044 {header: "Market Cap.", width: 100, sortable: true},
7045 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7046 {header: "Employees", width: 100, sortable: true, resizable: false}
7051 * The config options listed for this class are options which may appear in each
7052 * individual column definition.
7053 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7055 * @param {Object} config An Array of column config objects. See this class's
7056 * config objects for details.
7058 Roo.grid.ColumnModel = function(config){
7060 * The config passed into the constructor
7062 this.config = config;
7065 // if no id, create one
7066 // if the column does not have a dataIndex mapping,
7067 // map it to the order it is in the config
7068 for(var i = 0, len = config.length; i < len; i++){
7070 if(typeof c.dataIndex == "undefined"){
7073 if(typeof c.renderer == "string"){
7074 c.renderer = Roo.util.Format[c.renderer];
7076 if(typeof c.id == "undefined"){
7079 if(c.editor && c.editor.xtype){
7080 c.editor = Roo.factory(c.editor, Roo.grid);
7082 if(c.editor && c.editor.isFormField){
7083 c.editor = new Roo.grid.GridEditor(c.editor);
7085 this.lookup[c.id] = c;
7089 * The width of columns which have no width specified (defaults to 100)
7092 this.defaultWidth = 100;
7095 * Default sortable of columns which have no sortable specified (defaults to false)
7098 this.defaultSortable = false;
7102 * @event widthchange
7103 * Fires when the width of a column changes.
7104 * @param {ColumnModel} this
7105 * @param {Number} columnIndex The column index
7106 * @param {Number} newWidth The new width
7108 "widthchange": true,
7110 * @event headerchange
7111 * Fires when the text of a header changes.
7112 * @param {ColumnModel} this
7113 * @param {Number} columnIndex The column index
7114 * @param {Number} newText The new header text
7116 "headerchange": true,
7118 * @event hiddenchange
7119 * Fires when a column is hidden or "unhidden".
7120 * @param {ColumnModel} this
7121 * @param {Number} columnIndex The column index
7122 * @param {Boolean} hidden true if hidden, false otherwise
7124 "hiddenchange": true,
7126 * @event columnmoved
7127 * Fires when a column is moved.
7128 * @param {ColumnModel} this
7129 * @param {Number} oldIndex
7130 * @param {Number} newIndex
7132 "columnmoved" : true,
7134 * @event columlockchange
7135 * Fires when a column's locked state is changed
7136 * @param {ColumnModel} this
7137 * @param {Number} colIndex
7138 * @param {Boolean} locked true if locked
7140 "columnlockchange" : true
7142 Roo.grid.ColumnModel.superclass.constructor.call(this);
7144 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7146 * @cfg {String} header The header text to display in the Grid view.
7149 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7150 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7151 * specified, the column's index is used as an index into the Record's data Array.
7154 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7155 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7158 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7159 * Defaults to the value of the {@link #defaultSortable} property.
7160 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7163 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7166 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7169 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7172 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7175 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7176 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7177 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7178 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7181 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7184 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7187 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7190 * @cfg {String} cursor (Optional)
7193 * @cfg {String} tooltip (Optional)
7196 * @cfg {Number} xs (Optional)
7199 * @cfg {Number} sm (Optional)
7202 * @cfg {Number} md (Optional)
7205 * @cfg {Number} lg (Optional)
7208 * Returns the id of the column at the specified index.
7209 * @param {Number} index The column index
7210 * @return {String} the id
7212 getColumnId : function(index){
7213 return this.config[index].id;
7217 * Returns the column for a specified id.
7218 * @param {String} id The column id
7219 * @return {Object} the column
7221 getColumnById : function(id){
7222 return this.lookup[id];
7227 * Returns the column for a specified dataIndex.
7228 * @param {String} dataIndex The column dataIndex
7229 * @return {Object|Boolean} the column or false if not found
7231 getColumnByDataIndex: function(dataIndex){
7232 var index = this.findColumnIndex(dataIndex);
7233 return index > -1 ? this.config[index] : false;
7237 * Returns the index for a specified column id.
7238 * @param {String} id The column id
7239 * @return {Number} the index, or -1 if not found
7241 getIndexById : function(id){
7242 for(var i = 0, len = this.config.length; i < len; i++){
7243 if(this.config[i].id == id){
7251 * Returns the index for a specified column dataIndex.
7252 * @param {String} dataIndex The column dataIndex
7253 * @return {Number} the index, or -1 if not found
7256 findColumnIndex : function(dataIndex){
7257 for(var i = 0, len = this.config.length; i < len; i++){
7258 if(this.config[i].dataIndex == dataIndex){
7266 moveColumn : function(oldIndex, newIndex){
7267 var c = this.config[oldIndex];
7268 this.config.splice(oldIndex, 1);
7269 this.config.splice(newIndex, 0, c);
7270 this.dataMap = null;
7271 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7274 isLocked : function(colIndex){
7275 return this.config[colIndex].locked === true;
7278 setLocked : function(colIndex, value, suppressEvent){
7279 if(this.isLocked(colIndex) == value){
7282 this.config[colIndex].locked = value;
7284 this.fireEvent("columnlockchange", this, colIndex, value);
7288 getTotalLockedWidth : function(){
7290 for(var i = 0; i < this.config.length; i++){
7291 if(this.isLocked(i) && !this.isHidden(i)){
7292 this.totalWidth += this.getColumnWidth(i);
7298 getLockedCount : function(){
7299 for(var i = 0, len = this.config.length; i < len; i++){
7300 if(!this.isLocked(i)){
7305 return this.config.length;
7309 * Returns the number of columns.
7312 getColumnCount : function(visibleOnly){
7313 if(visibleOnly === true){
7315 for(var i = 0, len = this.config.length; i < len; i++){
7316 if(!this.isHidden(i)){
7322 return this.config.length;
7326 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7327 * @param {Function} fn
7328 * @param {Object} scope (optional)
7329 * @return {Array} result
7331 getColumnsBy : function(fn, scope){
7333 for(var i = 0, len = this.config.length; i < len; i++){
7334 var c = this.config[i];
7335 if(fn.call(scope||this, c, i) === true){
7343 * Returns true if the specified column is sortable.
7344 * @param {Number} col The column index
7347 isSortable : function(col){
7348 if(typeof this.config[col].sortable == "undefined"){
7349 return this.defaultSortable;
7351 return this.config[col].sortable;
7355 * Returns the rendering (formatting) function defined for the column.
7356 * @param {Number} col The column index.
7357 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7359 getRenderer : function(col){
7360 if(!this.config[col].renderer){
7361 return Roo.grid.ColumnModel.defaultRenderer;
7363 return this.config[col].renderer;
7367 * Sets the rendering (formatting) function for a column.
7368 * @param {Number} col The column index
7369 * @param {Function} fn The function to use to process the cell's raw data
7370 * to return HTML markup for the grid view. The render function is called with
7371 * the following parameters:<ul>
7372 * <li>Data value.</li>
7373 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7374 * <li>css A CSS style string to apply to the table cell.</li>
7375 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7376 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7377 * <li>Row index</li>
7378 * <li>Column index</li>
7379 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7381 setRenderer : function(col, fn){
7382 this.config[col].renderer = fn;
7386 * Returns the width for the specified column.
7387 * @param {Number} col The column index
7390 getColumnWidth : function(col){
7391 return this.config[col].width * 1 || this.defaultWidth;
7395 * Sets the width for a column.
7396 * @param {Number} col The column index
7397 * @param {Number} width The new width
7399 setColumnWidth : function(col, width, suppressEvent){
7400 this.config[col].width = width;
7401 this.totalWidth = null;
7403 this.fireEvent("widthchange", this, col, width);
7408 * Returns the total width of all columns.
7409 * @param {Boolean} includeHidden True to include hidden column widths
7412 getTotalWidth : function(includeHidden){
7413 if(!this.totalWidth){
7414 this.totalWidth = 0;
7415 for(var i = 0, len = this.config.length; i < len; i++){
7416 if(includeHidden || !this.isHidden(i)){
7417 this.totalWidth += this.getColumnWidth(i);
7421 return this.totalWidth;
7425 * Returns the header for the specified column.
7426 * @param {Number} col The column index
7429 getColumnHeader : function(col){
7430 return this.config[col].header;
7434 * Sets the header for a column.
7435 * @param {Number} col The column index
7436 * @param {String} header The new header
7438 setColumnHeader : function(col, header){
7439 this.config[col].header = header;
7440 this.fireEvent("headerchange", this, col, header);
7444 * Returns the tooltip for the specified column.
7445 * @param {Number} col The column index
7448 getColumnTooltip : function(col){
7449 return this.config[col].tooltip;
7452 * Sets the tooltip for a column.
7453 * @param {Number} col The column index
7454 * @param {String} tooltip The new tooltip
7456 setColumnTooltip : function(col, tooltip){
7457 this.config[col].tooltip = tooltip;
7461 * Returns the dataIndex for the specified column.
7462 * @param {Number} col The column index
7465 getDataIndex : function(col){
7466 return this.config[col].dataIndex;
7470 * Sets the dataIndex for a column.
7471 * @param {Number} col The column index
7472 * @param {Number} dataIndex The new dataIndex
7474 setDataIndex : function(col, dataIndex){
7475 this.config[col].dataIndex = dataIndex;
7481 * Returns true if the cell is editable.
7482 * @param {Number} colIndex The column index
7483 * @param {Number} rowIndex The row index - this is nto actually used..?
7486 isCellEditable : function(colIndex, rowIndex){
7487 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7491 * Returns the editor defined for the cell/column.
7492 * return false or null to disable editing.
7493 * @param {Number} colIndex The column index
7494 * @param {Number} rowIndex The row index
7497 getCellEditor : function(colIndex, rowIndex){
7498 return this.config[colIndex].editor;
7502 * Sets if a column is editable.
7503 * @param {Number} col The column index
7504 * @param {Boolean} editable True if the column is editable
7506 setEditable : function(col, editable){
7507 this.config[col].editable = editable;
7512 * Returns true if the column is hidden.
7513 * @param {Number} colIndex The column index
7516 isHidden : function(colIndex){
7517 return this.config[colIndex].hidden;
7522 * Returns true if the column width cannot be changed
7524 isFixed : function(colIndex){
7525 return this.config[colIndex].fixed;
7529 * Returns true if the column can be resized
7532 isResizable : function(colIndex){
7533 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7536 * Sets if a column is hidden.
7537 * @param {Number} colIndex The column index
7538 * @param {Boolean} hidden True if the column is hidden
7540 setHidden : function(colIndex, hidden){
7541 this.config[colIndex].hidden = hidden;
7542 this.totalWidth = null;
7543 this.fireEvent("hiddenchange", this, colIndex, hidden);
7547 * Sets the editor for a column.
7548 * @param {Number} col The column index
7549 * @param {Object} editor The editor object
7551 setEditor : function(col, editor){
7552 this.config[col].editor = editor;
7556 Roo.grid.ColumnModel.defaultRenderer = function(value)
7558 if(typeof value == "object") {
7561 if(typeof value == "string" && value.length < 1){
7565 return String.format("{0}", value);
7568 // Alias for backwards compatibility
7569 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7572 * Ext JS Library 1.1.1
7573 * Copyright(c) 2006-2007, Ext JS, LLC.
7575 * Originally Released Under LGPL - original licence link has changed is not relivant.
7578 * <script type="text/javascript">
7582 * @class Roo.LoadMask
7583 * A simple utility class for generically masking elements while loading data. If the element being masked has
7584 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7585 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7586 * element's UpdateManager load indicator and will be destroyed after the initial load.
7588 * Create a new LoadMask
7589 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7590 * @param {Object} config The config object
7592 Roo.LoadMask = function(el, config){
7593 this.el = Roo.get(el);
7594 Roo.apply(this, config);
7596 this.store.on('beforeload', this.onBeforeLoad, this);
7597 this.store.on('load', this.onLoad, this);
7598 this.store.on('loadexception', this.onLoadException, this);
7599 this.removeMask = false;
7601 var um = this.el.getUpdateManager();
7602 um.showLoadIndicator = false; // disable the default indicator
7603 um.on('beforeupdate', this.onBeforeLoad, this);
7604 um.on('update', this.onLoad, this);
7605 um.on('failure', this.onLoad, this);
7606 this.removeMask = true;
7610 Roo.LoadMask.prototype = {
7612 * @cfg {Boolean} removeMask
7613 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7614 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7618 * The text to display in a centered loading message box (defaults to 'Loading...')
7622 * @cfg {String} msgCls
7623 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7625 msgCls : 'x-mask-loading',
7628 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7634 * Disables the mask to prevent it from being displayed
7636 disable : function(){
7637 this.disabled = true;
7641 * Enables the mask so that it can be displayed
7643 enable : function(){
7644 this.disabled = false;
7647 onLoadException : function()
7651 if (typeof(arguments[3]) != 'undefined') {
7652 Roo.MessageBox.alert("Error loading",arguments[3]);
7656 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7657 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7664 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7669 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7673 onBeforeLoad : function(){
7675 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7680 destroy : function(){
7682 this.store.un('beforeload', this.onBeforeLoad, this);
7683 this.store.un('load', this.onLoad, this);
7684 this.store.un('loadexception', this.onLoadException, this);
7686 var um = this.el.getUpdateManager();
7687 um.un('beforeupdate', this.onBeforeLoad, this);
7688 um.un('update', this.onLoad, this);
7689 um.un('failure', this.onLoad, this);
7700 * @class Roo.bootstrap.Table
7701 * @extends Roo.bootstrap.Component
7702 * Bootstrap Table class
7703 * @cfg {String} cls table class
7704 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7705 * @cfg {String} bgcolor Specifies the background color for a table
7706 * @cfg {Number} border Specifies whether the table cells should have borders or not
7707 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7708 * @cfg {Number} cellspacing Specifies the space between cells
7709 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7710 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7711 * @cfg {String} sortable Specifies that the table should be sortable
7712 * @cfg {String} summary Specifies a summary of the content of a table
7713 * @cfg {Number} width Specifies the width of a table
7714 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7716 * @cfg {boolean} striped Should the rows be alternative striped
7717 * @cfg {boolean} bordered Add borders to the table
7718 * @cfg {boolean} hover Add hover highlighting
7719 * @cfg {boolean} condensed Format condensed
7720 * @cfg {boolean} responsive Format condensed
7721 * @cfg {Boolean} loadMask (true|false) default false
7722 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7723 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7724 * @cfg {Boolean} rowSelection (true|false) default false
7725 * @cfg {Boolean} cellSelection (true|false) default false
7726 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7727 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7728 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7729 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7733 * Create a new Table
7734 * @param {Object} config The config object
7737 Roo.bootstrap.Table = function(config){
7738 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7743 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7744 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7745 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7746 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7748 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7750 this.sm.grid = this;
7751 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7752 this.sm = this.selModel;
7753 this.sm.xmodule = this.xmodule || false;
7756 if (this.cm && typeof(this.cm.config) == 'undefined') {
7757 this.colModel = new Roo.grid.ColumnModel(this.cm);
7758 this.cm = this.colModel;
7759 this.cm.xmodule = this.xmodule || false;
7762 this.store= Roo.factory(this.store, Roo.data);
7763 this.ds = this.store;
7764 this.ds.xmodule = this.xmodule || false;
7767 if (this.footer && this.store) {
7768 this.footer.dataSource = this.ds;
7769 this.footer = Roo.factory(this.footer);
7776 * Fires when a cell is clicked
7777 * @param {Roo.bootstrap.Table} this
7778 * @param {Roo.Element} el
7779 * @param {Number} rowIndex
7780 * @param {Number} columnIndex
7781 * @param {Roo.EventObject} e
7785 * @event celldblclick
7786 * Fires when a cell is double clicked
7787 * @param {Roo.bootstrap.Table} this
7788 * @param {Roo.Element} el
7789 * @param {Number} rowIndex
7790 * @param {Number} columnIndex
7791 * @param {Roo.EventObject} e
7793 "celldblclick" : true,
7796 * Fires when a row is clicked
7797 * @param {Roo.bootstrap.Table} this
7798 * @param {Roo.Element} el
7799 * @param {Number} rowIndex
7800 * @param {Roo.EventObject} e
7804 * @event rowdblclick
7805 * Fires when a row is double clicked
7806 * @param {Roo.bootstrap.Table} this
7807 * @param {Roo.Element} el
7808 * @param {Number} rowIndex
7809 * @param {Roo.EventObject} e
7811 "rowdblclick" : true,
7814 * Fires when a mouseover occur
7815 * @param {Roo.bootstrap.Table} this
7816 * @param {Roo.Element} el
7817 * @param {Number} rowIndex
7818 * @param {Number} columnIndex
7819 * @param {Roo.EventObject} e
7824 * Fires when a mouseout occur
7825 * @param {Roo.bootstrap.Table} this
7826 * @param {Roo.Element} el
7827 * @param {Number} rowIndex
7828 * @param {Number} columnIndex
7829 * @param {Roo.EventObject} e
7834 * Fires when a row is rendered, so you can change add a style to it.
7835 * @param {Roo.bootstrap.Table} this
7836 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7840 * @event rowsrendered
7841 * Fires when all the rows have been rendered
7842 * @param {Roo.bootstrap.Table} this
7844 'rowsrendered' : true,
7846 * @event contextmenu
7847 * The raw contextmenu event for the entire grid.
7848 * @param {Roo.EventObject} e
7850 "contextmenu" : true,
7852 * @event rowcontextmenu
7853 * Fires when a row is right clicked
7854 * @param {Roo.bootstrap.Table} this
7855 * @param {Number} rowIndex
7856 * @param {Roo.EventObject} e
7858 "rowcontextmenu" : true,
7860 * @event cellcontextmenu
7861 * Fires when a cell is right clicked
7862 * @param {Roo.bootstrap.Table} this
7863 * @param {Number} rowIndex
7864 * @param {Number} cellIndex
7865 * @param {Roo.EventObject} e
7867 "cellcontextmenu" : true,
7869 * @event headercontextmenu
7870 * Fires when a header is right clicked
7871 * @param {Roo.bootstrap.Table} this
7872 * @param {Number} columnIndex
7873 * @param {Roo.EventObject} e
7875 "headercontextmenu" : true
7879 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7905 rowSelection : false,
7906 cellSelection : false,
7909 // Roo.Element - the tbody
7911 // Roo.Element - thead element
7914 container: false, // used by gridpanel...
7920 auto_hide_footer : false,
7922 getAutoCreate : function()
7924 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7931 if (this.scrollBody) {
7932 cfg.cls += ' table-body-fixed';
7935 cfg.cls += ' table-striped';
7939 cfg.cls += ' table-hover';
7941 if (this.bordered) {
7942 cfg.cls += ' table-bordered';
7944 if (this.condensed) {
7945 cfg.cls += ' table-condensed';
7947 if (this.responsive) {
7948 cfg.cls += ' table-responsive';
7952 cfg.cls+= ' ' +this.cls;
7955 // this lot should be simplifed...
7968 ].forEach(function(k) {
7976 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7979 if(this.store || this.cm){
7980 if(this.headerShow){
7981 cfg.cn.push(this.renderHeader());
7984 cfg.cn.push(this.renderBody());
7986 if(this.footerShow){
7987 cfg.cn.push(this.renderFooter());
7989 // where does this come from?
7990 //cfg.cls+= ' TableGrid';
7993 return { cn : [ cfg ] };
7996 initEvents : function()
7998 if(!this.store || !this.cm){
8001 if (this.selModel) {
8002 this.selModel.initEvents();
8006 //Roo.log('initEvents with ds!!!!');
8008 this.mainBody = this.el.select('tbody', true).first();
8009 this.mainHead = this.el.select('thead', true).first();
8010 this.mainFoot = this.el.select('tfoot', true).first();
8016 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8017 e.on('click', _this.sort, _this);
8020 this.mainBody.on("click", this.onClick, this);
8021 this.mainBody.on("dblclick", this.onDblClick, this);
8023 // why is this done????? = it breaks dialogs??
8024 //this.parent().el.setStyle('position', 'relative');
8028 this.footer.parentId = this.id;
8029 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8032 this.el.select('tfoot tr td').first().addClass('hide');
8037 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8040 this.store.on('load', this.onLoad, this);
8041 this.store.on('beforeload', this.onBeforeLoad, this);
8042 this.store.on('update', this.onUpdate, this);
8043 this.store.on('add', this.onAdd, this);
8044 this.store.on("clear", this.clear, this);
8046 this.el.on("contextmenu", this.onContextMenu, this);
8048 this.mainBody.on('scroll', this.onBodyScroll, this);
8050 this.cm.on("headerchange", this.onHeaderChange, this);
8052 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8056 onContextMenu : function(e, t)
8058 this.processEvent("contextmenu", e);
8061 processEvent : function(name, e)
8063 if (name != 'touchstart' ) {
8064 this.fireEvent(name, e);
8067 var t = e.getTarget();
8069 var cell = Roo.get(t);
8075 if(cell.findParent('tfoot', false, true)){
8079 if(cell.findParent('thead', false, true)){
8081 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8082 cell = Roo.get(t).findParent('th', false, true);
8084 Roo.log("failed to find th in thead?");
8085 Roo.log(e.getTarget());
8090 var cellIndex = cell.dom.cellIndex;
8092 var ename = name == 'touchstart' ? 'click' : name;
8093 this.fireEvent("header" + ename, this, cellIndex, e);
8098 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8099 cell = Roo.get(t).findParent('td', false, true);
8101 Roo.log("failed to find th in tbody?");
8102 Roo.log(e.getTarget());
8107 var row = cell.findParent('tr', false, true);
8108 var cellIndex = cell.dom.cellIndex;
8109 var rowIndex = row.dom.rowIndex - 1;
8113 this.fireEvent("row" + name, this, rowIndex, e);
8117 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8123 onMouseover : function(e, el)
8125 var cell = Roo.get(el);
8131 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8132 cell = cell.findParent('td', false, true);
8135 var row = cell.findParent('tr', false, true);
8136 var cellIndex = cell.dom.cellIndex;
8137 var rowIndex = row.dom.rowIndex - 1; // start from 0
8139 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8143 onMouseout : function(e, el)
8145 var cell = Roo.get(el);
8151 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8152 cell = cell.findParent('td', false, true);
8155 var row = cell.findParent('tr', false, true);
8156 var cellIndex = cell.dom.cellIndex;
8157 var rowIndex = row.dom.rowIndex - 1; // start from 0
8159 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8163 onClick : function(e, el)
8165 var cell = Roo.get(el);
8167 if(!cell || (!this.cellSelection && !this.rowSelection)){
8171 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8172 cell = cell.findParent('td', false, true);
8175 if(!cell || typeof(cell) == 'undefined'){
8179 var row = cell.findParent('tr', false, true);
8181 if(!row || typeof(row) == 'undefined'){
8185 var cellIndex = cell.dom.cellIndex;
8186 var rowIndex = this.getRowIndex(row);
8188 // why??? - should these not be based on SelectionModel?
8189 if(this.cellSelection){
8190 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8193 if(this.rowSelection){
8194 this.fireEvent('rowclick', this, row, rowIndex, e);
8200 onDblClick : function(e,el)
8202 var cell = Roo.get(el);
8204 if(!cell || (!this.cellSelection && !this.rowSelection)){
8208 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8209 cell = cell.findParent('td', false, true);
8212 if(!cell || typeof(cell) == 'undefined'){
8216 var row = cell.findParent('tr', false, true);
8218 if(!row || typeof(row) == 'undefined'){
8222 var cellIndex = cell.dom.cellIndex;
8223 var rowIndex = this.getRowIndex(row);
8225 if(this.cellSelection){
8226 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8229 if(this.rowSelection){
8230 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8234 sort : function(e,el)
8236 var col = Roo.get(el);
8238 if(!col.hasClass('sortable')){
8242 var sort = col.attr('sort');
8245 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8249 this.store.sortInfo = {field : sort, direction : dir};
8252 Roo.log("calling footer first");
8253 this.footer.onClick('first');
8256 this.store.load({ params : { start : 0 } });
8260 renderHeader : function()
8268 this.totalWidth = 0;
8270 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8272 var config = cm.config[i];
8276 cls : 'x-hcol-' + i,
8278 html: cm.getColumnHeader(i)
8283 if(typeof(config.sortable) != 'undefined' && config.sortable){
8285 c.html = '<i class="glyphicon"></i>' + c.html;
8288 // could use BS4 hidden-..-down
8290 if(typeof(config.lgHeader) != 'undefined'){
8291 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8294 if(typeof(config.mdHeader) != 'undefined'){
8295 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8298 if(typeof(config.smHeader) != 'undefined'){
8299 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8302 if(typeof(config.xsHeader) != 'undefined'){
8303 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8310 if(typeof(config.tooltip) != 'undefined'){
8311 c.tooltip = config.tooltip;
8314 if(typeof(config.colspan) != 'undefined'){
8315 c.colspan = config.colspan;
8318 if(typeof(config.hidden) != 'undefined' && config.hidden){
8319 c.style += ' display:none;';
8322 if(typeof(config.dataIndex) != 'undefined'){
8323 c.sort = config.dataIndex;
8328 if(typeof(config.align) != 'undefined' && config.align.length){
8329 c.style += ' text-align:' + config.align + ';';
8332 if(typeof(config.width) != 'undefined'){
8333 c.style += ' width:' + config.width + 'px;';
8334 this.totalWidth += config.width;
8336 this.totalWidth += 100; // assume minimum of 100 per column?
8339 if(typeof(config.cls) != 'undefined'){
8340 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8343 ['xs','sm','md','lg'].map(function(size){
8345 if(typeof(config[size]) == 'undefined'){
8349 if (!config[size]) { // 0 = hidden
8350 // BS 4 '0' is treated as hide that column and below.
8351 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8355 c.cls += ' col-' + size + '-' + config[size] + (
8356 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8368 renderBody : function()
8378 colspan : this.cm.getColumnCount()
8388 renderFooter : function()
8398 colspan : this.cm.getColumnCount()
8412 // Roo.log('ds onload');
8417 var ds = this.store;
8419 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8420 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8421 if (_this.store.sortInfo) {
8423 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8424 e.select('i', true).addClass(['glyphicon-arrow-up']);
8427 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8428 e.select('i', true).addClass(['glyphicon-arrow-down']);
8433 var tbody = this.mainBody;
8435 if(ds.getCount() > 0){
8436 ds.data.each(function(d,rowIndex){
8437 var row = this.renderRow(cm, ds, rowIndex);
8439 tbody.createChild(row);
8443 if(row.cellObjects.length){
8444 Roo.each(row.cellObjects, function(r){
8445 _this.renderCellObject(r);
8452 var tfoot = this.el.select('tfoot', true).first();
8454 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8456 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8458 var total = this.ds.getTotalCount();
8460 if(this.footer.pageSize < total){
8461 this.mainFoot.show();
8465 Roo.each(this.el.select('tbody td', true).elements, function(e){
8466 e.on('mouseover', _this.onMouseover, _this);
8469 Roo.each(this.el.select('tbody td', true).elements, function(e){
8470 e.on('mouseout', _this.onMouseout, _this);
8472 this.fireEvent('rowsrendered', this);
8478 onUpdate : function(ds,record)
8480 this.refreshRow(record);
8484 onRemove : function(ds, record, index, isUpdate){
8485 if(isUpdate !== true){
8486 this.fireEvent("beforerowremoved", this, index, record);
8488 var bt = this.mainBody.dom;
8490 var rows = this.el.select('tbody > tr', true).elements;
8492 if(typeof(rows[index]) != 'undefined'){
8493 bt.removeChild(rows[index].dom);
8496 // if(bt.rows[index]){
8497 // bt.removeChild(bt.rows[index]);
8500 if(isUpdate !== true){
8501 //this.stripeRows(index);
8502 //this.syncRowHeights(index, index);
8504 this.fireEvent("rowremoved", this, index, record);
8508 onAdd : function(ds, records, rowIndex)
8510 //Roo.log('on Add called');
8511 // - note this does not handle multiple adding very well..
8512 var bt = this.mainBody.dom;
8513 for (var i =0 ; i < records.length;i++) {
8514 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8515 //Roo.log(records[i]);
8516 //Roo.log(this.store.getAt(rowIndex+i));
8517 this.insertRow(this.store, rowIndex + i, false);
8524 refreshRow : function(record){
8525 var ds = this.store, index;
8526 if(typeof record == 'number'){
8528 record = ds.getAt(index);
8530 index = ds.indexOf(record);
8532 return; // should not happen - but seems to
8535 this.insertRow(ds, index, true);
8537 this.onRemove(ds, record, index+1, true);
8539 //this.syncRowHeights(index, index);
8541 this.fireEvent("rowupdated", this, index, record);
8544 insertRow : function(dm, rowIndex, isUpdate){
8547 this.fireEvent("beforerowsinserted", this, rowIndex);
8549 //var s = this.getScrollState();
8550 var row = this.renderRow(this.cm, this.store, rowIndex);
8551 // insert before rowIndex..
8552 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8556 if(row.cellObjects.length){
8557 Roo.each(row.cellObjects, function(r){
8558 _this.renderCellObject(r);
8563 this.fireEvent("rowsinserted", this, rowIndex);
8564 //this.syncRowHeights(firstRow, lastRow);
8565 //this.stripeRows(firstRow);
8572 getRowDom : function(rowIndex)
8574 var rows = this.el.select('tbody > tr', true).elements;
8576 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8579 // returns the object tree for a tr..
8582 renderRow : function(cm, ds, rowIndex)
8584 var d = ds.getAt(rowIndex);
8588 cls : 'x-row-' + rowIndex,
8592 var cellObjects = [];
8594 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8595 var config = cm.config[i];
8597 var renderer = cm.getRenderer(i);
8601 if(typeof(renderer) !== 'undefined'){
8602 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8604 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8605 // and are rendered into the cells after the row is rendered - using the id for the element.
8607 if(typeof(value) === 'object'){
8617 rowIndex : rowIndex,
8622 this.fireEvent('rowclass', this, rowcfg);
8626 cls : rowcfg.rowClass + ' x-col-' + i,
8628 html: (typeof(value) === 'object') ? '' : value
8635 if(typeof(config.colspan) != 'undefined'){
8636 td.colspan = config.colspan;
8639 if(typeof(config.hidden) != 'undefined' && config.hidden){
8640 td.style += ' display:none;';
8643 if(typeof(config.align) != 'undefined' && config.align.length){
8644 td.style += ' text-align:' + config.align + ';';
8646 if(typeof(config.valign) != 'undefined' && config.valign.length){
8647 td.style += ' vertical-align:' + config.valign + ';';
8650 if(typeof(config.width) != 'undefined'){
8651 td.style += ' width:' + config.width + 'px;';
8654 if(typeof(config.cursor) != 'undefined'){
8655 td.style += ' cursor:' + config.cursor + ';';
8658 if(typeof(config.cls) != 'undefined'){
8659 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8662 ['xs','sm','md','lg'].map(function(size){
8664 if(typeof(config[size]) == 'undefined'){
8670 if (!config[size]) { // 0 = hidden
8671 // BS 4 '0' is treated as hide that column and below.
8672 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8676 td.cls += ' col-' + size + '-' + config[size] + (
8677 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8687 row.cellObjects = cellObjects;
8695 onBeforeLoad : function()
8704 this.el.select('tbody', true).first().dom.innerHTML = '';
8707 * Show or hide a row.
8708 * @param {Number} rowIndex to show or hide
8709 * @param {Boolean} state hide
8711 setRowVisibility : function(rowIndex, state)
8713 var bt = this.mainBody.dom;
8715 var rows = this.el.select('tbody > tr', true).elements;
8717 if(typeof(rows[rowIndex]) == 'undefined'){
8720 rows[rowIndex].dom.style.display = state ? '' : 'none';
8724 getSelectionModel : function(){
8726 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8728 return this.selModel;
8731 * Render the Roo.bootstrap object from renderder
8733 renderCellObject : function(r)
8737 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8739 var t = r.cfg.render(r.container);
8742 Roo.each(r.cfg.cn, function(c){
8744 container: t.getChildContainer(),
8747 _this.renderCellObject(child);
8752 getRowIndex : function(row)
8756 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8767 * Returns the grid's underlying element = used by panel.Grid
8768 * @return {Element} The element
8770 getGridEl : function(){
8774 * Forces a resize - used by panel.Grid
8775 * @return {Element} The element
8777 autoSize : function()
8779 //var ctr = Roo.get(this.container.dom.parentElement);
8780 var ctr = Roo.get(this.el.dom);
8782 var thd = this.getGridEl().select('thead',true).first();
8783 var tbd = this.getGridEl().select('tbody', true).first();
8784 var tfd = this.getGridEl().select('tfoot', true).first();
8786 var cw = ctr.getWidth();
8787 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8791 tbd.setWidth(ctr.getWidth());
8792 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8793 // this needs fixing for various usage - currently only hydra job advers I think..
8795 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8797 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8800 cw = Math.max(cw, this.totalWidth);
8801 this.getGridEl().select('tbody tr',true).setWidth(cw);
8803 // resize 'expandable coloumn?
8805 return; // we doe not have a view in this design..
8808 onBodyScroll: function()
8810 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8812 this.mainHead.setStyle({
8813 'position' : 'relative',
8814 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8820 var scrollHeight = this.mainBody.dom.scrollHeight;
8822 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8824 var height = this.mainBody.getHeight();
8826 if(scrollHeight - height == scrollTop) {
8828 var total = this.ds.getTotalCount();
8830 if(this.footer.cursor + this.footer.pageSize < total){
8832 this.footer.ds.load({
8834 start : this.footer.cursor + this.footer.pageSize,
8835 limit : this.footer.pageSize
8845 onHeaderChange : function()
8847 var header = this.renderHeader();
8848 var table = this.el.select('table', true).first();
8850 this.mainHead.remove();
8851 this.mainHead = table.createChild(header, this.mainBody, false);
8854 onHiddenChange : function(colModel, colIndex, hidden)
8856 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8857 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8859 this.CSS.updateRule(thSelector, "display", "");
8860 this.CSS.updateRule(tdSelector, "display", "");
8863 this.CSS.updateRule(thSelector, "display", "none");
8864 this.CSS.updateRule(tdSelector, "display", "none");
8867 this.onHeaderChange();
8871 setColumnWidth: function(col_index, width)
8873 // width = "md-2 xs-2..."
8874 if(!this.colModel.config[col_index]) {
8878 var w = width.split(" ");
8880 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8882 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8885 for(var j = 0; j < w.length; j++) {
8891 var size_cls = w[j].split("-");
8893 if(!Number.isInteger(size_cls[1] * 1)) {
8897 if(!this.colModel.config[col_index][size_cls[0]]) {
8901 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8905 h_row[0].classList.replace(
8906 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8907 "col-"+size_cls[0]+"-"+size_cls[1]
8910 for(var i = 0; i < rows.length; i++) {
8912 var size_cls = w[j].split("-");
8914 if(!Number.isInteger(size_cls[1] * 1)) {
8918 if(!this.colModel.config[col_index][size_cls[0]]) {
8922 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8926 rows[i].classList.replace(
8927 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8928 "col-"+size_cls[0]+"-"+size_cls[1]
8932 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8947 * @class Roo.bootstrap.TableCell
8948 * @extends Roo.bootstrap.Component
8949 * Bootstrap TableCell class
8950 * @cfg {String} html cell contain text
8951 * @cfg {String} cls cell class
8952 * @cfg {String} tag cell tag (td|th) default td
8953 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8954 * @cfg {String} align Aligns the content in a cell
8955 * @cfg {String} axis Categorizes cells
8956 * @cfg {String} bgcolor Specifies the background color of a cell
8957 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8958 * @cfg {Number} colspan Specifies the number of columns a cell should span
8959 * @cfg {String} headers Specifies one or more header cells a cell is related to
8960 * @cfg {Number} height Sets the height of a cell
8961 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8962 * @cfg {Number} rowspan Sets the number of rows a cell should span
8963 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8964 * @cfg {String} valign Vertical aligns the content in a cell
8965 * @cfg {Number} width Specifies the width of a cell
8968 * Create a new TableCell
8969 * @param {Object} config The config object
8972 Roo.bootstrap.TableCell = function(config){
8973 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8976 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
8996 getAutoCreate : function(){
8997 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9017 cfg.align=this.align
9023 cfg.bgcolor=this.bgcolor
9026 cfg.charoff=this.charoff
9029 cfg.colspan=this.colspan
9032 cfg.headers=this.headers
9035 cfg.height=this.height
9038 cfg.nowrap=this.nowrap
9041 cfg.rowspan=this.rowspan
9044 cfg.scope=this.scope
9047 cfg.valign=this.valign
9050 cfg.width=this.width
9069 * @class Roo.bootstrap.TableRow
9070 * @extends Roo.bootstrap.Component
9071 * Bootstrap TableRow class
9072 * @cfg {String} cls row class
9073 * @cfg {String} align Aligns the content in a table row
9074 * @cfg {String} bgcolor Specifies a background color for a table row
9075 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9076 * @cfg {String} valign Vertical aligns the content in a table row
9079 * Create a new TableRow
9080 * @param {Object} config The config object
9083 Roo.bootstrap.TableRow = function(config){
9084 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9087 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9095 getAutoCreate : function(){
9096 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9106 cfg.align = this.align;
9109 cfg.bgcolor = this.bgcolor;
9112 cfg.charoff = this.charoff;
9115 cfg.valign = this.valign;
9133 * @class Roo.bootstrap.TableBody
9134 * @extends Roo.bootstrap.Component
9135 * Bootstrap TableBody class
9136 * @cfg {String} cls element class
9137 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9138 * @cfg {String} align Aligns the content inside the element
9139 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9140 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9143 * Create a new TableBody
9144 * @param {Object} config The config object
9147 Roo.bootstrap.TableBody = function(config){
9148 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9151 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9159 getAutoCreate : function(){
9160 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9174 cfg.align = this.align;
9177 cfg.charoff = this.charoff;
9180 cfg.valign = this.valign;
9187 // initEvents : function()
9194 // this.store = Roo.factory(this.store, Roo.data);
9195 // this.store.on('load', this.onLoad, this);
9197 // this.store.load();
9201 // onLoad: function ()
9203 // this.fireEvent('load', this);
9213 * Ext JS Library 1.1.1
9214 * Copyright(c) 2006-2007, Ext JS, LLC.
9216 * Originally Released Under LGPL - original licence link has changed is not relivant.
9219 * <script type="text/javascript">
9222 // as we use this in bootstrap.
9223 Roo.namespace('Roo.form');
9225 * @class Roo.form.Action
9226 * Internal Class used to handle form actions
9228 * @param {Roo.form.BasicForm} el The form element or its id
9229 * @param {Object} config Configuration options
9234 // define the action interface
9235 Roo.form.Action = function(form, options){
9237 this.options = options || {};
9240 * Client Validation Failed
9243 Roo.form.Action.CLIENT_INVALID = 'client';
9245 * Server Validation Failed
9248 Roo.form.Action.SERVER_INVALID = 'server';
9250 * Connect to Server Failed
9253 Roo.form.Action.CONNECT_FAILURE = 'connect';
9255 * Reading Data from Server Failed
9258 Roo.form.Action.LOAD_FAILURE = 'load';
9260 Roo.form.Action.prototype = {
9262 failureType : undefined,
9263 response : undefined,
9267 run : function(options){
9272 success : function(response){
9277 handleResponse : function(response){
9281 // default connection failure
9282 failure : function(response){
9284 this.response = response;
9285 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9286 this.form.afterAction(this, false);
9289 processResponse : function(response){
9290 this.response = response;
9291 if(!response.responseText){
9294 this.result = this.handleResponse(response);
9298 // utility functions used internally
9299 getUrl : function(appendParams){
9300 var url = this.options.url || this.form.url || this.form.el.dom.action;
9302 var p = this.getParams();
9304 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9310 getMethod : function(){
9311 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9314 getParams : function(){
9315 var bp = this.form.baseParams;
9316 var p = this.options.params;
9318 if(typeof p == "object"){
9319 p = Roo.urlEncode(Roo.applyIf(p, bp));
9320 }else if(typeof p == 'string' && bp){
9321 p += '&' + Roo.urlEncode(bp);
9324 p = Roo.urlEncode(bp);
9329 createCallback : function(){
9331 success: this.success,
9332 failure: this.failure,
9334 timeout: (this.form.timeout*1000),
9335 upload: this.form.fileUpload ? this.success : undefined
9340 Roo.form.Action.Submit = function(form, options){
9341 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9344 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9347 haveProgress : false,
9348 uploadComplete : false,
9350 // uploadProgress indicator.
9351 uploadProgress : function()
9353 if (!this.form.progressUrl) {
9357 if (!this.haveProgress) {
9358 Roo.MessageBox.progress("Uploading", "Uploading");
9360 if (this.uploadComplete) {
9361 Roo.MessageBox.hide();
9365 this.haveProgress = true;
9367 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9369 var c = new Roo.data.Connection();
9371 url : this.form.progressUrl,
9376 success : function(req){
9377 //console.log(data);
9381 rdata = Roo.decode(req.responseText)
9383 Roo.log("Invalid data from server..");
9387 if (!rdata || !rdata.success) {
9389 Roo.MessageBox.alert(Roo.encode(rdata));
9392 var data = rdata.data;
9394 if (this.uploadComplete) {
9395 Roo.MessageBox.hide();
9400 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9401 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9404 this.uploadProgress.defer(2000,this);
9407 failure: function(data) {
9408 Roo.log('progress url failed ');
9419 // run get Values on the form, so it syncs any secondary forms.
9420 this.form.getValues();
9422 var o = this.options;
9423 var method = this.getMethod();
9424 var isPost = method == 'POST';
9425 if(o.clientValidation === false || this.form.isValid()){
9427 if (this.form.progressUrl) {
9428 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9429 (new Date() * 1) + '' + Math.random());
9434 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9435 form:this.form.el.dom,
9436 url:this.getUrl(!isPost),
9438 params:isPost ? this.getParams() : null,
9439 isUpload: this.form.fileUpload,
9440 formData : this.form.formData
9443 this.uploadProgress();
9445 }else if (o.clientValidation !== false){ // client validation failed
9446 this.failureType = Roo.form.Action.CLIENT_INVALID;
9447 this.form.afterAction(this, false);
9451 success : function(response)
9453 this.uploadComplete= true;
9454 if (this.haveProgress) {
9455 Roo.MessageBox.hide();
9459 var result = this.processResponse(response);
9460 if(result === true || result.success){
9461 this.form.afterAction(this, true);
9465 this.form.markInvalid(result.errors);
9466 this.failureType = Roo.form.Action.SERVER_INVALID;
9468 this.form.afterAction(this, false);
9470 failure : function(response)
9472 this.uploadComplete= true;
9473 if (this.haveProgress) {
9474 Roo.MessageBox.hide();
9477 this.response = response;
9478 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9479 this.form.afterAction(this, false);
9482 handleResponse : function(response){
9483 if(this.form.errorReader){
9484 var rs = this.form.errorReader.read(response);
9487 for(var i = 0, len = rs.records.length; i < len; i++) {
9488 var r = rs.records[i];
9492 if(errors.length < 1){
9496 success : rs.success,
9502 ret = Roo.decode(response.responseText);
9506 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9516 Roo.form.Action.Load = function(form, options){
9517 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9518 this.reader = this.form.reader;
9521 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9526 Roo.Ajax.request(Roo.apply(
9527 this.createCallback(), {
9528 method:this.getMethod(),
9529 url:this.getUrl(false),
9530 params:this.getParams()
9534 success : function(response){
9536 var result = this.processResponse(response);
9537 if(result === true || !result.success || !result.data){
9538 this.failureType = Roo.form.Action.LOAD_FAILURE;
9539 this.form.afterAction(this, false);
9542 this.form.clearInvalid();
9543 this.form.setValues(result.data);
9544 this.form.afterAction(this, true);
9547 handleResponse : function(response){
9548 if(this.form.reader){
9549 var rs = this.form.reader.read(response);
9550 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9552 success : rs.success,
9556 return Roo.decode(response.responseText);
9560 Roo.form.Action.ACTION_TYPES = {
9561 'load' : Roo.form.Action.Load,
9562 'submit' : Roo.form.Action.Submit
9571 * @class Roo.bootstrap.Form
9572 * @extends Roo.bootstrap.Component
9573 * Bootstrap Form class
9574 * @cfg {String} method GET | POST (default POST)
9575 * @cfg {String} labelAlign top | left (default top)
9576 * @cfg {String} align left | right - for navbars
9577 * @cfg {Boolean} loadMask load mask when submit (default true)
9582 * @param {Object} config The config object
9586 Roo.bootstrap.Form = function(config){
9588 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9590 Roo.bootstrap.Form.popover.apply();
9594 * @event clientvalidation
9595 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9596 * @param {Form} this
9597 * @param {Boolean} valid true if the form has passed client-side validation
9599 clientvalidation: true,
9601 * @event beforeaction
9602 * Fires before any action is performed. Return false to cancel the action.
9603 * @param {Form} this
9604 * @param {Action} action The action to be performed
9608 * @event actionfailed
9609 * Fires when an action fails.
9610 * @param {Form} this
9611 * @param {Action} action The action that failed
9613 actionfailed : true,
9615 * @event actioncomplete
9616 * Fires when an action is completed.
9617 * @param {Form} this
9618 * @param {Action} action The action that completed
9620 actioncomplete : true
9624 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9627 * @cfg {String} method
9628 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9633 * The URL to use for form actions if one isn't supplied in the action options.
9636 * @cfg {Boolean} fileUpload
9637 * Set to true if this form is a file upload.
9641 * @cfg {Object} baseParams
9642 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9646 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9650 * @cfg {Sting} align (left|right) for navbar forms
9655 activeAction : null,
9658 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9659 * element by passing it or its id or mask the form itself by passing in true.
9662 waitMsgTarget : false,
9667 * @cfg {Boolean} errorMask (true|false) default false
9672 * @cfg {Number} maskOffset Default 100
9677 * @cfg {Boolean} maskBody
9681 getAutoCreate : function(){
9685 method : this.method || 'POST',
9686 id : this.id || Roo.id(),
9689 if (this.parent().xtype.match(/^Nav/)) {
9690 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9694 if (this.labelAlign == 'left' ) {
9695 cfg.cls += ' form-horizontal';
9701 initEvents : function()
9703 this.el.on('submit', this.onSubmit, this);
9704 // this was added as random key presses on the form where triggering form submit.
9705 this.el.on('keypress', function(e) {
9706 if (e.getCharCode() != 13) {
9709 // we might need to allow it for textareas.. and some other items.
9710 // check e.getTarget().
9712 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9716 Roo.log("keypress blocked");
9724 onSubmit : function(e){
9729 * Returns true if client-side validation on the form is successful.
9732 isValid : function(){
9733 var items = this.getItems();
9737 items.each(function(f){
9743 Roo.log('invalid field: ' + f.name);
9747 if(!target && f.el.isVisible(true)){
9753 if(this.errorMask && !valid){
9754 Roo.bootstrap.Form.popover.mask(this, target);
9761 * Returns true if any fields in this form have changed since their original load.
9764 isDirty : function(){
9766 var items = this.getItems();
9767 items.each(function(f){
9777 * Performs a predefined action (submit or load) or custom actions you define on this form.
9778 * @param {String} actionName The name of the action type
9779 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9780 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9781 * accept other config options):
9783 Property Type Description
9784 ---------------- --------------- ----------------------------------------------------------------------------------
9785 url String The url for the action (defaults to the form's url)
9786 method String The form method to use (defaults to the form's method, or POST if not defined)
9787 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9788 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9789 validate the form on the client (defaults to false)
9791 * @return {BasicForm} this
9793 doAction : function(action, options){
9794 if(typeof action == 'string'){
9795 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9797 if(this.fireEvent('beforeaction', this, action) !== false){
9798 this.beforeAction(action);
9799 action.run.defer(100, action);
9805 beforeAction : function(action){
9806 var o = action.options;
9811 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9813 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9816 // not really supported yet.. ??
9818 //if(this.waitMsgTarget === true){
9819 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9820 //}else if(this.waitMsgTarget){
9821 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9822 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9824 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9830 afterAction : function(action, success){
9831 this.activeAction = null;
9832 var o = action.options;
9837 Roo.get(document.body).unmask();
9843 //if(this.waitMsgTarget === true){
9844 // this.el.unmask();
9845 //}else if(this.waitMsgTarget){
9846 // this.waitMsgTarget.unmask();
9848 // Roo.MessageBox.updateProgress(1);
9849 // Roo.MessageBox.hide();
9856 Roo.callback(o.success, o.scope, [this, action]);
9857 this.fireEvent('actioncomplete', this, action);
9861 // failure condition..
9862 // we have a scenario where updates need confirming.
9863 // eg. if a locking scenario exists..
9864 // we look for { errors : { needs_confirm : true }} in the response.
9866 (typeof(action.result) != 'undefined') &&
9867 (typeof(action.result.errors) != 'undefined') &&
9868 (typeof(action.result.errors.needs_confirm) != 'undefined')
9871 Roo.log("not supported yet");
9874 Roo.MessageBox.confirm(
9875 "Change requires confirmation",
9876 action.result.errorMsg,
9881 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9891 Roo.callback(o.failure, o.scope, [this, action]);
9892 // show an error message if no failed handler is set..
9893 if (!this.hasListener('actionfailed')) {
9894 Roo.log("need to add dialog support");
9896 Roo.MessageBox.alert("Error",
9897 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9898 action.result.errorMsg :
9899 "Saving Failed, please check your entries or try again"
9904 this.fireEvent('actionfailed', this, action);
9909 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9910 * @param {String} id The value to search for
9913 findField : function(id){
9914 var items = this.getItems();
9915 var field = items.get(id);
9917 items.each(function(f){
9918 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9925 return field || null;
9928 * Mark fields in this form invalid in bulk.
9929 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9930 * @return {BasicForm} this
9932 markInvalid : function(errors){
9933 if(errors instanceof Array){
9934 for(var i = 0, len = errors.length; i < len; i++){
9935 var fieldError = errors[i];
9936 var f = this.findField(fieldError.id);
9938 f.markInvalid(fieldError.msg);
9944 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9945 field.markInvalid(errors[id]);
9949 //Roo.each(this.childForms || [], function (f) {
9950 // f.markInvalid(errors);
9957 * Set values for fields in this form in bulk.
9958 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9959 * @return {BasicForm} this
9961 setValues : function(values){
9962 if(values instanceof Array){ // array of objects
9963 for(var i = 0, len = values.length; i < len; i++){
9965 var f = this.findField(v.id);
9967 f.setValue(v.value);
9968 if(this.trackResetOnLoad){
9969 f.originalValue = f.getValue();
9973 }else{ // object hash
9976 if(typeof values[id] != 'function' && (field = this.findField(id))){
9978 if (field.setFromData &&
9980 field.displayField &&
9981 // combos' with local stores can
9982 // be queried via setValue()
9983 // to set their value..
9984 (field.store && !field.store.isLocal)
9988 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9989 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9990 field.setFromData(sd);
9992 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9994 field.setFromData(values);
9997 field.setValue(values[id]);
10001 if(this.trackResetOnLoad){
10002 field.originalValue = field.getValue();
10008 //Roo.each(this.childForms || [], function (f) {
10009 // f.setValues(values);
10016 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10017 * they are returned as an array.
10018 * @param {Boolean} asString
10021 getValues : function(asString){
10022 //if (this.childForms) {
10023 // copy values from the child forms
10024 // Roo.each(this.childForms, function (f) {
10025 // this.setValues(f.getValues());
10031 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10032 if(asString === true){
10035 return Roo.urlDecode(fs);
10039 * Returns the fields in this form as an object with key/value pairs.
10040 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10043 getFieldValues : function(with_hidden)
10045 var items = this.getItems();
10047 items.each(function(f){
10049 if (!f.getName()) {
10053 var v = f.getValue();
10055 if (f.inputType =='radio') {
10056 if (typeof(ret[f.getName()]) == 'undefined') {
10057 ret[f.getName()] = ''; // empty..
10060 if (!f.el.dom.checked) {
10064 v = f.el.dom.value;
10068 if(f.xtype == 'MoneyField'){
10069 ret[f.currencyName] = f.getCurrency();
10072 // not sure if this supported any more..
10073 if ((typeof(v) == 'object') && f.getRawValue) {
10074 v = f.getRawValue() ; // dates..
10076 // combo boxes where name != hiddenName...
10077 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10078 ret[f.name] = f.getRawValue();
10080 ret[f.getName()] = v;
10087 * Clears all invalid messages in this form.
10088 * @return {BasicForm} this
10090 clearInvalid : function(){
10091 var items = this.getItems();
10093 items.each(function(f){
10101 * Resets this form.
10102 * @return {BasicForm} this
10104 reset : function(){
10105 var items = this.getItems();
10106 items.each(function(f){
10110 Roo.each(this.childForms || [], function (f) {
10118 getItems : function()
10120 var r=new Roo.util.MixedCollection(false, function(o){
10121 return o.id || (o.id = Roo.id());
10123 var iter = function(el) {
10130 Roo.each(el.items,function(e) {
10139 hideFields : function(items)
10141 Roo.each(items, function(i){
10143 var f = this.findField(i);
10154 showFields : function(items)
10156 Roo.each(items, function(i){
10158 var f = this.findField(i);
10171 Roo.apply(Roo.bootstrap.Form, {
10187 intervalID : false,
10193 if(this.isApplied){
10198 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10199 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10200 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10201 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10204 this.maskEl.top.enableDisplayMode("block");
10205 this.maskEl.left.enableDisplayMode("block");
10206 this.maskEl.bottom.enableDisplayMode("block");
10207 this.maskEl.right.enableDisplayMode("block");
10209 this.toolTip = new Roo.bootstrap.Tooltip({
10210 cls : 'roo-form-error-popover',
10212 'left' : ['r-l', [-2,0], 'right'],
10213 'right' : ['l-r', [2,0], 'left'],
10214 'bottom' : ['tl-bl', [0,2], 'top'],
10215 'top' : [ 'bl-tl', [0,-2], 'bottom']
10219 this.toolTip.render(Roo.get(document.body));
10221 this.toolTip.el.enableDisplayMode("block");
10223 Roo.get(document.body).on('click', function(){
10227 Roo.get(document.body).on('touchstart', function(){
10231 this.isApplied = true
10234 mask : function(form, target)
10238 this.target = target;
10240 if(!this.form.errorMask || !target.el){
10244 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10246 Roo.log(scrollable);
10248 var ot = this.target.el.calcOffsetsTo(scrollable);
10250 var scrollTo = ot[1] - this.form.maskOffset;
10252 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10254 scrollable.scrollTo('top', scrollTo);
10256 var box = this.target.el.getBox();
10258 var zIndex = Roo.bootstrap.Modal.zIndex++;
10261 this.maskEl.top.setStyle('position', 'absolute');
10262 this.maskEl.top.setStyle('z-index', zIndex);
10263 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10264 this.maskEl.top.setLeft(0);
10265 this.maskEl.top.setTop(0);
10266 this.maskEl.top.show();
10268 this.maskEl.left.setStyle('position', 'absolute');
10269 this.maskEl.left.setStyle('z-index', zIndex);
10270 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10271 this.maskEl.left.setLeft(0);
10272 this.maskEl.left.setTop(box.y - this.padding);
10273 this.maskEl.left.show();
10275 this.maskEl.bottom.setStyle('position', 'absolute');
10276 this.maskEl.bottom.setStyle('z-index', zIndex);
10277 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10278 this.maskEl.bottom.setLeft(0);
10279 this.maskEl.bottom.setTop(box.bottom + this.padding);
10280 this.maskEl.bottom.show();
10282 this.maskEl.right.setStyle('position', 'absolute');
10283 this.maskEl.right.setStyle('z-index', zIndex);
10284 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10285 this.maskEl.right.setLeft(box.right + this.padding);
10286 this.maskEl.right.setTop(box.y - this.padding);
10287 this.maskEl.right.show();
10289 this.toolTip.bindEl = this.target.el;
10291 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10293 var tip = this.target.blankText;
10295 if(this.target.getValue() !== '' ) {
10297 if (this.target.invalidText.length) {
10298 tip = this.target.invalidText;
10299 } else if (this.target.regexText.length){
10300 tip = this.target.regexText;
10304 this.toolTip.show(tip);
10306 this.intervalID = window.setInterval(function() {
10307 Roo.bootstrap.Form.popover.unmask();
10310 window.onwheel = function(){ return false;};
10312 (function(){ this.isMasked = true; }).defer(500, this);
10316 unmask : function()
10318 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10322 this.maskEl.top.setStyle('position', 'absolute');
10323 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10324 this.maskEl.top.hide();
10326 this.maskEl.left.setStyle('position', 'absolute');
10327 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10328 this.maskEl.left.hide();
10330 this.maskEl.bottom.setStyle('position', 'absolute');
10331 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10332 this.maskEl.bottom.hide();
10334 this.maskEl.right.setStyle('position', 'absolute');
10335 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10336 this.maskEl.right.hide();
10338 this.toolTip.hide();
10340 this.toolTip.el.hide();
10342 window.onwheel = function(){ return true;};
10344 if(this.intervalID){
10345 window.clearInterval(this.intervalID);
10346 this.intervalID = false;
10349 this.isMasked = false;
10359 * Ext JS Library 1.1.1
10360 * Copyright(c) 2006-2007, Ext JS, LLC.
10362 * Originally Released Under LGPL - original licence link has changed is not relivant.
10365 * <script type="text/javascript">
10368 * @class Roo.form.VTypes
10369 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10372 Roo.form.VTypes = function(){
10373 // closure these in so they are only created once.
10374 var alpha = /^[a-zA-Z_]+$/;
10375 var alphanum = /^[a-zA-Z0-9_]+$/;
10376 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10377 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10379 // All these messages and functions are configurable
10382 * The function used to validate email addresses
10383 * @param {String} value The email address
10385 'email' : function(v){
10386 return email.test(v);
10389 * The error text to display when the email validation function returns false
10392 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10394 * The keystroke filter mask to be applied on email input
10397 'emailMask' : /[a-z0-9_\.\-@]/i,
10400 * The function used to validate URLs
10401 * @param {String} value The URL
10403 'url' : function(v){
10404 return url.test(v);
10407 * The error text to display when the url validation function returns false
10410 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10413 * The function used to validate alpha values
10414 * @param {String} value The value
10416 'alpha' : function(v){
10417 return alpha.test(v);
10420 * The error text to display when the alpha validation function returns false
10423 'alphaText' : 'This field should only contain letters and _',
10425 * The keystroke filter mask to be applied on alpha input
10428 'alphaMask' : /[a-z_]/i,
10431 * The function used to validate alphanumeric values
10432 * @param {String} value The value
10434 'alphanum' : function(v){
10435 return alphanum.test(v);
10438 * The error text to display when the alphanumeric validation function returns false
10441 'alphanumText' : 'This field should only contain letters, numbers and _',
10443 * The keystroke filter mask to be applied on alphanumeric input
10446 'alphanumMask' : /[a-z0-9_]/i
10456 * @class Roo.bootstrap.Input
10457 * @extends Roo.bootstrap.Component
10458 * Bootstrap Input class
10459 * @cfg {Boolean} disabled is it disabled
10460 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10461 * @cfg {String} name name of the input
10462 * @cfg {string} fieldLabel - the label associated
10463 * @cfg {string} placeholder - placeholder to put in text.
10464 * @cfg {string} before - input group add on before
10465 * @cfg {string} after - input group add on after
10466 * @cfg {string} size - (lg|sm) or leave empty..
10467 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10468 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10469 * @cfg {Number} md colspan out of 12 for computer-sized screens
10470 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10471 * @cfg {string} value default value of the input
10472 * @cfg {Number} labelWidth set the width of label
10473 * @cfg {Number} labellg set the width of label (1-12)
10474 * @cfg {Number} labelmd set the width of label (1-12)
10475 * @cfg {Number} labelsm set the width of label (1-12)
10476 * @cfg {Number} labelxs set the width of label (1-12)
10477 * @cfg {String} labelAlign (top|left)
10478 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10479 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10480 * @cfg {String} indicatorpos (left|right) default left
10481 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10482 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10483 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10485 * @cfg {String} align (left|center|right) Default left
10486 * @cfg {Boolean} forceFeedback (true|false) Default false
10489 * Create a new Input
10490 * @param {Object} config The config object
10493 Roo.bootstrap.Input = function(config){
10495 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10500 * Fires when this field receives input focus.
10501 * @param {Roo.form.Field} this
10506 * Fires when this field loses input focus.
10507 * @param {Roo.form.Field} this
10511 * @event specialkey
10512 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10513 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10514 * @param {Roo.form.Field} this
10515 * @param {Roo.EventObject} e The event object
10520 * Fires just before the field blurs if the field value has changed.
10521 * @param {Roo.form.Field} this
10522 * @param {Mixed} newValue The new value
10523 * @param {Mixed} oldValue The original value
10528 * Fires after the field has been marked as invalid.
10529 * @param {Roo.form.Field} this
10530 * @param {String} msg The validation message
10535 * Fires after the field has been validated with no errors.
10536 * @param {Roo.form.Field} this
10541 * Fires after the key up
10542 * @param {Roo.form.Field} this
10543 * @param {Roo.EventObject} e The event Object
10548 * Fires after the user pastes into input
10549 * @param {Roo.form.Field} this
10550 * @param {Roo.EventObject} e The event Object
10556 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10558 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10559 automatic validation (defaults to "keyup").
10561 validationEvent : "keyup",
10563 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10565 validateOnBlur : true,
10567 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10569 validationDelay : 250,
10571 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10573 focusClass : "x-form-focus", // not needed???
10577 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10579 invalidClass : "has-warning",
10582 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10584 validClass : "has-success",
10587 * @cfg {Boolean} hasFeedback (true|false) default true
10589 hasFeedback : true,
10592 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10594 invalidFeedbackClass : "glyphicon-warning-sign",
10597 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10599 validFeedbackClass : "glyphicon-ok",
10602 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10604 selectOnFocus : false,
10607 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10611 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10616 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10618 disableKeyFilter : false,
10621 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10625 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10629 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10631 blankText : "Please complete this mandatory field",
10634 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10638 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10640 maxLength : Number.MAX_VALUE,
10642 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10644 minLengthText : "The minimum length for this field is {0}",
10646 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10648 maxLengthText : "The maximum length for this field is {0}",
10652 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10653 * If available, this function will be called only after the basic validators all return true, and will be passed the
10654 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10658 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10659 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10660 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10664 * @cfg {String} regexText -- Depricated - use Invalid Text
10669 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10675 autocomplete: false,
10679 inputType : 'text',
10682 placeholder: false,
10687 preventMark: false,
10688 isFormField : true,
10691 labelAlign : false,
10694 formatedValue : false,
10695 forceFeedback : false,
10697 indicatorpos : 'left',
10707 parentLabelAlign : function()
10710 while (parent.parent()) {
10711 parent = parent.parent();
10712 if (typeof(parent.labelAlign) !='undefined') {
10713 return parent.labelAlign;
10720 getAutoCreate : function()
10722 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10728 if(this.inputType != 'hidden'){
10729 cfg.cls = 'form-group' //input-group
10735 type : this.inputType,
10736 value : this.value,
10737 cls : 'form-control',
10738 placeholder : this.placeholder || '',
10739 autocomplete : this.autocomplete || 'new-password'
10741 if (this.inputType == 'file') {
10742 input.style = 'overflow:hidden'; // why not in CSS?
10745 if(this.capture.length){
10746 input.capture = this.capture;
10749 if(this.accept.length){
10750 input.accept = this.accept + "/*";
10754 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10757 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10758 input.maxLength = this.maxLength;
10761 if (this.disabled) {
10762 input.disabled=true;
10765 if (this.readOnly) {
10766 input.readonly=true;
10770 input.name = this.name;
10774 input.cls += ' input-' + this.size;
10778 ['xs','sm','md','lg'].map(function(size){
10779 if (settings[size]) {
10780 cfg.cls += ' col-' + size + '-' + settings[size];
10784 var inputblock = input;
10788 cls: 'glyphicon form-control-feedback'
10791 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10794 cls : 'has-feedback',
10802 if (this.before || this.after) {
10805 cls : 'input-group',
10809 if (this.before && typeof(this.before) == 'string') {
10811 inputblock.cn.push({
10813 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10817 if (this.before && typeof(this.before) == 'object') {
10818 this.before = Roo.factory(this.before);
10820 inputblock.cn.push({
10822 cls : 'roo-input-before input-group-prepend input-group-' +
10823 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10827 inputblock.cn.push(input);
10829 if (this.after && typeof(this.after) == 'string') {
10830 inputblock.cn.push({
10832 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10836 if (this.after && typeof(this.after) == 'object') {
10837 this.after = Roo.factory(this.after);
10839 inputblock.cn.push({
10841 cls : 'roo-input-after input-group-append input-group-' +
10842 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10846 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10847 inputblock.cls += ' has-feedback';
10848 inputblock.cn.push(feedback);
10853 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10854 tooltip : 'This field is required'
10856 if (this.allowBlank ) {
10857 indicator.style = this.allowBlank ? ' display:none' : '';
10859 if (align ==='left' && this.fieldLabel.length) {
10861 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10868 cls : 'control-label col-form-label',
10869 html : this.fieldLabel
10880 var labelCfg = cfg.cn[1];
10881 var contentCfg = cfg.cn[2];
10883 if(this.indicatorpos == 'right'){
10888 cls : 'control-label col-form-label',
10892 html : this.fieldLabel
10906 labelCfg = cfg.cn[0];
10907 contentCfg = cfg.cn[1];
10911 if(this.labelWidth > 12){
10912 labelCfg.style = "width: " + this.labelWidth + 'px';
10915 if(this.labelWidth < 13 && this.labelmd == 0){
10916 this.labelmd = this.labelWidth;
10919 if(this.labellg > 0){
10920 labelCfg.cls += ' col-lg-' + this.labellg;
10921 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10924 if(this.labelmd > 0){
10925 labelCfg.cls += ' col-md-' + this.labelmd;
10926 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10929 if(this.labelsm > 0){
10930 labelCfg.cls += ' col-sm-' + this.labelsm;
10931 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10934 if(this.labelxs > 0){
10935 labelCfg.cls += ' col-xs-' + this.labelxs;
10936 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10940 } else if ( this.fieldLabel.length) {
10947 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10948 tooltip : 'This field is required',
10949 style : this.allowBlank ? ' display:none' : ''
10953 //cls : 'input-group-addon',
10954 html : this.fieldLabel
10962 if(this.indicatorpos == 'right'){
10967 //cls : 'input-group-addon',
10968 html : this.fieldLabel
10973 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10974 tooltip : 'This field is required',
10975 style : this.allowBlank ? ' display:none' : ''
10995 if (this.parentType === 'Navbar' && this.parent().bar) {
10996 cfg.cls += ' navbar-form';
10999 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11000 // on BS4 we do this only if not form
11001 cfg.cls += ' navbar-form';
11009 * return the real input element.
11011 inputEl: function ()
11013 return this.el.select('input.form-control',true).first();
11016 tooltipEl : function()
11018 return this.inputEl();
11021 indicatorEl : function()
11023 if (Roo.bootstrap.version == 4) {
11024 return false; // not enabled in v4 yet.
11027 var indicator = this.el.select('i.roo-required-indicator',true).first();
11037 setDisabled : function(v)
11039 var i = this.inputEl().dom;
11041 i.removeAttribute('disabled');
11045 i.setAttribute('disabled','true');
11047 initEvents : function()
11050 this.inputEl().on("keydown" , this.fireKey, this);
11051 this.inputEl().on("focus", this.onFocus, this);
11052 this.inputEl().on("blur", this.onBlur, this);
11054 this.inputEl().relayEvent('keyup', this);
11055 this.inputEl().relayEvent('paste', this);
11057 this.indicator = this.indicatorEl();
11059 if(this.indicator){
11060 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11063 // reference to original value for reset
11064 this.originalValue = this.getValue();
11065 //Roo.form.TextField.superclass.initEvents.call(this);
11066 if(this.validationEvent == 'keyup'){
11067 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11068 this.inputEl().on('keyup', this.filterValidation, this);
11070 else if(this.validationEvent !== false){
11071 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11074 if(this.selectOnFocus){
11075 this.on("focus", this.preFocus, this);
11078 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11079 this.inputEl().on("keypress", this.filterKeys, this);
11081 this.inputEl().relayEvent('keypress', this);
11084 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11085 this.el.on("click", this.autoSize, this);
11088 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11089 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11092 if (typeof(this.before) == 'object') {
11093 this.before.render(this.el.select('.roo-input-before',true).first());
11095 if (typeof(this.after) == 'object') {
11096 this.after.render(this.el.select('.roo-input-after',true).first());
11099 this.inputEl().on('change', this.onChange, this);
11102 filterValidation : function(e){
11103 if(!e.isNavKeyPress()){
11104 this.validationTask.delay(this.validationDelay);
11108 * Validates the field value
11109 * @return {Boolean} True if the value is valid, else false
11111 validate : function(){
11112 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11113 if(this.disabled || this.validateValue(this.getRawValue())){
11118 this.markInvalid();
11124 * Validates a value according to the field's validation rules and marks the field as invalid
11125 * if the validation fails
11126 * @param {Mixed} value The value to validate
11127 * @return {Boolean} True if the value is valid, else false
11129 validateValue : function(value)
11131 if(this.getVisibilityEl().hasClass('hidden')){
11135 if(value.length < 1) { // if it's blank
11136 if(this.allowBlank){
11142 if(value.length < this.minLength){
11145 if(value.length > this.maxLength){
11149 var vt = Roo.form.VTypes;
11150 if(!vt[this.vtype](value, this)){
11154 if(typeof this.validator == "function"){
11155 var msg = this.validator(value);
11159 if (typeof(msg) == 'string') {
11160 this.invalidText = msg;
11164 if(this.regex && !this.regex.test(value)){
11172 fireKey : function(e){
11173 //Roo.log('field ' + e.getKey());
11174 if(e.isNavKeyPress()){
11175 this.fireEvent("specialkey", this, e);
11178 focus : function (selectText){
11180 this.inputEl().focus();
11181 if(selectText === true){
11182 this.inputEl().dom.select();
11188 onFocus : function(){
11189 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11190 // this.el.addClass(this.focusClass);
11192 if(!this.hasFocus){
11193 this.hasFocus = true;
11194 this.startValue = this.getValue();
11195 this.fireEvent("focus", this);
11199 beforeBlur : Roo.emptyFn,
11203 onBlur : function(){
11205 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11206 //this.el.removeClass(this.focusClass);
11208 this.hasFocus = false;
11209 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11212 var v = this.getValue();
11213 if(String(v) !== String(this.startValue)){
11214 this.fireEvent('change', this, v, this.startValue);
11216 this.fireEvent("blur", this);
11219 onChange : function(e)
11221 var v = this.getValue();
11222 if(String(v) !== String(this.startValue)){
11223 this.fireEvent('change', this, v, this.startValue);
11229 * Resets the current field value to the originally loaded value and clears any validation messages
11231 reset : function(){
11232 this.setValue(this.originalValue);
11236 * Returns the name of the field
11237 * @return {Mixed} name The name field
11239 getName: function(){
11243 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11244 * @return {Mixed} value The field value
11246 getValue : function(){
11248 var v = this.inputEl().getValue();
11253 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11254 * @return {Mixed} value The field value
11256 getRawValue : function(){
11257 var v = this.inputEl().getValue();
11263 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11264 * @param {Mixed} value The value to set
11266 setRawValue : function(v){
11267 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11270 selectText : function(start, end){
11271 var v = this.getRawValue();
11273 start = start === undefined ? 0 : start;
11274 end = end === undefined ? v.length : end;
11275 var d = this.inputEl().dom;
11276 if(d.setSelectionRange){
11277 d.setSelectionRange(start, end);
11278 }else if(d.createTextRange){
11279 var range = d.createTextRange();
11280 range.moveStart("character", start);
11281 range.moveEnd("character", v.length-end);
11288 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11289 * @param {Mixed} value The value to set
11291 setValue : function(v){
11294 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11300 processValue : function(value){
11301 if(this.stripCharsRe){
11302 var newValue = value.replace(this.stripCharsRe, '');
11303 if(newValue !== value){
11304 this.setRawValue(newValue);
11311 preFocus : function(){
11313 if(this.selectOnFocus){
11314 this.inputEl().dom.select();
11317 filterKeys : function(e){
11318 var k = e.getKey();
11319 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11322 var c = e.getCharCode(), cc = String.fromCharCode(c);
11323 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11326 if(!this.maskRe.test(cc)){
11331 * Clear any invalid styles/messages for this field
11333 clearInvalid : function(){
11335 if(!this.el || this.preventMark){ // not rendered
11340 this.el.removeClass([this.invalidClass, 'is-invalid']);
11342 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11344 var feedback = this.el.select('.form-control-feedback', true).first();
11347 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11352 if(this.indicator){
11353 this.indicator.removeClass('visible');
11354 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11357 this.fireEvent('valid', this);
11361 * Mark this field as valid
11363 markValid : function()
11365 if(!this.el || this.preventMark){ // not rendered...
11369 this.el.removeClass([this.invalidClass, this.validClass]);
11370 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11372 var feedback = this.el.select('.form-control-feedback', true).first();
11375 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11378 if(this.indicator){
11379 this.indicator.removeClass('visible');
11380 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11388 if(this.allowBlank && !this.getRawValue().length){
11391 if (Roo.bootstrap.version == 3) {
11392 this.el.addClass(this.validClass);
11394 this.inputEl().addClass('is-valid');
11397 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
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]);
11403 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11408 this.fireEvent('valid', this);
11412 * Mark this field as invalid
11413 * @param {String} msg The validation message
11415 markInvalid : function(msg)
11417 if(!this.el || this.preventMark){ // not rendered
11421 this.el.removeClass([this.invalidClass, this.validClass]);
11422 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11424 var feedback = this.el.select('.form-control-feedback', true).first();
11427 this.el.select('.form-control-feedback', true).first().removeClass(
11428 [this.invalidFeedbackClass, this.validFeedbackClass]);
11435 if(this.allowBlank && !this.getRawValue().length){
11439 if(this.indicator){
11440 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11441 this.indicator.addClass('visible');
11443 if (Roo.bootstrap.version == 3) {
11444 this.el.addClass(this.invalidClass);
11446 this.inputEl().addClass('is-invalid');
11451 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11453 var feedback = this.el.select('.form-control-feedback', true).first();
11456 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11458 if(this.getValue().length || this.forceFeedback){
11459 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11466 this.fireEvent('invalid', this, msg);
11469 SafariOnKeyDown : function(event)
11471 // this is a workaround for a password hang bug on chrome/ webkit.
11472 if (this.inputEl().dom.type != 'password') {
11476 var isSelectAll = false;
11478 if(this.inputEl().dom.selectionEnd > 0){
11479 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11481 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11482 event.preventDefault();
11487 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11489 event.preventDefault();
11490 // this is very hacky as keydown always get's upper case.
11492 var cc = String.fromCharCode(event.getCharCode());
11493 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11497 adjustWidth : function(tag, w){
11498 tag = tag.toLowerCase();
11499 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11500 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11501 if(tag == 'input'){
11504 if(tag == 'textarea'){
11507 }else if(Roo.isOpera){
11508 if(tag == 'input'){
11511 if(tag == 'textarea'){
11519 setFieldLabel : function(v)
11521 if(!this.rendered){
11525 if(this.indicatorEl()){
11526 var ar = this.el.select('label > span',true);
11528 if (ar.elements.length) {
11529 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11530 this.fieldLabel = v;
11534 var br = this.el.select('label',true);
11536 if(br.elements.length) {
11537 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11538 this.fieldLabel = v;
11542 Roo.log('Cannot Found any of label > span || label in input');
11546 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11547 this.fieldLabel = v;
11562 * @class Roo.bootstrap.TextArea
11563 * @extends Roo.bootstrap.Input
11564 * Bootstrap TextArea class
11565 * @cfg {Number} cols Specifies the visible width of a text area
11566 * @cfg {Number} rows Specifies the visible number of lines in a text area
11567 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11568 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11569 * @cfg {string} html text
11572 * Create a new TextArea
11573 * @param {Object} config The config object
11576 Roo.bootstrap.TextArea = function(config){
11577 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11581 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11591 getAutoCreate : function(){
11593 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11599 if(this.inputType != 'hidden'){
11600 cfg.cls = 'form-group' //input-group
11608 value : this.value || '',
11609 html: this.html || '',
11610 cls : 'form-control',
11611 placeholder : this.placeholder || ''
11615 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11616 input.maxLength = this.maxLength;
11620 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11624 input.cols = this.cols;
11627 if (this.readOnly) {
11628 input.readonly = true;
11632 input.name = this.name;
11636 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11640 ['xs','sm','md','lg'].map(function(size){
11641 if (settings[size]) {
11642 cfg.cls += ' col-' + size + '-' + settings[size];
11646 var inputblock = input;
11648 if(this.hasFeedback && !this.allowBlank){
11652 cls: 'glyphicon form-control-feedback'
11656 cls : 'has-feedback',
11665 if (this.before || this.after) {
11668 cls : 'input-group',
11672 inputblock.cn.push({
11674 cls : 'input-group-addon',
11679 inputblock.cn.push(input);
11681 if(this.hasFeedback && !this.allowBlank){
11682 inputblock.cls += ' has-feedback';
11683 inputblock.cn.push(feedback);
11687 inputblock.cn.push({
11689 cls : 'input-group-addon',
11696 if (align ==='left' && this.fieldLabel.length) {
11701 cls : 'control-label',
11702 html : this.fieldLabel
11713 if(this.labelWidth > 12){
11714 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11717 if(this.labelWidth < 13 && this.labelmd == 0){
11718 this.labelmd = this.labelWidth;
11721 if(this.labellg > 0){
11722 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11723 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11726 if(this.labelmd > 0){
11727 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11728 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11731 if(this.labelsm > 0){
11732 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11733 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11736 if(this.labelxs > 0){
11737 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11738 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11741 } else if ( this.fieldLabel.length) {
11746 //cls : 'input-group-addon',
11747 html : this.fieldLabel
11765 if (this.disabled) {
11766 input.disabled=true;
11773 * return the real textarea element.
11775 inputEl: function ()
11777 return this.el.select('textarea.form-control',true).first();
11781 * Clear any invalid styles/messages for this field
11783 clearInvalid : function()
11786 if(!this.el || this.preventMark){ // not rendered
11790 var label = this.el.select('label', true).first();
11791 var icon = this.el.select('i.fa-star', true).first();
11796 this.el.removeClass( this.validClass);
11797 this.inputEl().removeClass('is-invalid');
11799 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11801 var feedback = this.el.select('.form-control-feedback', true).first();
11804 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11809 this.fireEvent('valid', this);
11813 * Mark this field as valid
11815 markValid : function()
11817 if(!this.el || this.preventMark){ // not rendered
11821 this.el.removeClass([this.invalidClass, this.validClass]);
11822 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11824 var feedback = this.el.select('.form-control-feedback', true).first();
11827 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11830 if(this.disabled || this.allowBlank){
11834 var label = this.el.select('label', true).first();
11835 var icon = this.el.select('i.fa-star', true).first();
11840 if (Roo.bootstrap.version == 3) {
11841 this.el.addClass(this.validClass);
11843 this.inputEl().addClass('is-valid');
11847 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11849 var feedback = this.el.select('.form-control-feedback', true).first();
11852 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11853 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11858 this.fireEvent('valid', this);
11862 * Mark this field as invalid
11863 * @param {String} msg The validation message
11865 markInvalid : function(msg)
11867 if(!this.el || this.preventMark){ // not rendered
11871 this.el.removeClass([this.invalidClass, this.validClass]);
11872 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11874 var feedback = this.el.select('.form-control-feedback', true).first();
11877 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11880 if(this.disabled || this.allowBlank){
11884 var label = this.el.select('label', true).first();
11885 var icon = this.el.select('i.fa-star', true).first();
11887 if(!this.getValue().length && label && !icon){
11888 this.el.createChild({
11890 cls : 'text-danger fa fa-lg fa-star',
11891 tooltip : 'This field is required',
11892 style : 'margin-right:5px;'
11896 if (Roo.bootstrap.version == 3) {
11897 this.el.addClass(this.invalidClass);
11899 this.inputEl().addClass('is-invalid');
11902 // fixme ... this may be depricated need to test..
11903 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11905 var feedback = this.el.select('.form-control-feedback', true).first();
11908 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11910 if(this.getValue().length || this.forceFeedback){
11911 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11918 this.fireEvent('invalid', this, msg);
11926 * trigger field - base class for combo..
11931 * @class Roo.bootstrap.TriggerField
11932 * @extends Roo.bootstrap.Input
11933 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11934 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11935 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11936 * for which you can provide a custom implementation. For example:
11938 var trigger = new Roo.bootstrap.TriggerField();
11939 trigger.onTriggerClick = myTriggerFn;
11940 trigger.applyTo('my-field');
11943 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11944 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11945 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11946 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11947 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11950 * Create a new TriggerField.
11951 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11952 * to the base TextField)
11954 Roo.bootstrap.TriggerField = function(config){
11955 this.mimicing = false;
11956 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11959 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11961 * @cfg {String} triggerClass A CSS class to apply to the trigger
11964 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11969 * @cfg {Boolean} removable (true|false) special filter default false
11973 /** @cfg {Boolean} grow @hide */
11974 /** @cfg {Number} growMin @hide */
11975 /** @cfg {Number} growMax @hide */
11981 autoSize: Roo.emptyFn,
11985 deferHeight : true,
11988 actionMode : 'wrap',
11993 getAutoCreate : function(){
11995 var align = this.labelAlign || this.parentLabelAlign();
12000 cls: 'form-group' //input-group
12007 type : this.inputType,
12008 cls : 'form-control',
12009 autocomplete: 'new-password',
12010 placeholder : this.placeholder || ''
12014 input.name = this.name;
12017 input.cls += ' input-' + this.size;
12020 if (this.disabled) {
12021 input.disabled=true;
12024 var inputblock = input;
12026 if(this.hasFeedback && !this.allowBlank){
12030 cls: 'glyphicon form-control-feedback'
12033 if(this.removable && !this.editable ){
12035 cls : 'has-feedback',
12041 cls : 'roo-combo-removable-btn close'
12048 cls : 'has-feedback',
12057 if(this.removable && !this.editable ){
12059 cls : 'roo-removable',
12065 cls : 'roo-combo-removable-btn close'
12072 if (this.before || this.after) {
12075 cls : 'input-group',
12079 inputblock.cn.push({
12081 cls : 'input-group-addon input-group-prepend input-group-text',
12086 inputblock.cn.push(input);
12088 if(this.hasFeedback && !this.allowBlank){
12089 inputblock.cls += ' has-feedback';
12090 inputblock.cn.push(feedback);
12094 inputblock.cn.push({
12096 cls : 'input-group-addon input-group-append input-group-text',
12105 var ibwrap = inputblock;
12110 cls: 'roo-select2-choices',
12114 cls: 'roo-select2-search-field',
12126 cls: 'roo-select2-container input-group',
12131 cls: 'form-hidden-field'
12137 if(!this.multiple && this.showToggleBtn){
12143 if (this.caret != false) {
12146 cls: 'fa fa-' + this.caret
12153 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12155 Roo.bootstrap.version == 3 ? caret : '',
12158 cls: 'combobox-clear',
12172 combobox.cls += ' roo-select2-container-multi';
12176 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12177 tooltip : 'This field is required'
12179 if (Roo.bootstrap.version == 4) {
12182 style : 'display:none'
12187 if (align ==='left' && this.fieldLabel.length) {
12189 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12196 cls : 'control-label',
12197 html : this.fieldLabel
12209 var labelCfg = cfg.cn[1];
12210 var contentCfg = cfg.cn[2];
12212 if(this.indicatorpos == 'right'){
12217 cls : 'control-label',
12221 html : this.fieldLabel
12235 labelCfg = cfg.cn[0];
12236 contentCfg = cfg.cn[1];
12239 if(this.labelWidth > 12){
12240 labelCfg.style = "width: " + this.labelWidth + 'px';
12243 if(this.labelWidth < 13 && this.labelmd == 0){
12244 this.labelmd = this.labelWidth;
12247 if(this.labellg > 0){
12248 labelCfg.cls += ' col-lg-' + this.labellg;
12249 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12252 if(this.labelmd > 0){
12253 labelCfg.cls += ' col-md-' + this.labelmd;
12254 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12257 if(this.labelsm > 0){
12258 labelCfg.cls += ' col-sm-' + this.labelsm;
12259 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12262 if(this.labelxs > 0){
12263 labelCfg.cls += ' col-xs-' + this.labelxs;
12264 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12267 } else if ( this.fieldLabel.length) {
12268 // Roo.log(" label");
12273 //cls : 'input-group-addon',
12274 html : this.fieldLabel
12282 if(this.indicatorpos == 'right'){
12290 html : this.fieldLabel
12304 // Roo.log(" no label && no align");
12311 ['xs','sm','md','lg'].map(function(size){
12312 if (settings[size]) {
12313 cfg.cls += ' col-' + size + '-' + settings[size];
12324 onResize : function(w, h){
12325 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12326 // if(typeof w == 'number'){
12327 // var x = w - this.trigger.getWidth();
12328 // this.inputEl().setWidth(this.adjustWidth('input', x));
12329 // this.trigger.setStyle('left', x+'px');
12334 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12337 getResizeEl : function(){
12338 return this.inputEl();
12342 getPositionEl : function(){
12343 return this.inputEl();
12347 alignErrorIcon : function(){
12348 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12352 initEvents : function(){
12356 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12357 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12358 if(!this.multiple && this.showToggleBtn){
12359 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12360 if(this.hideTrigger){
12361 this.trigger.setDisplayed(false);
12363 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12367 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12370 if(this.removable && !this.editable && !this.tickable){
12371 var close = this.closeTriggerEl();
12374 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12375 close.on('click', this.removeBtnClick, this, close);
12379 //this.trigger.addClassOnOver('x-form-trigger-over');
12380 //this.trigger.addClassOnClick('x-form-trigger-click');
12383 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12387 closeTriggerEl : function()
12389 var close = this.el.select('.roo-combo-removable-btn', true).first();
12390 return close ? close : false;
12393 removeBtnClick : function(e, h, el)
12395 e.preventDefault();
12397 if(this.fireEvent("remove", this) !== false){
12399 this.fireEvent("afterremove", this)
12403 createList : function()
12405 this.list = Roo.get(document.body).createChild({
12406 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12407 cls: 'typeahead typeahead-long dropdown-menu shadow',
12408 style: 'display:none'
12411 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12416 initTrigger : function(){
12421 onDestroy : function(){
12423 this.trigger.removeAllListeners();
12424 // this.trigger.remove();
12427 // this.wrap.remove();
12429 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12433 onFocus : function(){
12434 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12436 if(!this.mimicing){
12437 this.wrap.addClass('x-trigger-wrap-focus');
12438 this.mimicing = true;
12439 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12440 if(this.monitorTab){
12441 this.el.on("keydown", this.checkTab, this);
12448 checkTab : function(e){
12449 if(e.getKey() == e.TAB){
12450 this.triggerBlur();
12455 onBlur : function(){
12460 mimicBlur : function(e, t){
12462 if(!this.wrap.contains(t) && this.validateBlur()){
12463 this.triggerBlur();
12469 triggerBlur : function(){
12470 this.mimicing = false;
12471 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12472 if(this.monitorTab){
12473 this.el.un("keydown", this.checkTab, this);
12475 //this.wrap.removeClass('x-trigger-wrap-focus');
12476 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12480 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12481 validateBlur : function(e, t){
12486 onDisable : function(){
12487 this.inputEl().dom.disabled = true;
12488 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12490 // this.wrap.addClass('x-item-disabled');
12495 onEnable : function(){
12496 this.inputEl().dom.disabled = false;
12497 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12499 // this.el.removeClass('x-item-disabled');
12504 onShow : function(){
12505 var ae = this.getActionEl();
12508 ae.dom.style.display = '';
12509 ae.dom.style.visibility = 'visible';
12515 onHide : function(){
12516 var ae = this.getActionEl();
12517 ae.dom.style.display = 'none';
12521 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12522 * by an implementing function.
12524 * @param {EventObject} e
12526 onTriggerClick : Roo.emptyFn
12534 * @class Roo.bootstrap.CardUploader
12535 * @extends Roo.bootstrap.Button
12536 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12537 * @cfg {Number} errorTimeout default 3000
12538 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12539 * @cfg {Array} html The button text.
12543 * Create a new CardUploader
12544 * @param {Object} config The config object
12547 Roo.bootstrap.CardUploader = function(config){
12551 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12554 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12562 * When a image is clicked on - and needs to display a slideshow or similar..
12563 * @param {Roo.bootstrap.Card} this
12564 * @param {Object} The image information data
12570 * When a the download link is clicked
12571 * @param {Roo.bootstrap.Card} this
12572 * @param {Object} The image information data contains
12579 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12582 errorTimeout : 3000,
12586 fileCollection : false,
12589 getAutoCreate : function()
12593 cls :'form-group' ,
12598 //cls : 'input-group-addon',
12599 html : this.fieldLabel
12607 value : this.value,
12608 cls : 'd-none form-control'
12613 multiple : 'multiple',
12615 cls : 'd-none roo-card-upload-selector'
12619 cls : 'roo-card-uploader-button-container w-100 mb-2'
12622 cls : 'card-columns roo-card-uploader-container'
12632 getChildContainer : function() /// what children are added to.
12634 return this.containerEl;
12637 getButtonContainer : function() /// what children are added to.
12639 return this.el.select(".roo-card-uploader-button-container").first();
12642 initEvents : function()
12645 Roo.bootstrap.Input.prototype.initEvents.call(this);
12649 xns: Roo.bootstrap,
12652 container_method : 'getButtonContainer' ,
12653 html : this.html, // fix changable?
12656 'click' : function(btn, e) {
12665 this.urlAPI = (window.createObjectURL && window) ||
12666 (window.URL && URL.revokeObjectURL && URL) ||
12667 (window.webkitURL && webkitURL);
12672 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12674 this.selectorEl.on('change', this.onFileSelected, this);
12677 this.images.forEach(function(img) {
12680 this.images = false;
12682 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12688 onClick : function(e)
12690 e.preventDefault();
12692 this.selectorEl.dom.click();
12696 onFileSelected : function(e)
12698 e.preventDefault();
12700 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12704 Roo.each(this.selectorEl.dom.files, function(file){
12705 this.addFile(file);
12714 addFile : function(file)
12717 if(typeof(file) === 'string'){
12718 throw "Add file by name?"; // should not happen
12722 if(!file || !this.urlAPI){
12732 var url = _this.urlAPI.createObjectURL( file);
12735 id : Roo.bootstrap.CardUploader.ID--,
12736 is_uploaded : false,
12740 mimetype : file.type,
12748 * addCard - add an Attachment to the uploader
12749 * @param data - the data about the image to upload
12753 title : "Title of file",
12754 is_uploaded : false,
12755 src : "http://.....",
12756 srcfile : { the File upload object },
12757 mimetype : file.type,
12760 .. any other data...
12766 addCard : function (data)
12768 // hidden input element?
12769 // if the file is not an image...
12770 //then we need to use something other that and header_image
12775 xns : Roo.bootstrap,
12776 xtype : 'CardFooter',
12779 xns : Roo.bootstrap,
12785 xns : Roo.bootstrap,
12787 html : String.format("<small>{0}</small>", data.title),
12788 cls : 'col-10 text-left',
12793 click : function() {
12795 t.fireEvent( "download", t, data );
12801 xns : Roo.bootstrap,
12803 style: 'max-height: 28px; ',
12809 click : function() {
12810 t.removeCard(data.id)
12822 var cn = this.addxtype(
12825 xns : Roo.bootstrap,
12828 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12829 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12830 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12835 initEvents : function() {
12836 Roo.bootstrap.Card.prototype.initEvents.call(this);
12838 this.imgEl = this.el.select('.card-img-top').first();
12840 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12841 this.imgEl.set({ 'pointer' : 'cursor' });
12844 this.getCardFooter().addClass('p-1');
12851 // dont' really need ot update items.
12852 // this.items.push(cn);
12853 this.fileCollection.add(cn);
12855 if (!data.srcfile) {
12856 this.updateInput();
12861 var reader = new FileReader();
12862 reader.addEventListener("load", function() {
12863 data.srcdata = reader.result;
12866 reader.readAsDataURL(data.srcfile);
12871 removeCard : function(id)
12874 var card = this.fileCollection.get(id);
12875 card.data.is_deleted = 1;
12876 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12877 //this.fileCollection.remove(card);
12878 //this.items = this.items.filter(function(e) { return e != card });
12879 // dont' really need ot update items.
12880 card.el.dom.parentNode.removeChild(card.el.dom);
12881 this.updateInput();
12887 this.fileCollection.each(function(card) {
12888 if (card.el.dom && card.el.dom.parentNode) {
12889 card.el.dom.parentNode.removeChild(card.el.dom);
12892 this.fileCollection.clear();
12893 this.updateInput();
12896 updateInput : function()
12899 this.fileCollection.each(function(e) {
12903 this.inputEl().dom.value = JSON.stringify(data);
12913 Roo.bootstrap.CardUploader.ID = -1;/*
12915 * Ext JS Library 1.1.1
12916 * Copyright(c) 2006-2007, Ext JS, LLC.
12918 * Originally Released Under LGPL - original licence link has changed is not relivant.
12921 * <script type="text/javascript">
12926 * @class Roo.data.SortTypes
12928 * Defines the default sorting (casting?) comparison functions used when sorting data.
12930 Roo.data.SortTypes = {
12932 * Default sort that does nothing
12933 * @param {Mixed} s The value being converted
12934 * @return {Mixed} The comparison value
12936 none : function(s){
12941 * The regular expression used to strip tags
12945 stripTagsRE : /<\/?[^>]+>/gi,
12948 * Strips all HTML tags to sort on text only
12949 * @param {Mixed} s The value being converted
12950 * @return {String} The comparison value
12952 asText : function(s){
12953 return String(s).replace(this.stripTagsRE, "");
12957 * Strips all HTML tags to sort on text only - Case insensitive
12958 * @param {Mixed} s The value being converted
12959 * @return {String} The comparison value
12961 asUCText : function(s){
12962 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12966 * Case insensitive string
12967 * @param {Mixed} s The value being converted
12968 * @return {String} The comparison value
12970 asUCString : function(s) {
12971 return String(s).toUpperCase();
12976 * @param {Mixed} s The value being converted
12977 * @return {Number} The comparison value
12979 asDate : function(s) {
12983 if(s instanceof Date){
12984 return s.getTime();
12986 return Date.parse(String(s));
12991 * @param {Mixed} s The value being converted
12992 * @return {Float} The comparison value
12994 asFloat : function(s) {
12995 var val = parseFloat(String(s).replace(/,/g, ""));
13004 * @param {Mixed} s The value being converted
13005 * @return {Number} The comparison value
13007 asInt : function(s) {
13008 var val = parseInt(String(s).replace(/,/g, ""));
13016 * Ext JS Library 1.1.1
13017 * Copyright(c) 2006-2007, Ext JS, LLC.
13019 * Originally Released Under LGPL - original licence link has changed is not relivant.
13022 * <script type="text/javascript">
13026 * @class Roo.data.Record
13027 * Instances of this class encapsulate both record <em>definition</em> information, and record
13028 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13029 * to access Records cached in an {@link Roo.data.Store} object.<br>
13031 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13032 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13035 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13037 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13038 * {@link #create}. The parameters are the same.
13039 * @param {Array} data An associative Array of data values keyed by the field name.
13040 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13041 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13042 * not specified an integer id is generated.
13044 Roo.data.Record = function(data, id){
13045 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13050 * Generate a constructor for a specific record layout.
13051 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13052 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13053 * Each field definition object may contain the following properties: <ul>
13054 * <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,
13055 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13056 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13057 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13058 * is being used, then this is a string containing the javascript expression to reference the data relative to
13059 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13060 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13061 * this may be omitted.</p></li>
13062 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13063 * <ul><li>auto (Default, implies no conversion)</li>
13068 * <li>date</li></ul></p></li>
13069 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13070 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13071 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13072 * by the Reader into an object that will be stored in the Record. It is passed the
13073 * following parameters:<ul>
13074 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13076 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13078 * <br>usage:<br><pre><code>
13079 var TopicRecord = Roo.data.Record.create(
13080 {name: 'title', mapping: 'topic_title'},
13081 {name: 'author', mapping: 'username'},
13082 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13083 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13084 {name: 'lastPoster', mapping: 'user2'},
13085 {name: 'excerpt', mapping: 'post_text'}
13088 var myNewRecord = new TopicRecord({
13089 title: 'Do my job please',
13092 lastPost: new Date(),
13093 lastPoster: 'Animal',
13094 excerpt: 'No way dude!'
13096 myStore.add(myNewRecord);
13101 Roo.data.Record.create = function(o){
13102 var f = function(){
13103 f.superclass.constructor.apply(this, arguments);
13105 Roo.extend(f, Roo.data.Record);
13106 var p = f.prototype;
13107 p.fields = new Roo.util.MixedCollection(false, function(field){
13110 for(var i = 0, len = o.length; i < len; i++){
13111 p.fields.add(new Roo.data.Field(o[i]));
13113 f.getField = function(name){
13114 return p.fields.get(name);
13119 Roo.data.Record.AUTO_ID = 1000;
13120 Roo.data.Record.EDIT = 'edit';
13121 Roo.data.Record.REJECT = 'reject';
13122 Roo.data.Record.COMMIT = 'commit';
13124 Roo.data.Record.prototype = {
13126 * Readonly flag - true if this record has been modified.
13135 join : function(store){
13136 this.store = store;
13140 * Set the named field to the specified value.
13141 * @param {String} name The name of the field to set.
13142 * @param {Object} value The value to set the field to.
13144 set : function(name, value){
13145 if(this.data[name] == value){
13149 if(!this.modified){
13150 this.modified = {};
13152 if(typeof this.modified[name] == 'undefined'){
13153 this.modified[name] = this.data[name];
13155 this.data[name] = value;
13156 if(!this.editing && this.store){
13157 this.store.afterEdit(this);
13162 * Get the value of the named field.
13163 * @param {String} name The name of the field to get the value of.
13164 * @return {Object} The value of the field.
13166 get : function(name){
13167 return this.data[name];
13171 beginEdit : function(){
13172 this.editing = true;
13173 this.modified = {};
13177 cancelEdit : function(){
13178 this.editing = false;
13179 delete this.modified;
13183 endEdit : function(){
13184 this.editing = false;
13185 if(this.dirty && this.store){
13186 this.store.afterEdit(this);
13191 * Usually called by the {@link Roo.data.Store} which owns the Record.
13192 * Rejects all changes made to the Record since either creation, or the last commit operation.
13193 * Modified fields are reverted to their original values.
13195 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13196 * of reject operations.
13198 reject : function(){
13199 var m = this.modified;
13201 if(typeof m[n] != "function"){
13202 this.data[n] = m[n];
13205 this.dirty = false;
13206 delete this.modified;
13207 this.editing = false;
13209 this.store.afterReject(this);
13214 * Usually called by the {@link Roo.data.Store} which owns the Record.
13215 * Commits all changes made to the Record since either creation, or the last commit operation.
13217 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13218 * of commit operations.
13220 commit : function(){
13221 this.dirty = false;
13222 delete this.modified;
13223 this.editing = false;
13225 this.store.afterCommit(this);
13230 hasError : function(){
13231 return this.error != null;
13235 clearError : function(){
13240 * Creates a copy of this record.
13241 * @param {String} id (optional) A new record id if you don't want to use this record's id
13244 copy : function(newId) {
13245 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13249 * Ext JS Library 1.1.1
13250 * Copyright(c) 2006-2007, Ext JS, LLC.
13252 * Originally Released Under LGPL - original licence link has changed is not relivant.
13255 * <script type="text/javascript">
13261 * @class Roo.data.Store
13262 * @extends Roo.util.Observable
13263 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13264 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13266 * 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
13267 * has no knowledge of the format of the data returned by the Proxy.<br>
13269 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13270 * instances from the data object. These records are cached and made available through accessor functions.
13272 * Creates a new Store.
13273 * @param {Object} config A config object containing the objects needed for the Store to access data,
13274 * and read the data into Records.
13276 Roo.data.Store = function(config){
13277 this.data = new Roo.util.MixedCollection(false);
13278 this.data.getKey = function(o){
13281 this.baseParams = {};
13283 this.paramNames = {
13288 "multisort" : "_multisort"
13291 if(config && config.data){
13292 this.inlineData = config.data;
13293 delete config.data;
13296 Roo.apply(this, config);
13298 if(this.reader){ // reader passed
13299 this.reader = Roo.factory(this.reader, Roo.data);
13300 this.reader.xmodule = this.xmodule || false;
13301 if(!this.recordType){
13302 this.recordType = this.reader.recordType;
13304 if(this.reader.onMetaChange){
13305 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13309 if(this.recordType){
13310 this.fields = this.recordType.prototype.fields;
13312 this.modified = [];
13316 * @event datachanged
13317 * Fires when the data cache has changed, and a widget which is using this Store
13318 * as a Record cache should refresh its view.
13319 * @param {Store} this
13321 datachanged : true,
13323 * @event metachange
13324 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13325 * @param {Store} this
13326 * @param {Object} meta The JSON metadata
13331 * Fires when Records have been added to the Store
13332 * @param {Store} this
13333 * @param {Roo.data.Record[]} records The array of Records added
13334 * @param {Number} index The index at which the record(s) were added
13339 * Fires when a Record has been removed from the Store
13340 * @param {Store} this
13341 * @param {Roo.data.Record} record The Record that was removed
13342 * @param {Number} index The index at which the record was removed
13347 * Fires when a Record has been updated
13348 * @param {Store} this
13349 * @param {Roo.data.Record} record The Record that was updated
13350 * @param {String} operation The update operation being performed. Value may be one of:
13352 Roo.data.Record.EDIT
13353 Roo.data.Record.REJECT
13354 Roo.data.Record.COMMIT
13360 * Fires when the data cache has been cleared.
13361 * @param {Store} this
13365 * @event beforeload
13366 * Fires before a request is made for a new data object. If the beforeload handler returns false
13367 * the load action will be canceled.
13368 * @param {Store} this
13369 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13373 * @event beforeloadadd
13374 * Fires after a new set of Records has been loaded.
13375 * @param {Store} this
13376 * @param {Roo.data.Record[]} records The Records that were loaded
13377 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13379 beforeloadadd : true,
13382 * Fires after a new set of Records has been loaded, before they are added to the store.
13383 * @param {Store} this
13384 * @param {Roo.data.Record[]} records The Records that were loaded
13385 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13386 * @params {Object} return from reader
13390 * @event loadexception
13391 * Fires if an exception occurs in the Proxy during loading.
13392 * Called with the signature of the Proxy's "loadexception" event.
13393 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13396 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13397 * @param {Object} load options
13398 * @param {Object} jsonData from your request (normally this contains the Exception)
13400 loadexception : true
13404 this.proxy = Roo.factory(this.proxy, Roo.data);
13405 this.proxy.xmodule = this.xmodule || false;
13406 this.relayEvents(this.proxy, ["loadexception"]);
13408 this.sortToggle = {};
13409 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13411 Roo.data.Store.superclass.constructor.call(this);
13413 if(this.inlineData){
13414 this.loadData(this.inlineData);
13415 delete this.inlineData;
13419 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13421 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13422 * without a remote query - used by combo/forms at present.
13426 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13429 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13432 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13433 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13436 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13437 * on any HTTP request
13440 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13443 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13447 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13448 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13450 remoteSort : false,
13453 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13454 * loaded or when a record is removed. (defaults to false).
13456 pruneModifiedRecords : false,
13459 lastOptions : null,
13462 * Add Records to the Store and fires the add event.
13463 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13465 add : function(records){
13466 records = [].concat(records);
13467 for(var i = 0, len = records.length; i < len; i++){
13468 records[i].join(this);
13470 var index = this.data.length;
13471 this.data.addAll(records);
13472 this.fireEvent("add", this, records, index);
13476 * Remove a Record from the Store and fires the remove event.
13477 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13479 remove : function(record){
13480 var index = this.data.indexOf(record);
13481 this.data.removeAt(index);
13483 if(this.pruneModifiedRecords){
13484 this.modified.remove(record);
13486 this.fireEvent("remove", this, record, index);
13490 * Remove all Records from the Store and fires the clear event.
13492 removeAll : function(){
13494 if(this.pruneModifiedRecords){
13495 this.modified = [];
13497 this.fireEvent("clear", this);
13501 * Inserts Records to the Store at the given index and fires the add event.
13502 * @param {Number} index The start index at which to insert the passed Records.
13503 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13505 insert : function(index, records){
13506 records = [].concat(records);
13507 for(var i = 0, len = records.length; i < len; i++){
13508 this.data.insert(index, records[i]);
13509 records[i].join(this);
13511 this.fireEvent("add", this, records, index);
13515 * Get the index within the cache of the passed Record.
13516 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13517 * @return {Number} The index of the passed Record. Returns -1 if not found.
13519 indexOf : function(record){
13520 return this.data.indexOf(record);
13524 * Get the index within the cache of the Record with the passed id.
13525 * @param {String} id The id of the Record to find.
13526 * @return {Number} The index of the Record. Returns -1 if not found.
13528 indexOfId : function(id){
13529 return this.data.indexOfKey(id);
13533 * Get the Record with the specified id.
13534 * @param {String} id The id of the Record to find.
13535 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13537 getById : function(id){
13538 return this.data.key(id);
13542 * Get the Record at the specified index.
13543 * @param {Number} index The index of the Record to find.
13544 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13546 getAt : function(index){
13547 return this.data.itemAt(index);
13551 * Returns a range of Records between specified indices.
13552 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13553 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13554 * @return {Roo.data.Record[]} An array of Records
13556 getRange : function(start, end){
13557 return this.data.getRange(start, end);
13561 storeOptions : function(o){
13562 o = Roo.apply({}, o);
13565 this.lastOptions = o;
13569 * Loads the Record cache from the configured Proxy using the configured Reader.
13571 * If using remote paging, then the first load call must specify the <em>start</em>
13572 * and <em>limit</em> properties in the options.params property to establish the initial
13573 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13575 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13576 * and this call will return before the new data has been loaded. Perform any post-processing
13577 * in a callback function, or in a "load" event handler.</strong>
13579 * @param {Object} options An object containing properties which control loading options:<ul>
13580 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13581 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13582 * passed the following arguments:<ul>
13583 * <li>r : Roo.data.Record[]</li>
13584 * <li>options: Options object from the load call</li>
13585 * <li>success: Boolean success indicator</li></ul></li>
13586 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13587 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13590 load : function(options){
13591 options = options || {};
13592 if(this.fireEvent("beforeload", this, options) !== false){
13593 this.storeOptions(options);
13594 var p = Roo.apply(options.params || {}, this.baseParams);
13595 // if meta was not loaded from remote source.. try requesting it.
13596 if (!this.reader.metaFromRemote) {
13597 p._requestMeta = 1;
13599 if(this.sortInfo && this.remoteSort){
13600 var pn = this.paramNames;
13601 p[pn["sort"]] = this.sortInfo.field;
13602 p[pn["dir"]] = this.sortInfo.direction;
13604 if (this.multiSort) {
13605 var pn = this.paramNames;
13606 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13609 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13614 * Reloads the Record cache from the configured Proxy using the configured Reader and
13615 * the options from the last load operation performed.
13616 * @param {Object} options (optional) An object containing properties which may override the options
13617 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13618 * the most recently used options are reused).
13620 reload : function(options){
13621 this.load(Roo.applyIf(options||{}, this.lastOptions));
13625 // Called as a callback by the Reader during a load operation.
13626 loadRecords : function(o, options, success){
13627 if(!o || success === false){
13628 if(success !== false){
13629 this.fireEvent("load", this, [], options, o);
13631 if(options.callback){
13632 options.callback.call(options.scope || this, [], options, false);
13636 // if data returned failure - throw an exception.
13637 if (o.success === false) {
13638 // show a message if no listener is registered.
13639 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13640 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13642 // loadmask wil be hooked into this..
13643 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13646 var r = o.records, t = o.totalRecords || r.length;
13648 this.fireEvent("beforeloadadd", this, r, options, o);
13650 if(!options || options.add !== true){
13651 if(this.pruneModifiedRecords){
13652 this.modified = [];
13654 for(var i = 0, len = r.length; i < len; i++){
13658 this.data = this.snapshot;
13659 delete this.snapshot;
13662 this.data.addAll(r);
13663 this.totalLength = t;
13665 this.fireEvent("datachanged", this);
13667 this.totalLength = Math.max(t, this.data.length+r.length);
13671 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13673 var e = new Roo.data.Record({});
13675 e.set(this.parent.displayField, this.parent.emptyTitle);
13676 e.set(this.parent.valueField, '');
13681 this.fireEvent("load", this, r, options, o);
13682 if(options.callback){
13683 options.callback.call(options.scope || this, r, options, true);
13689 * Loads data from a passed data block. A Reader which understands the format of the data
13690 * must have been configured in the constructor.
13691 * @param {Object} data The data block from which to read the Records. The format of the data expected
13692 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13693 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13695 loadData : function(o, append){
13696 var r = this.reader.readRecords(o);
13697 this.loadRecords(r, {add: append}, true);
13701 * using 'cn' the nested child reader read the child array into it's child stores.
13702 * @param {Object} rec The record with a 'children array
13704 loadDataFromChildren : function(rec)
13706 this.loadData(this.reader.toLoadData(rec));
13711 * Gets the number of cached records.
13713 * <em>If using paging, this may not be the total size of the dataset. If the data object
13714 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13715 * the data set size</em>
13717 getCount : function(){
13718 return this.data.length || 0;
13722 * Gets the total number of records in the dataset as returned by the server.
13724 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13725 * the dataset size</em>
13727 getTotalCount : function(){
13728 return this.totalLength || 0;
13732 * Returns the sort state of the Store as an object with two properties:
13734 field {String} The name of the field by which the Records are sorted
13735 direction {String} The sort order, "ASC" or "DESC"
13738 getSortState : function(){
13739 return this.sortInfo;
13743 applySort : function(){
13744 if(this.sortInfo && !this.remoteSort){
13745 var s = this.sortInfo, f = s.field;
13746 var st = this.fields.get(f).sortType;
13747 var fn = function(r1, r2){
13748 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13749 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13751 this.data.sort(s.direction, fn);
13752 if(this.snapshot && this.snapshot != this.data){
13753 this.snapshot.sort(s.direction, fn);
13759 * Sets the default sort column and order to be used by the next load operation.
13760 * @param {String} fieldName The name of the field to sort by.
13761 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13763 setDefaultSort : function(field, dir){
13764 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13768 * Sort the Records.
13769 * If remote sorting is used, the sort is performed on the server, and the cache is
13770 * reloaded. If local sorting is used, the cache is sorted internally.
13771 * @param {String} fieldName The name of the field to sort by.
13772 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13774 sort : function(fieldName, dir){
13775 var f = this.fields.get(fieldName);
13777 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13779 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13780 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13785 this.sortToggle[f.name] = dir;
13786 this.sortInfo = {field: f.name, direction: dir};
13787 if(!this.remoteSort){
13789 this.fireEvent("datachanged", this);
13791 this.load(this.lastOptions);
13796 * Calls the specified function for each of the Records in the cache.
13797 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13798 * Returning <em>false</em> aborts and exits the iteration.
13799 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13801 each : function(fn, scope){
13802 this.data.each(fn, scope);
13806 * Gets all records modified since the last commit. Modified records are persisted across load operations
13807 * (e.g., during paging).
13808 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13810 getModifiedRecords : function(){
13811 return this.modified;
13815 createFilterFn : function(property, value, anyMatch){
13816 if(!value.exec){ // not a regex
13817 value = String(value);
13818 if(value.length == 0){
13821 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13823 return function(r){
13824 return value.test(r.data[property]);
13829 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13830 * @param {String} property A field on your records
13831 * @param {Number} start The record index to start at (defaults to 0)
13832 * @param {Number} end The last record index to include (defaults to length - 1)
13833 * @return {Number} The sum
13835 sum : function(property, start, end){
13836 var rs = this.data.items, v = 0;
13837 start = start || 0;
13838 end = (end || end === 0) ? end : rs.length-1;
13840 for(var i = start; i <= end; i++){
13841 v += (rs[i].data[property] || 0);
13847 * Filter the records by a specified property.
13848 * @param {String} field A field on your records
13849 * @param {String/RegExp} value Either a string that the field
13850 * should start with or a RegExp to test against the field
13851 * @param {Boolean} anyMatch True to match any part not just the beginning
13853 filter : function(property, value, anyMatch){
13854 var fn = this.createFilterFn(property, value, anyMatch);
13855 return fn ? this.filterBy(fn) : this.clearFilter();
13859 * Filter by a function. The specified function will be called with each
13860 * record in this data source. If the function returns true the record is included,
13861 * otherwise it is filtered.
13862 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13863 * @param {Object} scope (optional) The scope of the function (defaults to this)
13865 filterBy : function(fn, scope){
13866 this.snapshot = this.snapshot || this.data;
13867 this.data = this.queryBy(fn, scope||this);
13868 this.fireEvent("datachanged", this);
13872 * Query the records by a specified property.
13873 * @param {String} field A field on your records
13874 * @param {String/RegExp} value Either a string that the field
13875 * should start with or a RegExp to test against the field
13876 * @param {Boolean} anyMatch True to match any part not just the beginning
13877 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13879 query : function(property, value, anyMatch){
13880 var fn = this.createFilterFn(property, value, anyMatch);
13881 return fn ? this.queryBy(fn) : this.data.clone();
13885 * Query by a function. The specified function will be called with each
13886 * record in this data source. If the function returns true the record is included
13888 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13889 * @param {Object} scope (optional) The scope of the function (defaults to this)
13890 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13892 queryBy : function(fn, scope){
13893 var data = this.snapshot || this.data;
13894 return data.filterBy(fn, scope||this);
13898 * Collects unique values for a particular dataIndex from this store.
13899 * @param {String} dataIndex The property to collect
13900 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13901 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13902 * @return {Array} An array of the unique values
13904 collect : function(dataIndex, allowNull, bypassFilter){
13905 var d = (bypassFilter === true && this.snapshot) ?
13906 this.snapshot.items : this.data.items;
13907 var v, sv, r = [], l = {};
13908 for(var i = 0, len = d.length; i < len; i++){
13909 v = d[i].data[dataIndex];
13911 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13920 * Revert to a view of the Record cache with no filtering applied.
13921 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13923 clearFilter : function(suppressEvent){
13924 if(this.snapshot && this.snapshot != this.data){
13925 this.data = this.snapshot;
13926 delete this.snapshot;
13927 if(suppressEvent !== true){
13928 this.fireEvent("datachanged", this);
13934 afterEdit : function(record){
13935 if(this.modified.indexOf(record) == -1){
13936 this.modified.push(record);
13938 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13942 afterReject : function(record){
13943 this.modified.remove(record);
13944 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13948 afterCommit : function(record){
13949 this.modified.remove(record);
13950 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13954 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13955 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13957 commitChanges : function(){
13958 var m = this.modified.slice(0);
13959 this.modified = [];
13960 for(var i = 0, len = m.length; i < len; i++){
13966 * Cancel outstanding changes on all changed records.
13968 rejectChanges : function(){
13969 var m = this.modified.slice(0);
13970 this.modified = [];
13971 for(var i = 0, len = m.length; i < len; i++){
13976 onMetaChange : function(meta, rtype, o){
13977 this.recordType = rtype;
13978 this.fields = rtype.prototype.fields;
13979 delete this.snapshot;
13980 this.sortInfo = meta.sortInfo || this.sortInfo;
13981 this.modified = [];
13982 this.fireEvent('metachange', this, this.reader.meta);
13985 moveIndex : function(data, type)
13987 var index = this.indexOf(data);
13989 var newIndex = index + type;
13993 this.insert(newIndex, data);
13998 * Ext JS Library 1.1.1
13999 * Copyright(c) 2006-2007, Ext JS, LLC.
14001 * Originally Released Under LGPL - original licence link has changed is not relivant.
14004 * <script type="text/javascript">
14008 * @class Roo.data.SimpleStore
14009 * @extends Roo.data.Store
14010 * Small helper class to make creating Stores from Array data easier.
14011 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14012 * @cfg {Array} fields An array of field definition objects, or field name strings.
14013 * @cfg {Object} an existing reader (eg. copied from another store)
14014 * @cfg {Array} data The multi-dimensional array of data
14016 * @param {Object} config
14018 Roo.data.SimpleStore = function(config)
14020 Roo.data.SimpleStore.superclass.constructor.call(this, {
14022 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14025 Roo.data.Record.create(config.fields)
14027 proxy : new Roo.data.MemoryProxy(config.data)
14031 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14033 * Ext JS Library 1.1.1
14034 * Copyright(c) 2006-2007, Ext JS, LLC.
14036 * Originally Released Under LGPL - original licence link has changed is not relivant.
14039 * <script type="text/javascript">
14044 * @extends Roo.data.Store
14045 * @class Roo.data.JsonStore
14046 * Small helper class to make creating Stores for JSON data easier. <br/>
14048 var store = new Roo.data.JsonStore({
14049 url: 'get-images.php',
14051 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14054 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14055 * JsonReader and HttpProxy (unless inline data is provided).</b>
14056 * @cfg {Array} fields An array of field definition objects, or field name strings.
14058 * @param {Object} config
14060 Roo.data.JsonStore = function(c){
14061 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14062 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14063 reader: new Roo.data.JsonReader(c, c.fields)
14066 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14068 * Ext JS Library 1.1.1
14069 * Copyright(c) 2006-2007, Ext JS, LLC.
14071 * Originally Released Under LGPL - original licence link has changed is not relivant.
14074 * <script type="text/javascript">
14078 Roo.data.Field = function(config){
14079 if(typeof config == "string"){
14080 config = {name: config};
14082 Roo.apply(this, config);
14085 this.type = "auto";
14088 var st = Roo.data.SortTypes;
14089 // named sortTypes are supported, here we look them up
14090 if(typeof this.sortType == "string"){
14091 this.sortType = st[this.sortType];
14094 // set default sortType for strings and dates
14095 if(!this.sortType){
14098 this.sortType = st.asUCString;
14101 this.sortType = st.asDate;
14104 this.sortType = st.none;
14109 var stripRe = /[\$,%]/g;
14111 // prebuilt conversion function for this field, instead of
14112 // switching every time we're reading a value
14114 var cv, dateFormat = this.dateFormat;
14119 cv = function(v){ return v; };
14122 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14126 return v !== undefined && v !== null && v !== '' ?
14127 parseInt(String(v).replace(stripRe, ""), 10) : '';
14132 return v !== undefined && v !== null && v !== '' ?
14133 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14138 cv = function(v){ return v === true || v === "true" || v == 1; };
14145 if(v instanceof Date){
14149 if(dateFormat == "timestamp"){
14150 return new Date(v*1000);
14152 return Date.parseDate(v, dateFormat);
14154 var parsed = Date.parse(v);
14155 return parsed ? new Date(parsed) : null;
14164 Roo.data.Field.prototype = {
14172 * Ext JS Library 1.1.1
14173 * Copyright(c) 2006-2007, Ext JS, LLC.
14175 * Originally Released Under LGPL - original licence link has changed is not relivant.
14178 * <script type="text/javascript">
14181 // Base class for reading structured data from a data source. This class is intended to be
14182 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14185 * @class Roo.data.DataReader
14186 * Base class for reading structured data from a data source. This class is intended to be
14187 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14190 Roo.data.DataReader = function(meta, recordType){
14194 this.recordType = recordType instanceof Array ?
14195 Roo.data.Record.create(recordType) : recordType;
14198 Roo.data.DataReader.prototype = {
14201 readerType : 'Data',
14203 * Create an empty record
14204 * @param {Object} data (optional) - overlay some values
14205 * @return {Roo.data.Record} record created.
14207 newRow : function(d) {
14209 this.recordType.prototype.fields.each(function(c) {
14211 case 'int' : da[c.name] = 0; break;
14212 case 'date' : da[c.name] = new Date(); break;
14213 case 'float' : da[c.name] = 0.0; break;
14214 case 'boolean' : da[c.name] = false; break;
14215 default : da[c.name] = ""; break;
14219 return new this.recordType(Roo.apply(da, d));
14225 * Ext JS Library 1.1.1
14226 * Copyright(c) 2006-2007, Ext JS, LLC.
14228 * Originally Released Under LGPL - original licence link has changed is not relivant.
14231 * <script type="text/javascript">
14235 * @class Roo.data.DataProxy
14236 * @extends Roo.data.Observable
14237 * This class is an abstract base class for implementations which provide retrieval of
14238 * unformatted data objects.<br>
14240 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14241 * (of the appropriate type which knows how to parse the data object) to provide a block of
14242 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14244 * Custom implementations must implement the load method as described in
14245 * {@link Roo.data.HttpProxy#load}.
14247 Roo.data.DataProxy = function(){
14250 * @event beforeload
14251 * Fires before a network request is made to retrieve a data object.
14252 * @param {Object} This DataProxy object.
14253 * @param {Object} params The params parameter to the load function.
14258 * Fires before the load method's callback is called.
14259 * @param {Object} This DataProxy object.
14260 * @param {Object} o The data object.
14261 * @param {Object} arg The callback argument object passed to the load function.
14265 * @event loadexception
14266 * Fires if an Exception occurs during data retrieval.
14267 * @param {Object} This DataProxy object.
14268 * @param {Object} o The data object.
14269 * @param {Object} arg The callback argument object passed to the load function.
14270 * @param {Object} e The Exception.
14272 loadexception : true
14274 Roo.data.DataProxy.superclass.constructor.call(this);
14277 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14280 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14284 * Ext JS Library 1.1.1
14285 * Copyright(c) 2006-2007, Ext JS, LLC.
14287 * Originally Released Under LGPL - original licence link has changed is not relivant.
14290 * <script type="text/javascript">
14293 * @class Roo.data.MemoryProxy
14294 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14295 * to the Reader when its load method is called.
14297 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14299 Roo.data.MemoryProxy = function(data){
14303 Roo.data.MemoryProxy.superclass.constructor.call(this);
14307 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14310 * Load data from the requested source (in this case an in-memory
14311 * data object passed to the constructor), read the data object into
14312 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14313 * process that block using the passed callback.
14314 * @param {Object} params This parameter is not used by the MemoryProxy class.
14315 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14316 * object into a block of Roo.data.Records.
14317 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14318 * The function must be passed <ul>
14319 * <li>The Record block object</li>
14320 * <li>The "arg" argument from the load function</li>
14321 * <li>A boolean success indicator</li>
14323 * @param {Object} scope The scope in which to call the callback
14324 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14326 load : function(params, reader, callback, scope, arg){
14327 params = params || {};
14330 result = reader.readRecords(params.data ? params.data :this.data);
14332 this.fireEvent("loadexception", this, arg, null, e);
14333 callback.call(scope, null, arg, false);
14336 callback.call(scope, result, arg, true);
14340 update : function(params, records){
14345 * Ext JS Library 1.1.1
14346 * Copyright(c) 2006-2007, Ext JS, LLC.
14348 * Originally Released Under LGPL - original licence link has changed is not relivant.
14351 * <script type="text/javascript">
14354 * @class Roo.data.HttpProxy
14355 * @extends Roo.data.DataProxy
14356 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14357 * configured to reference a certain URL.<br><br>
14359 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14360 * from which the running page was served.<br><br>
14362 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14364 * Be aware that to enable the browser to parse an XML document, the server must set
14365 * the Content-Type header in the HTTP response to "text/xml".
14367 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14368 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14369 * will be used to make the request.
14371 Roo.data.HttpProxy = function(conn){
14372 Roo.data.HttpProxy.superclass.constructor.call(this);
14373 // is conn a conn config or a real conn?
14375 this.useAjax = !conn || !conn.events;
14379 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14380 // thse are take from connection...
14383 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14386 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14387 * extra parameters to each request made by this object. (defaults to undefined)
14390 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14391 * to each request made by this object. (defaults to undefined)
14394 * @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)
14397 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14400 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14406 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14410 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14411 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14412 * a finer-grained basis than the DataProxy events.
14414 getConnection : function(){
14415 return this.useAjax ? Roo.Ajax : this.conn;
14419 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14420 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14421 * process that block using the passed callback.
14422 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14423 * for the request to the remote server.
14424 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14425 * object into a block of Roo.data.Records.
14426 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14427 * The function must be passed <ul>
14428 * <li>The Record block object</li>
14429 * <li>The "arg" argument from the load function</li>
14430 * <li>A boolean success indicator</li>
14432 * @param {Object} scope The scope in which to call the callback
14433 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14435 load : function(params, reader, callback, scope, arg){
14436 if(this.fireEvent("beforeload", this, params) !== false){
14438 params : params || {},
14440 callback : callback,
14445 callback : this.loadResponse,
14449 Roo.applyIf(o, this.conn);
14450 if(this.activeRequest){
14451 Roo.Ajax.abort(this.activeRequest);
14453 this.activeRequest = Roo.Ajax.request(o);
14455 this.conn.request(o);
14458 callback.call(scope||this, null, arg, false);
14463 loadResponse : function(o, success, response){
14464 delete this.activeRequest;
14466 this.fireEvent("loadexception", this, o, response);
14467 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14472 result = o.reader.read(response);
14474 this.fireEvent("loadexception", this, o, response, e);
14475 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14479 this.fireEvent("load", this, o, o.request.arg);
14480 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14484 update : function(dataSet){
14489 updateResponse : function(dataSet){
14494 * Ext JS Library 1.1.1
14495 * Copyright(c) 2006-2007, Ext JS, LLC.
14497 * Originally Released Under LGPL - original licence link has changed is not relivant.
14500 * <script type="text/javascript">
14504 * @class Roo.data.ScriptTagProxy
14505 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14506 * other than the originating domain of the running page.<br><br>
14508 * <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
14509 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14511 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14512 * source code that is used as the source inside a <script> tag.<br><br>
14514 * In order for the browser to process the returned data, the server must wrap the data object
14515 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14516 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14517 * depending on whether the callback name was passed:
14520 boolean scriptTag = false;
14521 String cb = request.getParameter("callback");
14524 response.setContentType("text/javascript");
14526 response.setContentType("application/x-json");
14528 Writer out = response.getWriter();
14530 out.write(cb + "(");
14532 out.print(dataBlock.toJsonString());
14539 * @param {Object} config A configuration object.
14541 Roo.data.ScriptTagProxy = function(config){
14542 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14543 Roo.apply(this, config);
14544 this.head = document.getElementsByTagName("head")[0];
14547 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14549 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14551 * @cfg {String} url The URL from which to request the data object.
14554 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14558 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14559 * the server the name of the callback function set up by the load call to process the returned data object.
14560 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14561 * javascript output which calls this named function passing the data object as its only parameter.
14563 callbackParam : "callback",
14565 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14566 * name to the request.
14571 * Load data from the configured URL, read the data object into
14572 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14573 * process that block using the passed callback.
14574 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14575 * for the request to the remote server.
14576 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14577 * object into a block of Roo.data.Records.
14578 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14579 * The function must be passed <ul>
14580 * <li>The Record block object</li>
14581 * <li>The "arg" argument from the load function</li>
14582 * <li>A boolean success indicator</li>
14584 * @param {Object} scope The scope in which to call the callback
14585 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14587 load : function(params, reader, callback, scope, arg){
14588 if(this.fireEvent("beforeload", this, params) !== false){
14590 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14592 var url = this.url;
14593 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14595 url += "&_dc=" + (new Date().getTime());
14597 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14600 cb : "stcCallback"+transId,
14601 scriptId : "stcScript"+transId,
14605 callback : callback,
14611 window[trans.cb] = function(o){
14612 conn.handleResponse(o, trans);
14615 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14617 if(this.autoAbort !== false){
14621 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14623 var script = document.createElement("script");
14624 script.setAttribute("src", url);
14625 script.setAttribute("type", "text/javascript");
14626 script.setAttribute("id", trans.scriptId);
14627 this.head.appendChild(script);
14629 this.trans = trans;
14631 callback.call(scope||this, null, arg, false);
14636 isLoading : function(){
14637 return this.trans ? true : false;
14641 * Abort the current server request.
14643 abort : function(){
14644 if(this.isLoading()){
14645 this.destroyTrans(this.trans);
14650 destroyTrans : function(trans, isLoaded){
14651 this.head.removeChild(document.getElementById(trans.scriptId));
14652 clearTimeout(trans.timeoutId);
14654 window[trans.cb] = undefined;
14656 delete window[trans.cb];
14659 // if hasn't been loaded, wait for load to remove it to prevent script error
14660 window[trans.cb] = function(){
14661 window[trans.cb] = undefined;
14663 delete window[trans.cb];
14670 handleResponse : function(o, trans){
14671 this.trans = false;
14672 this.destroyTrans(trans, true);
14675 result = trans.reader.readRecords(o);
14677 this.fireEvent("loadexception", this, o, trans.arg, e);
14678 trans.callback.call(trans.scope||window, null, trans.arg, false);
14681 this.fireEvent("load", this, o, trans.arg);
14682 trans.callback.call(trans.scope||window, result, trans.arg, true);
14686 handleFailure : function(trans){
14687 this.trans = false;
14688 this.destroyTrans(trans, false);
14689 this.fireEvent("loadexception", this, null, trans.arg);
14690 trans.callback.call(trans.scope||window, null, trans.arg, false);
14694 * Ext JS Library 1.1.1
14695 * Copyright(c) 2006-2007, Ext JS, LLC.
14697 * Originally Released Under LGPL - original licence link has changed is not relivant.
14700 * <script type="text/javascript">
14704 * @class Roo.data.JsonReader
14705 * @extends Roo.data.DataReader
14706 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14707 * based on mappings in a provided Roo.data.Record constructor.
14709 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14710 * in the reply previously.
14715 var RecordDef = Roo.data.Record.create([
14716 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14717 {name: 'occupation'} // This field will use "occupation" as the mapping.
14719 var myReader = new Roo.data.JsonReader({
14720 totalProperty: "results", // The property which contains the total dataset size (optional)
14721 root: "rows", // The property which contains an Array of row objects
14722 id: "id" // The property within each row object that provides an ID for the record (optional)
14726 * This would consume a JSON file like this:
14728 { 'results': 2, 'rows': [
14729 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14730 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14733 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14734 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14735 * paged from the remote server.
14736 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14737 * @cfg {String} root name of the property which contains the Array of row objects.
14738 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14739 * @cfg {Array} fields Array of field definition objects
14741 * Create a new JsonReader
14742 * @param {Object} meta Metadata configuration options
14743 * @param {Object} recordType Either an Array of field definition objects,
14744 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14746 Roo.data.JsonReader = function(meta, recordType){
14749 // set some defaults:
14750 Roo.applyIf(meta, {
14751 totalProperty: 'total',
14752 successProperty : 'success',
14757 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14759 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14761 readerType : 'Json',
14764 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14765 * Used by Store query builder to append _requestMeta to params.
14768 metaFromRemote : false,
14770 * This method is only used by a DataProxy which has retrieved data from a remote server.
14771 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14772 * @return {Object} data A data block which is used by an Roo.data.Store object as
14773 * a cache of Roo.data.Records.
14775 read : function(response){
14776 var json = response.responseText;
14778 var o = /* eval:var:o */ eval("("+json+")");
14780 throw {message: "JsonReader.read: Json object not found"};
14786 this.metaFromRemote = true;
14787 this.meta = o.metaData;
14788 this.recordType = Roo.data.Record.create(o.metaData.fields);
14789 this.onMetaChange(this.meta, this.recordType, o);
14791 return this.readRecords(o);
14794 // private function a store will implement
14795 onMetaChange : function(meta, recordType, o){
14802 simpleAccess: function(obj, subsc) {
14809 getJsonAccessor: function(){
14811 return function(expr) {
14813 return(re.test(expr))
14814 ? new Function("obj", "return obj." + expr)
14819 return Roo.emptyFn;
14824 * Create a data block containing Roo.data.Records from an XML document.
14825 * @param {Object} o An object which contains an Array of row objects in the property specified
14826 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14827 * which contains the total size of the dataset.
14828 * @return {Object} data A data block which is used by an Roo.data.Store object as
14829 * a cache of Roo.data.Records.
14831 readRecords : function(o){
14833 * After any data loads, the raw JSON data is available for further custom processing.
14837 var s = this.meta, Record = this.recordType,
14838 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14840 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14842 if(s.totalProperty) {
14843 this.getTotal = this.getJsonAccessor(s.totalProperty);
14845 if(s.successProperty) {
14846 this.getSuccess = this.getJsonAccessor(s.successProperty);
14848 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14850 var g = this.getJsonAccessor(s.id);
14851 this.getId = function(rec) {
14853 return (r === undefined || r === "") ? null : r;
14856 this.getId = function(){return null;};
14859 for(var jj = 0; jj < fl; jj++){
14861 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14862 this.ef[jj] = this.getJsonAccessor(map);
14866 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14867 if(s.totalProperty){
14868 var vt = parseInt(this.getTotal(o), 10);
14873 if(s.successProperty){
14874 var vs = this.getSuccess(o);
14875 if(vs === false || vs === 'false'){
14880 for(var i = 0; i < c; i++){
14883 var id = this.getId(n);
14884 for(var j = 0; j < fl; j++){
14886 var v = this.ef[j](n);
14888 Roo.log('missing convert for ' + f.name);
14892 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14894 var record = new Record(values, id);
14896 records[i] = record;
14902 totalRecords : totalRecords
14905 // used when loading children.. @see loadDataFromChildren
14906 toLoadData: function(rec)
14908 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14909 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14910 return { data : data, total : data.length };
14915 * Ext JS Library 1.1.1
14916 * Copyright(c) 2006-2007, Ext JS, LLC.
14918 * Originally Released Under LGPL - original licence link has changed is not relivant.
14921 * <script type="text/javascript">
14925 * @class Roo.data.ArrayReader
14926 * @extends Roo.data.DataReader
14927 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14928 * Each element of that Array represents a row of data fields. The
14929 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14930 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14934 var RecordDef = Roo.data.Record.create([
14935 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14936 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14938 var myReader = new Roo.data.ArrayReader({
14939 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14943 * This would consume an Array like this:
14945 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14949 * Create a new JsonReader
14950 * @param {Object} meta Metadata configuration options.
14951 * @param {Object|Array} recordType Either an Array of field definition objects
14953 * @cfg {Array} fields Array of field definition objects
14954 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14955 * as specified to {@link Roo.data.Record#create},
14956 * or an {@link Roo.data.Record} object
14959 * created using {@link Roo.data.Record#create}.
14961 Roo.data.ArrayReader = function(meta, recordType)
14963 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14966 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14969 * Create a data block containing Roo.data.Records from an XML document.
14970 * @param {Object} o An Array of row objects which represents the dataset.
14971 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14972 * a cache of Roo.data.Records.
14974 readRecords : function(o)
14976 var sid = this.meta ? this.meta.id : null;
14977 var recordType = this.recordType, fields = recordType.prototype.fields;
14980 for(var i = 0; i < root.length; i++){
14983 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14984 for(var j = 0, jlen = fields.length; j < jlen; j++){
14985 var f = fields.items[j];
14986 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14987 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14989 values[f.name] = v;
14991 var record = new recordType(values, id);
14993 records[records.length] = record;
14997 totalRecords : records.length
15000 // used when loading children.. @see loadDataFromChildren
15001 toLoadData: function(rec)
15003 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15004 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15015 * @class Roo.bootstrap.ComboBox
15016 * @extends Roo.bootstrap.TriggerField
15017 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15018 * @cfg {Boolean} append (true|false) default false
15019 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15020 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15021 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15022 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15023 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15024 * @cfg {Boolean} animate default true
15025 * @cfg {Boolean} emptyResultText only for touch device
15026 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15027 * @cfg {String} emptyTitle default ''
15028 * @cfg {Number} width fixed with? experimental
15030 * Create a new ComboBox.
15031 * @param {Object} config Configuration options
15033 Roo.bootstrap.ComboBox = function(config){
15034 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15038 * Fires when the dropdown list is expanded
15039 * @param {Roo.bootstrap.ComboBox} combo This combo box
15044 * Fires when the dropdown list is collapsed
15045 * @param {Roo.bootstrap.ComboBox} combo This combo box
15049 * @event beforeselect
15050 * Fires before a list item is selected. Return false to cancel the selection.
15051 * @param {Roo.bootstrap.ComboBox} combo This combo box
15052 * @param {Roo.data.Record} record The data record returned from the underlying store
15053 * @param {Number} index The index of the selected item in the dropdown list
15055 'beforeselect' : true,
15058 * Fires when a list item is selected
15059 * @param {Roo.bootstrap.ComboBox} combo This combo box
15060 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15061 * @param {Number} index The index of the selected item in the dropdown list
15065 * @event beforequery
15066 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15067 * The event object passed has these properties:
15068 * @param {Roo.bootstrap.ComboBox} combo This combo box
15069 * @param {String} query The query
15070 * @param {Boolean} forceAll true to force "all" query
15071 * @param {Boolean} cancel true to cancel the query
15072 * @param {Object} e The query event object
15074 'beforequery': true,
15077 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15078 * @param {Roo.bootstrap.ComboBox} combo This combo box
15083 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15084 * @param {Roo.bootstrap.ComboBox} combo This combo box
15085 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15090 * Fires when the remove value from the combobox array
15091 * @param {Roo.bootstrap.ComboBox} combo This combo box
15095 * @event afterremove
15096 * Fires when the remove value from the combobox array
15097 * @param {Roo.bootstrap.ComboBox} combo This combo box
15099 'afterremove' : true,
15101 * @event specialfilter
15102 * Fires when specialfilter
15103 * @param {Roo.bootstrap.ComboBox} combo This combo box
15105 'specialfilter' : true,
15108 * Fires when tick the element
15109 * @param {Roo.bootstrap.ComboBox} combo This combo box
15113 * @event touchviewdisplay
15114 * Fires when touch view require special display (default is using displayField)
15115 * @param {Roo.bootstrap.ComboBox} combo This combo box
15116 * @param {Object} cfg set html .
15118 'touchviewdisplay' : true
15123 this.tickItems = [];
15125 this.selectedIndex = -1;
15126 if(this.mode == 'local'){
15127 if(config.queryDelay === undefined){
15128 this.queryDelay = 10;
15130 if(config.minChars === undefined){
15136 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15139 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15140 * rendering into an Roo.Editor, defaults to false)
15143 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15144 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15147 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15150 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15151 * the dropdown list (defaults to undefined, with no header element)
15155 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15159 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15161 listWidth: undefined,
15163 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15164 * mode = 'remote' or 'text' if mode = 'local')
15166 displayField: undefined,
15169 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15170 * mode = 'remote' or 'value' if mode = 'local').
15171 * Note: use of a valueField requires the user make a selection
15172 * in order for a value to be mapped.
15174 valueField: undefined,
15176 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15181 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15182 * field's data value (defaults to the underlying DOM element's name)
15184 hiddenName: undefined,
15186 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15190 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15192 selectedClass: 'active',
15195 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15199 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15200 * anchor positions (defaults to 'tl-bl')
15202 listAlign: 'tl-bl?',
15204 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15208 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15209 * query specified by the allQuery config option (defaults to 'query')
15211 triggerAction: 'query',
15213 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15214 * (defaults to 4, does not apply if editable = false)
15218 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15219 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15223 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15224 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15228 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15229 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15233 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15234 * when editable = true (defaults to false)
15236 selectOnFocus:false,
15238 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15240 queryParam: 'query',
15242 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15243 * when mode = 'remote' (defaults to 'Loading...')
15245 loadingText: 'Loading...',
15247 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15251 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15255 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15256 * traditional select (defaults to true)
15260 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15264 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15268 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15269 * listWidth has a higher value)
15273 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15274 * allow the user to set arbitrary text into the field (defaults to false)
15276 forceSelection:false,
15278 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15279 * if typeAhead = true (defaults to 250)
15281 typeAheadDelay : 250,
15283 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15284 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15286 valueNotFoundText : undefined,
15288 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15290 blockFocus : false,
15293 * @cfg {Boolean} disableClear Disable showing of clear button.
15295 disableClear : false,
15297 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15299 alwaysQuery : false,
15302 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15307 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15309 invalidClass : "has-warning",
15312 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15314 validClass : "has-success",
15317 * @cfg {Boolean} specialFilter (true|false) special filter default false
15319 specialFilter : false,
15322 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15324 mobileTouchView : true,
15327 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15329 useNativeIOS : false,
15332 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15334 mobile_restrict_height : false,
15336 ios_options : false,
15348 btnPosition : 'right',
15349 triggerList : true,
15350 showToggleBtn : true,
15352 emptyResultText: 'Empty',
15353 triggerText : 'Select',
15357 // element that contains real text value.. (when hidden is used..)
15359 getAutoCreate : function()
15364 * Render classic select for iso
15367 if(Roo.isIOS && this.useNativeIOS){
15368 cfg = this.getAutoCreateNativeIOS();
15376 if(Roo.isTouch && this.mobileTouchView){
15377 cfg = this.getAutoCreateTouchView();
15384 if(!this.tickable){
15385 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15390 * ComboBox with tickable selections
15393 var align = this.labelAlign || this.parentLabelAlign();
15396 cls : 'form-group roo-combobox-tickable' //input-group
15399 var btn_text_select = '';
15400 var btn_text_done = '';
15401 var btn_text_cancel = '';
15403 if (this.btn_text_show) {
15404 btn_text_select = 'Select';
15405 btn_text_done = 'Done';
15406 btn_text_cancel = 'Cancel';
15411 cls : 'tickable-buttons',
15416 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15417 //html : this.triggerText
15418 html: btn_text_select
15424 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15426 html: btn_text_done
15432 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15434 html: btn_text_cancel
15440 buttons.cn.unshift({
15442 cls: 'roo-select2-search-field-input'
15448 Roo.each(buttons.cn, function(c){
15450 c.cls += ' btn-' + _this.size;
15453 if (_this.disabled) {
15460 style : 'display: contents',
15465 cls: 'form-hidden-field'
15469 cls: 'roo-select2-choices',
15473 cls: 'roo-select2-search-field',
15484 cls: 'roo-select2-container input-group roo-select2-container-multi',
15490 // cls: 'typeahead typeahead-long dropdown-menu',
15491 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15496 if(this.hasFeedback && !this.allowBlank){
15500 cls: 'glyphicon form-control-feedback'
15503 combobox.cn.push(feedback);
15510 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15511 tooltip : 'This field is required'
15513 if (Roo.bootstrap.version == 4) {
15516 style : 'display:none'
15519 if (align ==='left' && this.fieldLabel.length) {
15521 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15528 cls : 'control-label col-form-label',
15529 html : this.fieldLabel
15541 var labelCfg = cfg.cn[1];
15542 var contentCfg = cfg.cn[2];
15545 if(this.indicatorpos == 'right'){
15551 cls : 'control-label col-form-label',
15555 html : this.fieldLabel
15571 labelCfg = cfg.cn[0];
15572 contentCfg = cfg.cn[1];
15576 if(this.labelWidth > 12){
15577 labelCfg.style = "width: " + this.labelWidth + 'px';
15579 if(this.width * 1 > 0){
15580 contentCfg.style = "width: " + this.width + 'px';
15582 if(this.labelWidth < 13 && this.labelmd == 0){
15583 this.labelmd = this.labelWidth;
15586 if(this.labellg > 0){
15587 labelCfg.cls += ' col-lg-' + this.labellg;
15588 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15591 if(this.labelmd > 0){
15592 labelCfg.cls += ' col-md-' + this.labelmd;
15593 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15596 if(this.labelsm > 0){
15597 labelCfg.cls += ' col-sm-' + this.labelsm;
15598 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15601 if(this.labelxs > 0){
15602 labelCfg.cls += ' col-xs-' + this.labelxs;
15603 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15607 } else if ( this.fieldLabel.length) {
15608 // Roo.log(" label");
15613 //cls : 'input-group-addon',
15614 html : this.fieldLabel
15619 if(this.indicatorpos == 'right'){
15623 //cls : 'input-group-addon',
15624 html : this.fieldLabel
15634 // Roo.log(" no label && no align");
15641 ['xs','sm','md','lg'].map(function(size){
15642 if (settings[size]) {
15643 cfg.cls += ' col-' + size + '-' + settings[size];
15651 _initEventsCalled : false,
15654 initEvents: function()
15656 if (this._initEventsCalled) { // as we call render... prevent looping...
15659 this._initEventsCalled = true;
15662 throw "can not find store for combo";
15665 this.indicator = this.indicatorEl();
15667 this.store = Roo.factory(this.store, Roo.data);
15668 this.store.parent = this;
15670 // if we are building from html. then this element is so complex, that we can not really
15671 // use the rendered HTML.
15672 // so we have to trash and replace the previous code.
15673 if (Roo.XComponent.build_from_html) {
15674 // remove this element....
15675 var e = this.el.dom, k=0;
15676 while (e ) { e = e.previousSibling; ++k;}
15681 this.rendered = false;
15683 this.render(this.parent().getChildContainer(true), k);
15686 if(Roo.isIOS && this.useNativeIOS){
15687 this.initIOSView();
15695 if(Roo.isTouch && this.mobileTouchView){
15696 this.initTouchView();
15701 this.initTickableEvents();
15705 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15707 if(this.hiddenName){
15709 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15711 this.hiddenField.dom.value =
15712 this.hiddenValue !== undefined ? this.hiddenValue :
15713 this.value !== undefined ? this.value : '';
15715 // prevent input submission
15716 this.el.dom.removeAttribute('name');
15717 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15722 // this.el.dom.setAttribute('autocomplete', 'off');
15725 var cls = 'x-combo-list';
15727 //this.list = new Roo.Layer({
15728 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15734 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15735 _this.list.setWidth(lw);
15738 this.list.on('mouseover', this.onViewOver, this);
15739 this.list.on('mousemove', this.onViewMove, this);
15740 this.list.on('scroll', this.onViewScroll, this);
15743 this.list.swallowEvent('mousewheel');
15744 this.assetHeight = 0;
15747 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15748 this.assetHeight += this.header.getHeight();
15751 this.innerList = this.list.createChild({cls:cls+'-inner'});
15752 this.innerList.on('mouseover', this.onViewOver, this);
15753 this.innerList.on('mousemove', this.onViewMove, this);
15754 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15756 if(this.allowBlank && !this.pageSize && !this.disableClear){
15757 this.footer = this.list.createChild({cls:cls+'-ft'});
15758 this.pageTb = new Roo.Toolbar(this.footer);
15762 this.footer = this.list.createChild({cls:cls+'-ft'});
15763 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15764 {pageSize: this.pageSize});
15768 if (this.pageTb && this.allowBlank && !this.disableClear) {
15770 this.pageTb.add(new Roo.Toolbar.Fill(), {
15771 cls: 'x-btn-icon x-btn-clear',
15773 handler: function()
15776 _this.clearValue();
15777 _this.onSelect(false, -1);
15782 this.assetHeight += this.footer.getHeight();
15787 this.tpl = Roo.bootstrap.version == 4 ?
15788 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15789 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15792 this.view = new Roo.View(this.list, this.tpl, {
15793 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15795 //this.view.wrapEl.setDisplayed(false);
15796 this.view.on('click', this.onViewClick, this);
15799 this.store.on('beforeload', this.onBeforeLoad, this);
15800 this.store.on('load', this.onLoad, this);
15801 this.store.on('loadexception', this.onLoadException, this);
15803 if(this.resizable){
15804 this.resizer = new Roo.Resizable(this.list, {
15805 pinned:true, handles:'se'
15807 this.resizer.on('resize', function(r, w, h){
15808 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15809 this.listWidth = w;
15810 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15811 this.restrictHeight();
15813 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15816 if(!this.editable){
15817 this.editable = true;
15818 this.setEditable(false);
15823 if (typeof(this.events.add.listeners) != 'undefined') {
15825 this.addicon = this.wrap.createChild(
15826 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15828 this.addicon.on('click', function(e) {
15829 this.fireEvent('add', this);
15832 if (typeof(this.events.edit.listeners) != 'undefined') {
15834 this.editicon = this.wrap.createChild(
15835 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15836 if (this.addicon) {
15837 this.editicon.setStyle('margin-left', '40px');
15839 this.editicon.on('click', function(e) {
15841 // we fire even if inothing is selected..
15842 this.fireEvent('edit', this, this.lastData );
15848 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15849 "up" : function(e){
15850 this.inKeyMode = true;
15854 "down" : function(e){
15855 if(!this.isExpanded()){
15856 this.onTriggerClick();
15858 this.inKeyMode = true;
15863 "enter" : function(e){
15864 // this.onViewClick();
15868 if(this.fireEvent("specialkey", this, e)){
15869 this.onViewClick(false);
15875 "esc" : function(e){
15879 "tab" : function(e){
15882 if(this.fireEvent("specialkey", this, e)){
15883 this.onViewClick(false);
15891 doRelay : function(foo, bar, hname){
15892 if(hname == 'down' || this.scope.isExpanded()){
15893 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15902 this.queryDelay = Math.max(this.queryDelay || 10,
15903 this.mode == 'local' ? 10 : 250);
15906 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15908 if(this.typeAhead){
15909 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15911 if(this.editable !== false){
15912 this.inputEl().on("keyup", this.onKeyUp, this);
15914 if(this.forceSelection){
15915 this.inputEl().on('blur', this.doForce, this);
15919 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15920 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15924 initTickableEvents: function()
15928 if(this.hiddenName){
15930 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15932 this.hiddenField.dom.value =
15933 this.hiddenValue !== undefined ? this.hiddenValue :
15934 this.value !== undefined ? this.value : '';
15936 // prevent input submission
15937 this.el.dom.removeAttribute('name');
15938 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15943 // this.list = this.el.select('ul.dropdown-menu',true).first();
15945 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15946 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15947 if(this.triggerList){
15948 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15951 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15952 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15954 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15955 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15957 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15958 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15960 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15961 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15962 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15965 this.cancelBtn.hide();
15970 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15971 _this.list.setWidth(lw);
15974 this.list.on('mouseover', this.onViewOver, this);
15975 this.list.on('mousemove', this.onViewMove, this);
15977 this.list.on('scroll', this.onViewScroll, this);
15980 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15981 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15984 this.view = new Roo.View(this.list, this.tpl, {
15989 selectedClass: this.selectedClass
15992 //this.view.wrapEl.setDisplayed(false);
15993 this.view.on('click', this.onViewClick, this);
15997 this.store.on('beforeload', this.onBeforeLoad, this);
15998 this.store.on('load', this.onLoad, this);
15999 this.store.on('loadexception', this.onLoadException, this);
16002 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16003 "up" : function(e){
16004 this.inKeyMode = true;
16008 "down" : function(e){
16009 this.inKeyMode = true;
16013 "enter" : function(e){
16014 if(this.fireEvent("specialkey", this, e)){
16015 this.onViewClick(false);
16021 "esc" : function(e){
16022 this.onTickableFooterButtonClick(e, false, false);
16025 "tab" : function(e){
16026 this.fireEvent("specialkey", this, e);
16028 this.onTickableFooterButtonClick(e, false, false);
16035 doRelay : function(e, fn, key){
16036 if(this.scope.isExpanded()){
16037 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16046 this.queryDelay = Math.max(this.queryDelay || 10,
16047 this.mode == 'local' ? 10 : 250);
16050 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16052 if(this.typeAhead){
16053 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16056 if(this.editable !== false){
16057 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16060 this.indicator = this.indicatorEl();
16062 if(this.indicator){
16063 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16064 this.indicator.hide();
16069 onDestroy : function(){
16071 this.view.setStore(null);
16072 this.view.el.removeAllListeners();
16073 this.view.el.remove();
16074 this.view.purgeListeners();
16077 this.list.dom.innerHTML = '';
16081 this.store.un('beforeload', this.onBeforeLoad, this);
16082 this.store.un('load', this.onLoad, this);
16083 this.store.un('loadexception', this.onLoadException, this);
16085 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16089 fireKey : function(e){
16090 if(e.isNavKeyPress() && !this.list.isVisible()){
16091 this.fireEvent("specialkey", this, e);
16096 onResize: function(w, h)
16100 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16102 // if(typeof w != 'number'){
16103 // // we do not handle it!?!?
16106 // var tw = this.trigger.getWidth();
16107 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16108 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16110 // this.inputEl().setWidth( this.adjustWidth('input', x));
16112 // //this.trigger.setStyle('left', x+'px');
16114 // if(this.list && this.listWidth === undefined){
16115 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16116 // this.list.setWidth(lw);
16117 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16125 * Allow or prevent the user from directly editing the field text. If false is passed,
16126 * the user will only be able to select from the items defined in the dropdown list. This method
16127 * is the runtime equivalent of setting the 'editable' config option at config time.
16128 * @param {Boolean} value True to allow the user to directly edit the field text
16130 setEditable : function(value){
16131 if(value == this.editable){
16134 this.editable = value;
16136 this.inputEl().dom.setAttribute('readOnly', true);
16137 this.inputEl().on('mousedown', this.onTriggerClick, this);
16138 this.inputEl().addClass('x-combo-noedit');
16140 this.inputEl().dom.setAttribute('readOnly', false);
16141 this.inputEl().un('mousedown', this.onTriggerClick, this);
16142 this.inputEl().removeClass('x-combo-noedit');
16148 onBeforeLoad : function(combo,opts){
16149 if(!this.hasFocus){
16153 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16155 this.restrictHeight();
16156 this.selectedIndex = -1;
16160 onLoad : function(){
16162 this.hasQuery = false;
16164 if(!this.hasFocus){
16168 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16169 this.loading.hide();
16172 if(this.store.getCount() > 0){
16175 this.restrictHeight();
16176 if(this.lastQuery == this.allQuery){
16177 if(this.editable && !this.tickable){
16178 this.inputEl().dom.select();
16182 !this.selectByValue(this.value, true) &&
16185 !this.store.lastOptions ||
16186 typeof(this.store.lastOptions.add) == 'undefined' ||
16187 this.store.lastOptions.add != true
16190 this.select(0, true);
16193 if(this.autoFocus){
16196 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16197 this.taTask.delay(this.typeAheadDelay);
16201 this.onEmptyResults();
16207 onLoadException : function()
16209 this.hasQuery = false;
16211 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16212 this.loading.hide();
16215 if(this.tickable && this.editable){
16220 // only causes errors at present
16221 //Roo.log(this.store.reader.jsonData);
16222 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16224 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16230 onTypeAhead : function(){
16231 if(this.store.getCount() > 0){
16232 var r = this.store.getAt(0);
16233 var newValue = r.data[this.displayField];
16234 var len = newValue.length;
16235 var selStart = this.getRawValue().length;
16237 if(selStart != len){
16238 this.setRawValue(newValue);
16239 this.selectText(selStart, newValue.length);
16245 onSelect : function(record, index){
16247 if(this.fireEvent('beforeselect', this, record, index) !== false){
16249 this.setFromData(index > -1 ? record.data : false);
16252 this.fireEvent('select', this, record, index);
16257 * Returns the currently selected field value or empty string if no value is set.
16258 * @return {String} value The selected value
16260 getValue : function()
16262 if(Roo.isIOS && this.useNativeIOS){
16263 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16267 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16270 if(this.valueField){
16271 return typeof this.value != 'undefined' ? this.value : '';
16273 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16277 getRawValue : function()
16279 if(Roo.isIOS && this.useNativeIOS){
16280 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16283 var v = this.inputEl().getValue();
16289 * Clears any text/value currently set in the field
16291 clearValue : function(){
16293 if(this.hiddenField){
16294 this.hiddenField.dom.value = '';
16297 this.setRawValue('');
16298 this.lastSelectionText = '';
16299 this.lastData = false;
16301 var close = this.closeTriggerEl();
16312 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16313 * will be displayed in the field. If the value does not match the data value of an existing item,
16314 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16315 * Otherwise the field will be blank (although the value will still be set).
16316 * @param {String} value The value to match
16318 setValue : function(v)
16320 if(Roo.isIOS && this.useNativeIOS){
16321 this.setIOSValue(v);
16331 if(this.valueField){
16332 var r = this.findRecord(this.valueField, v);
16334 text = r.data[this.displayField];
16335 }else if(this.valueNotFoundText !== undefined){
16336 text = this.valueNotFoundText;
16339 this.lastSelectionText = text;
16340 if(this.hiddenField){
16341 this.hiddenField.dom.value = v;
16343 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16346 var close = this.closeTriggerEl();
16349 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16355 * @property {Object} the last set data for the element
16360 * Sets the value of the field based on a object which is related to the record format for the store.
16361 * @param {Object} value the value to set as. or false on reset?
16363 setFromData : function(o){
16370 var dv = ''; // display value
16371 var vv = ''; // value value..
16373 if (this.displayField) {
16374 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16376 // this is an error condition!!!
16377 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16380 if(this.valueField){
16381 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16384 var close = this.closeTriggerEl();
16387 if(dv.length || vv * 1 > 0){
16389 this.blockFocus=true;
16395 if(this.hiddenField){
16396 this.hiddenField.dom.value = vv;
16398 this.lastSelectionText = dv;
16399 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16403 // no hidden field.. - we store the value in 'value', but still display
16404 // display field!!!!
16405 this.lastSelectionText = dv;
16406 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16413 reset : function(){
16414 // overridden so that last data is reset..
16421 this.setValue(this.originalValue);
16422 //this.clearInvalid();
16423 this.lastData = false;
16425 this.view.clearSelections();
16431 findRecord : function(prop, value){
16433 if(this.store.getCount() > 0){
16434 this.store.each(function(r){
16435 if(r.data[prop] == value){
16445 getName: function()
16447 // returns hidden if it's set..
16448 if (!this.rendered) {return ''};
16449 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16453 onViewMove : function(e, t){
16454 this.inKeyMode = false;
16458 onViewOver : function(e, t){
16459 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16462 var item = this.view.findItemFromChild(t);
16465 var index = this.view.indexOf(item);
16466 this.select(index, false);
16471 onViewClick : function(view, doFocus, el, e)
16473 var index = this.view.getSelectedIndexes()[0];
16475 var r = this.store.getAt(index);
16479 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16486 Roo.each(this.tickItems, function(v,k){
16488 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16490 _this.tickItems.splice(k, 1);
16492 if(typeof(e) == 'undefined' && view == false){
16493 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16505 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16506 this.tickItems.push(r.data);
16509 if(typeof(e) == 'undefined' && view == false){
16510 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16517 this.onSelect(r, index);
16519 if(doFocus !== false && !this.blockFocus){
16520 this.inputEl().focus();
16525 restrictHeight : function(){
16526 //this.innerList.dom.style.height = '';
16527 //var inner = this.innerList.dom;
16528 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16529 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16530 //this.list.beginUpdate();
16531 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16532 this.list.alignTo(this.inputEl(), this.listAlign);
16533 this.list.alignTo(this.inputEl(), this.listAlign);
16534 //this.list.endUpdate();
16538 onEmptyResults : function(){
16540 if(this.tickable && this.editable){
16541 this.hasFocus = false;
16542 this.restrictHeight();
16550 * Returns true if the dropdown list is expanded, else false.
16552 isExpanded : function(){
16553 return this.list.isVisible();
16557 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16558 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16559 * @param {String} value The data value of the item to select
16560 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16561 * selected item if it is not currently in view (defaults to true)
16562 * @return {Boolean} True if the value matched an item in the list, else false
16564 selectByValue : function(v, scrollIntoView){
16565 if(v !== undefined && v !== null){
16566 var r = this.findRecord(this.valueField || this.displayField, v);
16568 this.select(this.store.indexOf(r), scrollIntoView);
16576 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16577 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16578 * @param {Number} index The zero-based index of the list item to select
16579 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16580 * selected item if it is not currently in view (defaults to true)
16582 select : function(index, scrollIntoView){
16583 this.selectedIndex = index;
16584 this.view.select(index);
16585 if(scrollIntoView !== false){
16586 var el = this.view.getNode(index);
16588 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16591 this.list.scrollChildIntoView(el, false);
16597 selectNext : function(){
16598 var ct = this.store.getCount();
16600 if(this.selectedIndex == -1){
16602 }else if(this.selectedIndex < ct-1){
16603 this.select(this.selectedIndex+1);
16609 selectPrev : function(){
16610 var ct = this.store.getCount();
16612 if(this.selectedIndex == -1){
16614 }else if(this.selectedIndex != 0){
16615 this.select(this.selectedIndex-1);
16621 onKeyUp : function(e){
16622 if(this.editable !== false && !e.isSpecialKey()){
16623 this.lastKey = e.getKey();
16624 this.dqTask.delay(this.queryDelay);
16629 validateBlur : function(){
16630 return !this.list || !this.list.isVisible();
16634 initQuery : function(){
16636 var v = this.getRawValue();
16638 if(this.tickable && this.editable){
16639 v = this.tickableInputEl().getValue();
16646 doForce : function(){
16647 if(this.inputEl().dom.value.length > 0){
16648 this.inputEl().dom.value =
16649 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16655 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16656 * query allowing the query action to be canceled if needed.
16657 * @param {String} query The SQL query to execute
16658 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16659 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16660 * saved in the current store (defaults to false)
16662 doQuery : function(q, forceAll){
16664 if(q === undefined || q === null){
16669 forceAll: forceAll,
16673 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16678 forceAll = qe.forceAll;
16679 if(forceAll === true || (q.length >= this.minChars)){
16681 this.hasQuery = true;
16683 if(this.lastQuery != q || this.alwaysQuery){
16684 this.lastQuery = q;
16685 if(this.mode == 'local'){
16686 this.selectedIndex = -1;
16688 this.store.clearFilter();
16691 if(this.specialFilter){
16692 this.fireEvent('specialfilter', this);
16697 this.store.filter(this.displayField, q);
16700 this.store.fireEvent("datachanged", this.store);
16707 this.store.baseParams[this.queryParam] = q;
16709 var options = {params : this.getParams(q)};
16712 options.add = true;
16713 options.params.start = this.page * this.pageSize;
16716 this.store.load(options);
16719 * this code will make the page width larger, at the beginning, the list not align correctly,
16720 * we should expand the list on onLoad
16721 * so command out it
16726 this.selectedIndex = -1;
16731 this.loadNext = false;
16735 getParams : function(q){
16737 //p[this.queryParam] = q;
16741 p.limit = this.pageSize;
16747 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16749 collapse : function(){
16750 if(!this.isExpanded()){
16756 this.hasFocus = false;
16760 this.cancelBtn.hide();
16761 this.trigger.show();
16764 this.tickableInputEl().dom.value = '';
16765 this.tickableInputEl().blur();
16770 Roo.get(document).un('mousedown', this.collapseIf, this);
16771 Roo.get(document).un('mousewheel', this.collapseIf, this);
16772 if (!this.editable) {
16773 Roo.get(document).un('keydown', this.listKeyPress, this);
16775 this.fireEvent('collapse', this);
16781 collapseIf : function(e){
16782 var in_combo = e.within(this.el);
16783 var in_list = e.within(this.list);
16784 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16786 if (in_combo || in_list || is_list) {
16787 //e.stopPropagation();
16792 this.onTickableFooterButtonClick(e, false, false);
16800 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16802 expand : function(){
16804 if(this.isExpanded() || !this.hasFocus){
16808 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16809 this.list.setWidth(lw);
16815 this.restrictHeight();
16819 this.tickItems = Roo.apply([], this.item);
16822 this.cancelBtn.show();
16823 this.trigger.hide();
16826 this.tickableInputEl().focus();
16831 Roo.get(document).on('mousedown', this.collapseIf, this);
16832 Roo.get(document).on('mousewheel', this.collapseIf, this);
16833 if (!this.editable) {
16834 Roo.get(document).on('keydown', this.listKeyPress, this);
16837 this.fireEvent('expand', this);
16841 // Implements the default empty TriggerField.onTriggerClick function
16842 onTriggerClick : function(e)
16844 Roo.log('trigger click');
16846 if(this.disabled || !this.triggerList){
16851 this.loadNext = false;
16853 if(this.isExpanded()){
16855 if (!this.blockFocus) {
16856 this.inputEl().focus();
16860 this.hasFocus = true;
16861 if(this.triggerAction == 'all') {
16862 this.doQuery(this.allQuery, true);
16864 this.doQuery(this.getRawValue());
16866 if (!this.blockFocus) {
16867 this.inputEl().focus();
16872 onTickableTriggerClick : function(e)
16879 this.loadNext = false;
16880 this.hasFocus = true;
16882 if(this.triggerAction == 'all') {
16883 this.doQuery(this.allQuery, true);
16885 this.doQuery(this.getRawValue());
16889 onSearchFieldClick : function(e)
16891 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16892 this.onTickableFooterButtonClick(e, false, false);
16896 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16901 this.loadNext = false;
16902 this.hasFocus = true;
16904 if(this.triggerAction == 'all') {
16905 this.doQuery(this.allQuery, true);
16907 this.doQuery(this.getRawValue());
16911 listKeyPress : function(e)
16913 //Roo.log('listkeypress');
16914 // scroll to first matching element based on key pres..
16915 if (e.isSpecialKey()) {
16918 var k = String.fromCharCode(e.getKey()).toUpperCase();
16921 var csel = this.view.getSelectedNodes();
16922 var cselitem = false;
16924 var ix = this.view.indexOf(csel[0]);
16925 cselitem = this.store.getAt(ix);
16926 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16932 this.store.each(function(v) {
16934 // start at existing selection.
16935 if (cselitem.id == v.id) {
16941 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16942 match = this.store.indexOf(v);
16948 if (match === false) {
16949 return true; // no more action?
16952 this.view.select(match);
16953 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16954 sn.scrollIntoView(sn.dom.parentNode, false);
16957 onViewScroll : function(e, t){
16959 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){
16963 this.hasQuery = true;
16965 this.loading = this.list.select('.loading', true).first();
16967 if(this.loading === null){
16968 this.list.createChild({
16970 cls: 'loading roo-select2-more-results roo-select2-active',
16971 html: 'Loading more results...'
16974 this.loading = this.list.select('.loading', true).first();
16976 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16978 this.loading.hide();
16981 this.loading.show();
16986 this.loadNext = true;
16988 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16993 addItem : function(o)
16995 var dv = ''; // display value
16997 if (this.displayField) {
16998 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17000 // this is an error condition!!!
17001 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17008 var choice = this.choices.createChild({
17010 cls: 'roo-select2-search-choice',
17019 cls: 'roo-select2-search-choice-close fa fa-times',
17024 }, this.searchField);
17026 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17028 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17036 this.inputEl().dom.value = '';
17041 onRemoveItem : function(e, _self, o)
17043 e.preventDefault();
17045 this.lastItem = Roo.apply([], this.item);
17047 var index = this.item.indexOf(o.data) * 1;
17050 Roo.log('not this item?!');
17054 this.item.splice(index, 1);
17059 this.fireEvent('remove', this, e);
17065 syncValue : function()
17067 if(!this.item.length){
17074 Roo.each(this.item, function(i){
17075 if(_this.valueField){
17076 value.push(i[_this.valueField]);
17083 this.value = value.join(',');
17085 if(this.hiddenField){
17086 this.hiddenField.dom.value = this.value;
17089 this.store.fireEvent("datachanged", this.store);
17094 clearItem : function()
17096 if(!this.multiple){
17102 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17110 if(this.tickable && !Roo.isTouch){
17111 this.view.refresh();
17115 inputEl: function ()
17117 if(Roo.isIOS && this.useNativeIOS){
17118 return this.el.select('select.roo-ios-select', true).first();
17121 if(Roo.isTouch && this.mobileTouchView){
17122 return this.el.select('input.form-control',true).first();
17126 return this.searchField;
17129 return this.el.select('input.form-control',true).first();
17132 onTickableFooterButtonClick : function(e, btn, el)
17134 e.preventDefault();
17136 this.lastItem = Roo.apply([], this.item);
17138 if(btn && btn.name == 'cancel'){
17139 this.tickItems = Roo.apply([], this.item);
17148 Roo.each(this.tickItems, function(o){
17156 validate : function()
17158 if(this.getVisibilityEl().hasClass('hidden')){
17162 var v = this.getRawValue();
17165 v = this.getValue();
17168 if(this.disabled || this.allowBlank || v.length){
17173 this.markInvalid();
17177 tickableInputEl : function()
17179 if(!this.tickable || !this.editable){
17180 return this.inputEl();
17183 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17187 getAutoCreateTouchView : function()
17192 cls: 'form-group' //input-group
17198 type : this.inputType,
17199 cls : 'form-control x-combo-noedit',
17200 autocomplete: 'new-password',
17201 placeholder : this.placeholder || '',
17206 input.name = this.name;
17210 input.cls += ' input-' + this.size;
17213 if (this.disabled) {
17214 input.disabled = true;
17218 cls : 'roo-combobox-wrap',
17225 inputblock.cls += ' input-group';
17227 inputblock.cn.unshift({
17229 cls : 'input-group-addon input-group-prepend input-group-text',
17234 if(this.removable && !this.multiple){
17235 inputblock.cls += ' roo-removable';
17237 inputblock.cn.push({
17240 cls : 'roo-combo-removable-btn close'
17244 if(this.hasFeedback && !this.allowBlank){
17246 inputblock.cls += ' has-feedback';
17248 inputblock.cn.push({
17250 cls: 'glyphicon form-control-feedback'
17257 inputblock.cls += (this.before) ? '' : ' input-group';
17259 inputblock.cn.push({
17261 cls : 'input-group-addon input-group-append input-group-text',
17267 var ibwrap = inputblock;
17272 cls: 'roo-select2-choices',
17276 cls: 'roo-select2-search-field',
17289 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17294 cls: 'form-hidden-field'
17300 if(!this.multiple && this.showToggleBtn){
17306 if (this.caret != false) {
17309 cls: 'fa fa-' + this.caret
17316 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17318 Roo.bootstrap.version == 3 ? caret : '',
17321 cls: 'combobox-clear',
17335 combobox.cls += ' roo-select2-container-multi';
17338 var align = this.labelAlign || this.parentLabelAlign();
17340 if (align ==='left' && this.fieldLabel.length) {
17345 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17346 tooltip : 'This field is required'
17350 cls : 'control-label col-form-label',
17351 html : this.fieldLabel
17355 cls : 'roo-combobox-wrap ',
17362 var labelCfg = cfg.cn[1];
17363 var contentCfg = cfg.cn[2];
17366 if(this.indicatorpos == 'right'){
17371 cls : 'control-label col-form-label',
17375 html : this.fieldLabel
17379 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17380 tooltip : 'This field is required'
17385 cls : "roo-combobox-wrap ",
17393 labelCfg = cfg.cn[0];
17394 contentCfg = cfg.cn[1];
17399 if(this.labelWidth > 12){
17400 labelCfg.style = "width: " + this.labelWidth + 'px';
17403 if(this.labelWidth < 13 && this.labelmd == 0){
17404 this.labelmd = this.labelWidth;
17407 if(this.labellg > 0){
17408 labelCfg.cls += ' col-lg-' + this.labellg;
17409 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17412 if(this.labelmd > 0){
17413 labelCfg.cls += ' col-md-' + this.labelmd;
17414 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17417 if(this.labelsm > 0){
17418 labelCfg.cls += ' col-sm-' + this.labelsm;
17419 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17422 if(this.labelxs > 0){
17423 labelCfg.cls += ' col-xs-' + this.labelxs;
17424 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17428 } else if ( this.fieldLabel.length) {
17432 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17433 tooltip : 'This field is required'
17437 cls : 'control-label',
17438 html : this.fieldLabel
17449 if(this.indicatorpos == 'right'){
17453 cls : 'control-label',
17454 html : this.fieldLabel,
17458 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17459 tooltip : 'This field is required'
17476 var settings = this;
17478 ['xs','sm','md','lg'].map(function(size){
17479 if (settings[size]) {
17480 cfg.cls += ' col-' + size + '-' + settings[size];
17487 initTouchView : function()
17489 this.renderTouchView();
17491 this.touchViewEl.on('scroll', function(){
17492 this.el.dom.scrollTop = 0;
17495 this.originalValue = this.getValue();
17497 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17499 this.inputEl().on("click", this.showTouchView, this);
17500 if (this.triggerEl) {
17501 this.triggerEl.on("click", this.showTouchView, this);
17505 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17506 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17508 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17510 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17511 this.store.on('load', this.onTouchViewLoad, this);
17512 this.store.on('loadexception', this.onTouchViewLoadException, this);
17514 if(this.hiddenName){
17516 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17518 this.hiddenField.dom.value =
17519 this.hiddenValue !== undefined ? this.hiddenValue :
17520 this.value !== undefined ? this.value : '';
17522 this.el.dom.removeAttribute('name');
17523 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17527 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17528 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17531 if(this.removable && !this.multiple){
17532 var close = this.closeTriggerEl();
17534 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17535 close.on('click', this.removeBtnClick, this, close);
17539 * fix the bug in Safari iOS8
17541 this.inputEl().on("focus", function(e){
17542 document.activeElement.blur();
17545 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17552 renderTouchView : function()
17554 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17555 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17557 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17558 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17560 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17561 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17562 this.touchViewBodyEl.setStyle('overflow', 'auto');
17564 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17565 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17567 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17568 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17572 showTouchView : function()
17578 this.touchViewHeaderEl.hide();
17580 if(this.modalTitle.length){
17581 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17582 this.touchViewHeaderEl.show();
17585 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17586 this.touchViewEl.show();
17588 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17590 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17591 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17593 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17595 if(this.modalTitle.length){
17596 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17599 this.touchViewBodyEl.setHeight(bodyHeight);
17603 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17605 this.touchViewEl.addClass(['in','show']);
17608 if(this._touchViewMask){
17609 Roo.get(document.body).addClass("x-body-masked");
17610 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17611 this._touchViewMask.setStyle('z-index', 10000);
17612 this._touchViewMask.addClass('show');
17615 this.doTouchViewQuery();
17619 hideTouchView : function()
17621 this.touchViewEl.removeClass(['in','show']);
17625 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17627 this.touchViewEl.setStyle('display', 'none');
17630 if(this._touchViewMask){
17631 this._touchViewMask.removeClass('show');
17632 Roo.get(document.body).removeClass("x-body-masked");
17636 setTouchViewValue : function()
17643 Roo.each(this.tickItems, function(o){
17648 this.hideTouchView();
17651 doTouchViewQuery : function()
17660 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17664 if(!this.alwaysQuery || this.mode == 'local'){
17665 this.onTouchViewLoad();
17672 onTouchViewBeforeLoad : function(combo,opts)
17678 onTouchViewLoad : function()
17680 if(this.store.getCount() < 1){
17681 this.onTouchViewEmptyResults();
17685 this.clearTouchView();
17687 var rawValue = this.getRawValue();
17689 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17691 this.tickItems = [];
17693 this.store.data.each(function(d, rowIndex){
17694 var row = this.touchViewListGroup.createChild(template);
17696 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17697 row.addClass(d.data.cls);
17700 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17703 html : d.data[this.displayField]
17706 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17707 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17710 row.removeClass('selected');
17711 if(!this.multiple && this.valueField &&
17712 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17715 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17716 row.addClass('selected');
17719 if(this.multiple && this.valueField &&
17720 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17724 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17725 this.tickItems.push(d.data);
17728 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17732 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17734 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17736 if(this.modalTitle.length){
17737 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17740 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17742 if(this.mobile_restrict_height && listHeight < bodyHeight){
17743 this.touchViewBodyEl.setHeight(listHeight);
17748 if(firstChecked && listHeight > bodyHeight){
17749 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17754 onTouchViewLoadException : function()
17756 this.hideTouchView();
17759 onTouchViewEmptyResults : function()
17761 this.clearTouchView();
17763 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17765 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17769 clearTouchView : function()
17771 this.touchViewListGroup.dom.innerHTML = '';
17774 onTouchViewClick : function(e, el, o)
17776 e.preventDefault();
17779 var rowIndex = o.rowIndex;
17781 var r = this.store.getAt(rowIndex);
17783 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17785 if(!this.multiple){
17786 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17787 c.dom.removeAttribute('checked');
17790 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17792 this.setFromData(r.data);
17794 var close = this.closeTriggerEl();
17800 this.hideTouchView();
17802 this.fireEvent('select', this, r, rowIndex);
17807 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17808 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17809 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17813 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17814 this.addItem(r.data);
17815 this.tickItems.push(r.data);
17819 getAutoCreateNativeIOS : function()
17822 cls: 'form-group' //input-group,
17827 cls : 'roo-ios-select'
17831 combobox.name = this.name;
17834 if (this.disabled) {
17835 combobox.disabled = true;
17838 var settings = this;
17840 ['xs','sm','md','lg'].map(function(size){
17841 if (settings[size]) {
17842 cfg.cls += ' col-' + size + '-' + settings[size];
17852 initIOSView : function()
17854 this.store.on('load', this.onIOSViewLoad, this);
17859 onIOSViewLoad : function()
17861 if(this.store.getCount() < 1){
17865 this.clearIOSView();
17867 if(this.allowBlank) {
17869 var default_text = '-- SELECT --';
17871 if(this.placeholder.length){
17872 default_text = this.placeholder;
17875 if(this.emptyTitle.length){
17876 default_text += ' - ' + this.emptyTitle + ' -';
17879 var opt = this.inputEl().createChild({
17882 html : default_text
17886 o[this.valueField] = 0;
17887 o[this.displayField] = default_text;
17889 this.ios_options.push({
17896 this.store.data.each(function(d, rowIndex){
17900 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17901 html = d.data[this.displayField];
17906 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17907 value = d.data[this.valueField];
17916 if(this.value == d.data[this.valueField]){
17917 option['selected'] = true;
17920 var opt = this.inputEl().createChild(option);
17922 this.ios_options.push({
17929 this.inputEl().on('change', function(){
17930 this.fireEvent('select', this);
17935 clearIOSView: function()
17937 this.inputEl().dom.innerHTML = '';
17939 this.ios_options = [];
17942 setIOSValue: function(v)
17946 if(!this.ios_options){
17950 Roo.each(this.ios_options, function(opts){
17952 opts.el.dom.removeAttribute('selected');
17954 if(opts.data[this.valueField] != v){
17958 opts.el.dom.setAttribute('selected', true);
17964 * @cfg {Boolean} grow
17968 * @cfg {Number} growMin
17972 * @cfg {Number} growMax
17981 Roo.apply(Roo.bootstrap.ComboBox, {
17985 cls: 'modal-header',
18007 cls: 'list-group-item',
18011 cls: 'roo-combobox-list-group-item-value'
18015 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18029 listItemCheckbox : {
18031 cls: 'list-group-item',
18035 cls: 'roo-combobox-list-group-item-value'
18039 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18055 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18060 cls: 'modal-footer',
18068 cls: 'col-xs-6 text-left',
18071 cls: 'btn btn-danger roo-touch-view-cancel',
18077 cls: 'col-xs-6 text-right',
18080 cls: 'btn btn-success roo-touch-view-ok',
18091 Roo.apply(Roo.bootstrap.ComboBox, {
18093 touchViewTemplate : {
18095 cls: 'modal fade roo-combobox-touch-view',
18099 cls: 'modal-dialog',
18100 style : 'position:fixed', // we have to fix position....
18104 cls: 'modal-content',
18106 Roo.bootstrap.ComboBox.header,
18107 Roo.bootstrap.ComboBox.body,
18108 Roo.bootstrap.ComboBox.footer
18117 * Ext JS Library 1.1.1
18118 * Copyright(c) 2006-2007, Ext JS, LLC.
18120 * Originally Released Under LGPL - original licence link has changed is not relivant.
18123 * <script type="text/javascript">
18128 * @extends Roo.util.Observable
18129 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18130 * This class also supports single and multi selection modes. <br>
18131 * Create a data model bound view:
18133 var store = new Roo.data.Store(...);
18135 var view = new Roo.View({
18137 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18139 singleSelect: true,
18140 selectedClass: "ydataview-selected",
18144 // listen for node click?
18145 view.on("click", function(vw, index, node, e){
18146 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18150 dataModel.load("foobar.xml");
18152 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18154 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18155 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18157 * Note: old style constructor is still suported (container, template, config)
18160 * Create a new View
18161 * @param {Object} config The config object
18164 Roo.View = function(config, depreciated_tpl, depreciated_config){
18166 this.parent = false;
18168 if (typeof(depreciated_tpl) == 'undefined') {
18169 // new way.. - universal constructor.
18170 Roo.apply(this, config);
18171 this.el = Roo.get(this.el);
18174 this.el = Roo.get(config);
18175 this.tpl = depreciated_tpl;
18176 Roo.apply(this, depreciated_config);
18178 this.wrapEl = this.el.wrap().wrap();
18179 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18182 if(typeof(this.tpl) == "string"){
18183 this.tpl = new Roo.Template(this.tpl);
18185 // support xtype ctors..
18186 this.tpl = new Roo.factory(this.tpl, Roo);
18190 this.tpl.compile();
18195 * @event beforeclick
18196 * Fires before a click is processed. Returns false to cancel the default action.
18197 * @param {Roo.View} this
18198 * @param {Number} index The index of the target node
18199 * @param {HTMLElement} node The target node
18200 * @param {Roo.EventObject} e The raw event object
18202 "beforeclick" : true,
18205 * Fires when a template node is clicked.
18206 * @param {Roo.View} this
18207 * @param {Number} index The index of the target node
18208 * @param {HTMLElement} node The target node
18209 * @param {Roo.EventObject} e The raw event object
18214 * Fires when a template node is double clicked.
18215 * @param {Roo.View} this
18216 * @param {Number} index The index of the target node
18217 * @param {HTMLElement} node The target node
18218 * @param {Roo.EventObject} e The raw event object
18222 * @event contextmenu
18223 * Fires when a template node is right clicked.
18224 * @param {Roo.View} this
18225 * @param {Number} index The index of the target node
18226 * @param {HTMLElement} node The target node
18227 * @param {Roo.EventObject} e The raw event object
18229 "contextmenu" : true,
18231 * @event selectionchange
18232 * Fires when the selected nodes change.
18233 * @param {Roo.View} this
18234 * @param {Array} selections Array of the selected nodes
18236 "selectionchange" : true,
18239 * @event beforeselect
18240 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18241 * @param {Roo.View} this
18242 * @param {HTMLElement} node The node to be selected
18243 * @param {Array} selections Array of currently selected nodes
18245 "beforeselect" : true,
18247 * @event preparedata
18248 * Fires on every row to render, to allow you to change the data.
18249 * @param {Roo.View} this
18250 * @param {Object} data to be rendered (change this)
18252 "preparedata" : true
18260 "click": this.onClick,
18261 "dblclick": this.onDblClick,
18262 "contextmenu": this.onContextMenu,
18266 this.selections = [];
18268 this.cmp = new Roo.CompositeElementLite([]);
18270 this.store = Roo.factory(this.store, Roo.data);
18271 this.setStore(this.store, true);
18274 if ( this.footer && this.footer.xtype) {
18276 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18278 this.footer.dataSource = this.store;
18279 this.footer.container = fctr;
18280 this.footer = Roo.factory(this.footer, Roo);
18281 fctr.insertFirst(this.el);
18283 // this is a bit insane - as the paging toolbar seems to detach the el..
18284 // dom.parentNode.parentNode.parentNode
18285 // they get detached?
18289 Roo.View.superclass.constructor.call(this);
18294 Roo.extend(Roo.View, Roo.util.Observable, {
18297 * @cfg {Roo.data.Store} store Data store to load data from.
18302 * @cfg {String|Roo.Element} el The container element.
18307 * @cfg {String|Roo.Template} tpl The template used by this View
18311 * @cfg {String} dataName the named area of the template to use as the data area
18312 * Works with domtemplates roo-name="name"
18316 * @cfg {String} selectedClass The css class to add to selected nodes
18318 selectedClass : "x-view-selected",
18320 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18325 * @cfg {String} text to display on mask (default Loading)
18329 * @cfg {Boolean} multiSelect Allow multiple selection
18331 multiSelect : false,
18333 * @cfg {Boolean} singleSelect Allow single selection
18335 singleSelect: false,
18338 * @cfg {Boolean} toggleSelect - selecting
18340 toggleSelect : false,
18343 * @cfg {Boolean} tickable - selecting
18348 * Returns the element this view is bound to.
18349 * @return {Roo.Element}
18351 getEl : function(){
18352 return this.wrapEl;
18358 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18360 refresh : function(){
18361 //Roo.log('refresh');
18364 // if we are using something like 'domtemplate', then
18365 // the what gets used is:
18366 // t.applySubtemplate(NAME, data, wrapping data..)
18367 // the outer template then get' applied with
18368 // the store 'extra data'
18369 // and the body get's added to the
18370 // roo-name="data" node?
18371 // <span class='roo-tpl-{name}'></span> ?????
18375 this.clearSelections();
18376 this.el.update("");
18378 var records = this.store.getRange();
18379 if(records.length < 1) {
18381 // is this valid?? = should it render a template??
18383 this.el.update(this.emptyText);
18387 if (this.dataName) {
18388 this.el.update(t.apply(this.store.meta)); //????
18389 el = this.el.child('.roo-tpl-' + this.dataName);
18392 for(var i = 0, len = records.length; i < len; i++){
18393 var data = this.prepareData(records[i].data, i, records[i]);
18394 this.fireEvent("preparedata", this, data, i, records[i]);
18396 var d = Roo.apply({}, data);
18399 Roo.apply(d, {'roo-id' : Roo.id()});
18403 Roo.each(this.parent.item, function(item){
18404 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18407 Roo.apply(d, {'roo-data-checked' : 'checked'});
18411 html[html.length] = Roo.util.Format.trim(
18413 t.applySubtemplate(this.dataName, d, this.store.meta) :
18420 el.update(html.join(""));
18421 this.nodes = el.dom.childNodes;
18422 this.updateIndexes(0);
18427 * Function to override to reformat the data that is sent to
18428 * the template for each node.
18429 * DEPRICATED - use the preparedata event handler.
18430 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18431 * a JSON object for an UpdateManager bound view).
18433 prepareData : function(data, index, record)
18435 this.fireEvent("preparedata", this, data, index, record);
18439 onUpdate : function(ds, record){
18440 // Roo.log('on update');
18441 this.clearSelections();
18442 var index = this.store.indexOf(record);
18443 var n = this.nodes[index];
18444 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18445 n.parentNode.removeChild(n);
18446 this.updateIndexes(index, index);
18452 onAdd : function(ds, records, index)
18454 //Roo.log(['on Add', ds, records, index] );
18455 this.clearSelections();
18456 if(this.nodes.length == 0){
18460 var n = this.nodes[index];
18461 for(var i = 0, len = records.length; i < len; i++){
18462 var d = this.prepareData(records[i].data, i, records[i]);
18464 this.tpl.insertBefore(n, d);
18467 this.tpl.append(this.el, d);
18470 this.updateIndexes(index);
18473 onRemove : function(ds, record, index){
18474 // Roo.log('onRemove');
18475 this.clearSelections();
18476 var el = this.dataName ?
18477 this.el.child('.roo-tpl-' + this.dataName) :
18480 el.dom.removeChild(this.nodes[index]);
18481 this.updateIndexes(index);
18485 * Refresh an individual node.
18486 * @param {Number} index
18488 refreshNode : function(index){
18489 this.onUpdate(this.store, this.store.getAt(index));
18492 updateIndexes : function(startIndex, endIndex){
18493 var ns = this.nodes;
18494 startIndex = startIndex || 0;
18495 endIndex = endIndex || ns.length - 1;
18496 for(var i = startIndex; i <= endIndex; i++){
18497 ns[i].nodeIndex = i;
18502 * Changes the data store this view uses and refresh the view.
18503 * @param {Store} store
18505 setStore : function(store, initial){
18506 if(!initial && this.store){
18507 this.store.un("datachanged", this.refresh);
18508 this.store.un("add", this.onAdd);
18509 this.store.un("remove", this.onRemove);
18510 this.store.un("update", this.onUpdate);
18511 this.store.un("clear", this.refresh);
18512 this.store.un("beforeload", this.onBeforeLoad);
18513 this.store.un("load", this.onLoad);
18514 this.store.un("loadexception", this.onLoad);
18518 store.on("datachanged", this.refresh, this);
18519 store.on("add", this.onAdd, this);
18520 store.on("remove", this.onRemove, this);
18521 store.on("update", this.onUpdate, this);
18522 store.on("clear", this.refresh, this);
18523 store.on("beforeload", this.onBeforeLoad, this);
18524 store.on("load", this.onLoad, this);
18525 store.on("loadexception", this.onLoad, this);
18533 * onbeforeLoad - masks the loading area.
18536 onBeforeLoad : function(store,opts)
18538 //Roo.log('onBeforeLoad');
18540 this.el.update("");
18542 this.el.mask(this.mask ? this.mask : "Loading" );
18544 onLoad : function ()
18551 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18552 * @param {HTMLElement} node
18553 * @return {HTMLElement} The template node
18555 findItemFromChild : function(node){
18556 var el = this.dataName ?
18557 this.el.child('.roo-tpl-' + this.dataName,true) :
18560 if(!node || node.parentNode == el){
18563 var p = node.parentNode;
18564 while(p && p != el){
18565 if(p.parentNode == el){
18574 onClick : function(e){
18575 var item = this.findItemFromChild(e.getTarget());
18577 var index = this.indexOf(item);
18578 if(this.onItemClick(item, index, e) !== false){
18579 this.fireEvent("click", this, index, item, e);
18582 this.clearSelections();
18587 onContextMenu : function(e){
18588 var item = this.findItemFromChild(e.getTarget());
18590 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18595 onDblClick : function(e){
18596 var item = this.findItemFromChild(e.getTarget());
18598 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18602 onItemClick : function(item, index, e)
18604 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18607 if (this.toggleSelect) {
18608 var m = this.isSelected(item) ? 'unselect' : 'select';
18611 _t[m](item, true, false);
18614 if(this.multiSelect || this.singleSelect){
18615 if(this.multiSelect && e.shiftKey && this.lastSelection){
18616 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18618 this.select(item, this.multiSelect && e.ctrlKey);
18619 this.lastSelection = item;
18622 if(!this.tickable){
18623 e.preventDefault();
18631 * Get the number of selected nodes.
18634 getSelectionCount : function(){
18635 return this.selections.length;
18639 * Get the currently selected nodes.
18640 * @return {Array} An array of HTMLElements
18642 getSelectedNodes : function(){
18643 return this.selections;
18647 * Get the indexes of the selected nodes.
18650 getSelectedIndexes : function(){
18651 var indexes = [], s = this.selections;
18652 for(var i = 0, len = s.length; i < len; i++){
18653 indexes.push(s[i].nodeIndex);
18659 * Clear all selections
18660 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18662 clearSelections : function(suppressEvent){
18663 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18664 this.cmp.elements = this.selections;
18665 this.cmp.removeClass(this.selectedClass);
18666 this.selections = [];
18667 if(!suppressEvent){
18668 this.fireEvent("selectionchange", this, this.selections);
18674 * Returns true if the passed node is selected
18675 * @param {HTMLElement/Number} node The node or node index
18676 * @return {Boolean}
18678 isSelected : function(node){
18679 var s = this.selections;
18683 node = this.getNode(node);
18684 return s.indexOf(node) !== -1;
18689 * @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
18690 * @param {Boolean} keepExisting (optional) true to keep existing selections
18691 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18693 select : function(nodeInfo, keepExisting, suppressEvent){
18694 if(nodeInfo instanceof Array){
18696 this.clearSelections(true);
18698 for(var i = 0, len = nodeInfo.length; i < len; i++){
18699 this.select(nodeInfo[i], true, true);
18703 var node = this.getNode(nodeInfo);
18704 if(!node || this.isSelected(node)){
18705 return; // already selected.
18708 this.clearSelections(true);
18711 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18712 Roo.fly(node).addClass(this.selectedClass);
18713 this.selections.push(node);
18714 if(!suppressEvent){
18715 this.fireEvent("selectionchange", this, this.selections);
18723 * @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
18724 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18725 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18727 unselect : function(nodeInfo, keepExisting, suppressEvent)
18729 if(nodeInfo instanceof Array){
18730 Roo.each(this.selections, function(s) {
18731 this.unselect(s, nodeInfo);
18735 var node = this.getNode(nodeInfo);
18736 if(!node || !this.isSelected(node)){
18737 //Roo.log("not selected");
18738 return; // not selected.
18742 Roo.each(this.selections, function(s) {
18744 Roo.fly(node).removeClass(this.selectedClass);
18751 this.selections= ns;
18752 this.fireEvent("selectionchange", this, this.selections);
18756 * Gets a template node.
18757 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18758 * @return {HTMLElement} The node or null if it wasn't found
18760 getNode : function(nodeInfo){
18761 if(typeof nodeInfo == "string"){
18762 return document.getElementById(nodeInfo);
18763 }else if(typeof nodeInfo == "number"){
18764 return this.nodes[nodeInfo];
18770 * Gets a range template nodes.
18771 * @param {Number} startIndex
18772 * @param {Number} endIndex
18773 * @return {Array} An array of nodes
18775 getNodes : function(start, end){
18776 var ns = this.nodes;
18777 start = start || 0;
18778 end = typeof end == "undefined" ? ns.length - 1 : end;
18781 for(var i = start; i <= end; i++){
18785 for(var i = start; i >= end; i--){
18793 * Finds the index of the passed node
18794 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18795 * @return {Number} The index of the node or -1
18797 indexOf : function(node){
18798 node = this.getNode(node);
18799 if(typeof node.nodeIndex == "number"){
18800 return node.nodeIndex;
18802 var ns = this.nodes;
18803 for(var i = 0, len = ns.length; i < len; i++){
18814 * based on jquery fullcalendar
18818 Roo.bootstrap = Roo.bootstrap || {};
18820 * @class Roo.bootstrap.Calendar
18821 * @extends Roo.bootstrap.Component
18822 * Bootstrap Calendar class
18823 * @cfg {Boolean} loadMask (true|false) default false
18824 * @cfg {Object} header generate the user specific header of the calendar, default false
18827 * Create a new Container
18828 * @param {Object} config The config object
18833 Roo.bootstrap.Calendar = function(config){
18834 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18838 * Fires when a date is selected
18839 * @param {DatePicker} this
18840 * @param {Date} date The selected date
18844 * @event monthchange
18845 * Fires when the displayed month changes
18846 * @param {DatePicker} this
18847 * @param {Date} date The selected month
18849 'monthchange': true,
18851 * @event evententer
18852 * Fires when mouse over an event
18853 * @param {Calendar} this
18854 * @param {event} Event
18856 'evententer': true,
18858 * @event eventleave
18859 * Fires when the mouse leaves an
18860 * @param {Calendar} this
18863 'eventleave': true,
18865 * @event eventclick
18866 * Fires when the mouse click an
18867 * @param {Calendar} this
18876 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18879 * @cfg {Number} startDay
18880 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18888 getAutoCreate : function(){
18891 var fc_button = function(name, corner, style, content ) {
18892 return Roo.apply({},{
18894 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18896 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18899 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18910 style : 'width:100%',
18917 cls : 'fc-header-left',
18919 fc_button('prev', 'left', 'arrow', '‹' ),
18920 fc_button('next', 'right', 'arrow', '›' ),
18921 { tag: 'span', cls: 'fc-header-space' },
18922 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18930 cls : 'fc-header-center',
18934 cls: 'fc-header-title',
18937 html : 'month / year'
18945 cls : 'fc-header-right',
18947 /* fc_button('month', 'left', '', 'month' ),
18948 fc_button('week', '', '', 'week' ),
18949 fc_button('day', 'right', '', 'day' )
18961 header = this.header;
18964 var cal_heads = function() {
18966 // fixme - handle this.
18968 for (var i =0; i < Date.dayNames.length; i++) {
18969 var d = Date.dayNames[i];
18972 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18973 html : d.substring(0,3)
18977 ret[0].cls += ' fc-first';
18978 ret[6].cls += ' fc-last';
18981 var cal_cell = function(n) {
18984 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18989 cls: 'fc-day-number',
18993 cls: 'fc-day-content',
18997 style: 'position: relative;' // height: 17px;
19009 var cal_rows = function() {
19012 for (var r = 0; r < 6; r++) {
19019 for (var i =0; i < Date.dayNames.length; i++) {
19020 var d = Date.dayNames[i];
19021 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19024 row.cn[0].cls+=' fc-first';
19025 row.cn[0].cn[0].style = 'min-height:90px';
19026 row.cn[6].cls+=' fc-last';
19030 ret[0].cls += ' fc-first';
19031 ret[4].cls += ' fc-prev-last';
19032 ret[5].cls += ' fc-last';
19039 cls: 'fc-border-separate',
19040 style : 'width:100%',
19048 cls : 'fc-first fc-last',
19066 cls : 'fc-content',
19067 style : "position: relative;",
19070 cls : 'fc-view fc-view-month fc-grid',
19071 style : 'position: relative',
19072 unselectable : 'on',
19075 cls : 'fc-event-container',
19076 style : 'position:absolute;z-index:8;top:0;left:0;'
19094 initEvents : function()
19097 throw "can not find store for calendar";
19103 style: "text-align:center",
19107 style: "background-color:white;width:50%;margin:250 auto",
19111 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19122 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19124 var size = this.el.select('.fc-content', true).first().getSize();
19125 this.maskEl.setSize(size.width, size.height);
19126 this.maskEl.enableDisplayMode("block");
19127 if(!this.loadMask){
19128 this.maskEl.hide();
19131 this.store = Roo.factory(this.store, Roo.data);
19132 this.store.on('load', this.onLoad, this);
19133 this.store.on('beforeload', this.onBeforeLoad, this);
19137 this.cells = this.el.select('.fc-day',true);
19138 //Roo.log(this.cells);
19139 this.textNodes = this.el.query('.fc-day-number');
19140 this.cells.addClassOnOver('fc-state-hover');
19142 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19143 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19144 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19145 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19147 this.on('monthchange', this.onMonthChange, this);
19149 this.update(new Date().clearTime());
19152 resize : function() {
19153 var sz = this.el.getSize();
19155 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19156 this.el.select('.fc-day-content div',true).setHeight(34);
19161 showPrevMonth : function(e){
19162 this.update(this.activeDate.add("mo", -1));
19164 showToday : function(e){
19165 this.update(new Date().clearTime());
19168 showNextMonth : function(e){
19169 this.update(this.activeDate.add("mo", 1));
19173 showPrevYear : function(){
19174 this.update(this.activeDate.add("y", -1));
19178 showNextYear : function(){
19179 this.update(this.activeDate.add("y", 1));
19184 update : function(date)
19186 var vd = this.activeDate;
19187 this.activeDate = date;
19188 // if(vd && this.el){
19189 // var t = date.getTime();
19190 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19191 // Roo.log('using add remove');
19193 // this.fireEvent('monthchange', this, date);
19195 // this.cells.removeClass("fc-state-highlight");
19196 // this.cells.each(function(c){
19197 // if(c.dateValue == t){
19198 // c.addClass("fc-state-highlight");
19199 // setTimeout(function(){
19200 // try{c.dom.firstChild.focus();}catch(e){}
19210 var days = date.getDaysInMonth();
19212 var firstOfMonth = date.getFirstDateOfMonth();
19213 var startingPos = firstOfMonth.getDay()-this.startDay;
19215 if(startingPos < this.startDay){
19219 var pm = date.add(Date.MONTH, -1);
19220 var prevStart = pm.getDaysInMonth()-startingPos;
19222 this.cells = this.el.select('.fc-day',true);
19223 this.textNodes = this.el.query('.fc-day-number');
19224 this.cells.addClassOnOver('fc-state-hover');
19226 var cells = this.cells.elements;
19227 var textEls = this.textNodes;
19229 Roo.each(cells, function(cell){
19230 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19233 days += startingPos;
19235 // convert everything to numbers so it's fast
19236 var day = 86400000;
19237 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19240 //Roo.log(prevStart);
19242 var today = new Date().clearTime().getTime();
19243 var sel = date.clearTime().getTime();
19244 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19245 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19246 var ddMatch = this.disabledDatesRE;
19247 var ddText = this.disabledDatesText;
19248 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19249 var ddaysText = this.disabledDaysText;
19250 var format = this.format;
19252 var setCellClass = function(cal, cell){
19256 //Roo.log('set Cell Class');
19258 var t = d.getTime();
19262 cell.dateValue = t;
19264 cell.className += " fc-today";
19265 cell.className += " fc-state-highlight";
19266 cell.title = cal.todayText;
19269 // disable highlight in other month..
19270 //cell.className += " fc-state-highlight";
19275 cell.className = " fc-state-disabled";
19276 cell.title = cal.minText;
19280 cell.className = " fc-state-disabled";
19281 cell.title = cal.maxText;
19285 if(ddays.indexOf(d.getDay()) != -1){
19286 cell.title = ddaysText;
19287 cell.className = " fc-state-disabled";
19290 if(ddMatch && format){
19291 var fvalue = d.dateFormat(format);
19292 if(ddMatch.test(fvalue)){
19293 cell.title = ddText.replace("%0", fvalue);
19294 cell.className = " fc-state-disabled";
19298 if (!cell.initialClassName) {
19299 cell.initialClassName = cell.dom.className;
19302 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19307 for(; i < startingPos; i++) {
19308 textEls[i].innerHTML = (++prevStart);
19309 d.setDate(d.getDate()+1);
19311 cells[i].className = "fc-past fc-other-month";
19312 setCellClass(this, cells[i]);
19317 for(; i < days; i++){
19318 intDay = i - startingPos + 1;
19319 textEls[i].innerHTML = (intDay);
19320 d.setDate(d.getDate()+1);
19322 cells[i].className = ''; // "x-date-active";
19323 setCellClass(this, cells[i]);
19327 for(; i < 42; i++) {
19328 textEls[i].innerHTML = (++extraDays);
19329 d.setDate(d.getDate()+1);
19331 cells[i].className = "fc-future fc-other-month";
19332 setCellClass(this, cells[i]);
19335 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19337 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19339 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19340 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19342 if(totalRows != 6){
19343 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19344 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19347 this.fireEvent('monthchange', this, date);
19351 if(!this.internalRender){
19352 var main = this.el.dom.firstChild;
19353 var w = main.offsetWidth;
19354 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19355 Roo.fly(main).setWidth(w);
19356 this.internalRender = true;
19357 // opera does not respect the auto grow header center column
19358 // then, after it gets a width opera refuses to recalculate
19359 // without a second pass
19360 if(Roo.isOpera && !this.secondPass){
19361 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19362 this.secondPass = true;
19363 this.update.defer(10, this, [date]);
19370 findCell : function(dt) {
19371 dt = dt.clearTime().getTime();
19373 this.cells.each(function(c){
19374 //Roo.log("check " +c.dateValue + '?=' + dt);
19375 if(c.dateValue == dt){
19385 findCells : function(ev) {
19386 var s = ev.start.clone().clearTime().getTime();
19388 var e= ev.end.clone().clearTime().getTime();
19391 this.cells.each(function(c){
19392 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19394 if(c.dateValue > e){
19397 if(c.dateValue < s){
19406 // findBestRow: function(cells)
19410 // for (var i =0 ; i < cells.length;i++) {
19411 // ret = Math.max(cells[i].rows || 0,ret);
19418 addItem : function(ev)
19420 // look for vertical location slot in
19421 var cells = this.findCells(ev);
19423 // ev.row = this.findBestRow(cells);
19425 // work out the location.
19429 for(var i =0; i < cells.length; i++) {
19431 cells[i].row = cells[0].row;
19434 cells[i].row = cells[i].row + 1;
19444 if (crow.start.getY() == cells[i].getY()) {
19446 crow.end = cells[i];
19463 cells[0].events.push(ev);
19465 this.calevents.push(ev);
19468 clearEvents: function() {
19470 if(!this.calevents){
19474 Roo.each(this.cells.elements, function(c){
19480 Roo.each(this.calevents, function(e) {
19481 Roo.each(e.els, function(el) {
19482 el.un('mouseenter' ,this.onEventEnter, this);
19483 el.un('mouseleave' ,this.onEventLeave, this);
19488 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19494 renderEvents: function()
19498 this.cells.each(function(c) {
19507 if(c.row != c.events.length){
19508 r = 4 - (4 - (c.row - c.events.length));
19511 c.events = ev.slice(0, r);
19512 c.more = ev.slice(r);
19514 if(c.more.length && c.more.length == 1){
19515 c.events.push(c.more.pop());
19518 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19522 this.cells.each(function(c) {
19524 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19527 for (var e = 0; e < c.events.length; e++){
19528 var ev = c.events[e];
19529 var rows = ev.rows;
19531 for(var i = 0; i < rows.length; i++) {
19533 // how many rows should it span..
19536 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19537 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19539 unselectable : "on",
19542 cls: 'fc-event-inner',
19546 // cls: 'fc-event-time',
19547 // html : cells.length > 1 ? '' : ev.time
19551 cls: 'fc-event-title',
19552 html : String.format('{0}', ev.title)
19559 cls: 'ui-resizable-handle ui-resizable-e',
19560 html : '  '
19567 cfg.cls += ' fc-event-start';
19569 if ((i+1) == rows.length) {
19570 cfg.cls += ' fc-event-end';
19573 var ctr = _this.el.select('.fc-event-container',true).first();
19574 var cg = ctr.createChild(cfg);
19576 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19577 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19579 var r = (c.more.length) ? 1 : 0;
19580 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19581 cg.setWidth(ebox.right - sbox.x -2);
19583 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19584 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19585 cg.on('click', _this.onEventClick, _this, ev);
19596 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19597 style : 'position: absolute',
19598 unselectable : "on",
19601 cls: 'fc-event-inner',
19605 cls: 'fc-event-title',
19613 cls: 'ui-resizable-handle ui-resizable-e',
19614 html : '  '
19620 var ctr = _this.el.select('.fc-event-container',true).first();
19621 var cg = ctr.createChild(cfg);
19623 var sbox = c.select('.fc-day-content',true).first().getBox();
19624 var ebox = c.select('.fc-day-content',true).first().getBox();
19626 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19627 cg.setWidth(ebox.right - sbox.x -2);
19629 cg.on('click', _this.onMoreEventClick, _this, c.more);
19639 onEventEnter: function (e, el,event,d) {
19640 this.fireEvent('evententer', this, el, event);
19643 onEventLeave: function (e, el,event,d) {
19644 this.fireEvent('eventleave', this, el, event);
19647 onEventClick: function (e, el,event,d) {
19648 this.fireEvent('eventclick', this, el, event);
19651 onMonthChange: function () {
19655 onMoreEventClick: function(e, el, more)
19659 this.calpopover.placement = 'right';
19660 this.calpopover.setTitle('More');
19662 this.calpopover.setContent('');
19664 var ctr = this.calpopover.el.select('.popover-content', true).first();
19666 Roo.each(more, function(m){
19668 cls : 'fc-event-hori fc-event-draggable',
19671 var cg = ctr.createChild(cfg);
19673 cg.on('click', _this.onEventClick, _this, m);
19676 this.calpopover.show(el);
19681 onLoad: function ()
19683 this.calevents = [];
19686 if(this.store.getCount() > 0){
19687 this.store.data.each(function(d){
19690 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19691 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19692 time : d.data.start_time,
19693 title : d.data.title,
19694 description : d.data.description,
19695 venue : d.data.venue
19700 this.renderEvents();
19702 if(this.calevents.length && this.loadMask){
19703 this.maskEl.hide();
19707 onBeforeLoad: function()
19709 this.clearEvents();
19711 this.maskEl.show();
19725 * @class Roo.bootstrap.Popover
19726 * @extends Roo.bootstrap.Component
19727 * Bootstrap Popover class
19728 * @cfg {String} html contents of the popover (or false to use children..)
19729 * @cfg {String} title of popover (or false to hide)
19730 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19731 * @cfg {String} trigger click || hover (or false to trigger manually)
19732 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19733 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19734 * - if false and it has a 'parent' then it will be automatically added to that element
19735 * - if string - Roo.get will be called
19736 * @cfg {Number} delay - delay before showing
19739 * Create a new Popover
19740 * @param {Object} config The config object
19743 Roo.bootstrap.Popover = function(config){
19744 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19750 * After the popover show
19752 * @param {Roo.bootstrap.Popover} this
19757 * After the popover hide
19759 * @param {Roo.bootstrap.Popover} this
19765 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19770 placement : 'right',
19771 trigger : 'hover', // hover
19777 can_build_overlaid : false,
19779 maskEl : false, // the mask element
19782 alignEl : false, // when show is called with an element - this get's stored.
19784 getChildContainer : function()
19786 return this.contentEl;
19789 getPopoverHeader : function()
19791 this.title = true; // flag not to hide it..
19792 this.headerEl.addClass('p-0');
19793 return this.headerEl
19797 getAutoCreate : function(){
19800 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19801 style: 'display:block',
19807 cls : 'popover-inner ',
19811 cls: 'popover-title popover-header',
19812 html : this.title === false ? '' : this.title
19815 cls : 'popover-content popover-body ' + (this.cls || ''),
19816 html : this.html || ''
19827 * @param {string} the title
19829 setTitle: function(str)
19833 this.headerEl.dom.innerHTML = str;
19838 * @param {string} the body content
19840 setContent: function(str)
19843 if (this.contentEl) {
19844 this.contentEl.dom.innerHTML = str;
19848 // as it get's added to the bottom of the page.
19849 onRender : function(ct, position)
19851 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19856 var cfg = Roo.apply({}, this.getAutoCreate());
19860 cfg.cls += ' ' + this.cls;
19863 cfg.style = this.style;
19865 //Roo.log("adding to ");
19866 this.el = Roo.get(document.body).createChild(cfg, position);
19867 // Roo.log(this.el);
19870 this.contentEl = this.el.select('.popover-content',true).first();
19871 this.headerEl = this.el.select('.popover-title',true).first();
19874 if(typeof(this.items) != 'undefined'){
19875 var items = this.items;
19878 for(var i =0;i < items.length;i++) {
19879 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19883 this.items = nitems;
19885 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19886 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19893 resizeMask : function()
19895 this.maskEl.setSize(
19896 Roo.lib.Dom.getViewWidth(true),
19897 Roo.lib.Dom.getViewHeight(true)
19901 initEvents : function()
19905 Roo.bootstrap.Popover.register(this);
19908 this.arrowEl = this.el.select('.arrow',true).first();
19909 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19910 this.el.enableDisplayMode('block');
19914 if (this.over === false && !this.parent()) {
19917 if (this.triggers === false) {
19922 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19923 var triggers = this.trigger ? this.trigger.split(' ') : [];
19924 Roo.each(triggers, function(trigger) {
19926 if (trigger == 'click') {
19927 on_el.on('click', this.toggle, this);
19928 } else if (trigger != 'manual') {
19929 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19930 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19932 on_el.on(eventIn ,this.enter, this);
19933 on_el.on(eventOut, this.leave, this);
19943 toggle : function () {
19944 this.hoverState == 'in' ? this.leave() : this.enter();
19947 enter : function () {
19949 clearTimeout(this.timeout);
19951 this.hoverState = 'in';
19953 if (!this.delay || !this.delay.show) {
19958 this.timeout = setTimeout(function () {
19959 if (_t.hoverState == 'in') {
19962 }, this.delay.show)
19965 leave : function() {
19966 clearTimeout(this.timeout);
19968 this.hoverState = 'out';
19970 if (!this.delay || !this.delay.hide) {
19975 this.timeout = setTimeout(function () {
19976 if (_t.hoverState == 'out') {
19979 }, this.delay.hide)
19983 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19984 * @param {string} (left|right|top|bottom) position
19986 show : function (on_el, placement)
19988 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19989 on_el = on_el || false; // default to false
19992 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19993 on_el = this.parent().el;
19994 } else if (this.over) {
19995 Roo.get(this.over);
20000 this.alignEl = Roo.get( on_el );
20003 this.render(document.body);
20009 if (this.title === false) {
20010 this.headerEl.hide();
20015 this.el.dom.style.display = 'block';
20018 if (this.alignEl) {
20019 this.updatePosition(this.placement, true);
20022 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20023 var es = this.el.getSize();
20024 var x = Roo.lib.Dom.getViewWidth()/2;
20025 var y = Roo.lib.Dom.getViewHeight()/2;
20026 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20031 //var arrow = this.el.select('.arrow',true).first();
20032 //arrow.set(align[2],
20034 this.el.addClass('in');
20038 this.hoverState = 'in';
20041 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20042 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20043 this.maskEl.dom.style.display = 'block';
20044 this.maskEl.addClass('show');
20046 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20048 this.fireEvent('show', this);
20052 * fire this manually after loading a grid in the table for example
20053 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20054 * @param {Boolean} try and move it if we cant get right position.
20056 updatePosition : function(placement, try_move)
20058 // allow for calling with no parameters
20059 placement = placement ? placement : this.placement;
20060 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20062 this.el.removeClass([
20063 'fade','top','bottom', 'left', 'right','in',
20064 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20066 this.el.addClass(placement + ' bs-popover-' + placement);
20068 if (!this.alignEl ) {
20072 switch (placement) {
20074 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20075 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20076 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20077 //normal display... or moved up/down.
20078 this.el.setXY(offset);
20079 var xy = this.alignEl.getAnchorXY('tr', false);
20081 this.arrowEl.setXY(xy);
20084 // continue through...
20085 return this.updatePosition('left', false);
20089 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20090 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20091 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20092 //normal display... or moved up/down.
20093 this.el.setXY(offset);
20094 var xy = this.alignEl.getAnchorXY('tl', false);
20095 xy[0]-=10;xy[1]+=5; // << fix me
20096 this.arrowEl.setXY(xy);
20100 return this.updatePosition('right', false);
20103 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20104 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20105 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20106 //normal display... or moved up/down.
20107 this.el.setXY(offset);
20108 var xy = this.alignEl.getAnchorXY('t', false);
20109 xy[1]-=10; // << fix me
20110 this.arrowEl.setXY(xy);
20114 return this.updatePosition('bottom', false);
20117 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20118 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20119 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20120 //normal display... or moved up/down.
20121 this.el.setXY(offset);
20122 var xy = this.alignEl.getAnchorXY('b', false);
20123 xy[1]+=2; // << fix me
20124 this.arrowEl.setXY(xy);
20128 return this.updatePosition('top', false);
20139 this.el.setXY([0,0]);
20140 this.el.removeClass('in');
20142 this.hoverState = null;
20143 this.maskEl.hide(); // always..
20144 this.fireEvent('hide', this);
20150 Roo.apply(Roo.bootstrap.Popover, {
20153 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20154 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20155 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20156 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20161 clickHander : false,
20164 onMouseDown : function(e)
20166 if (!e.getTarget(".roo-popover")) {
20174 register : function(popup)
20176 if (!Roo.bootstrap.Popover.clickHandler) {
20177 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20179 // hide other popups.
20181 this.popups.push(popup);
20183 hideAll : function()
20185 this.popups.forEach(function(p) {
20193 * Card header - holder for the card header elements.
20198 * @class Roo.bootstrap.PopoverNav
20199 * @extends Roo.bootstrap.NavGroup
20200 * Bootstrap Popover header navigation class
20202 * Create a new Popover Header Navigation
20203 * @param {Object} config The config object
20206 Roo.bootstrap.PopoverNav = function(config){
20207 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20210 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20213 container_method : 'getPopoverHeader'
20231 * @class Roo.bootstrap.Progress
20232 * @extends Roo.bootstrap.Component
20233 * Bootstrap Progress class
20234 * @cfg {Boolean} striped striped of the progress bar
20235 * @cfg {Boolean} active animated of the progress bar
20239 * Create a new Progress
20240 * @param {Object} config The config object
20243 Roo.bootstrap.Progress = function(config){
20244 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20247 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20252 getAutoCreate : function(){
20260 cfg.cls += ' progress-striped';
20264 cfg.cls += ' active';
20283 * @class Roo.bootstrap.ProgressBar
20284 * @extends Roo.bootstrap.Component
20285 * Bootstrap ProgressBar class
20286 * @cfg {Number} aria_valuenow aria-value now
20287 * @cfg {Number} aria_valuemin aria-value min
20288 * @cfg {Number} aria_valuemax aria-value max
20289 * @cfg {String} label label for the progress bar
20290 * @cfg {String} panel (success | info | warning | danger )
20291 * @cfg {String} role role of the progress bar
20292 * @cfg {String} sr_only text
20296 * Create a new ProgressBar
20297 * @param {Object} config The config object
20300 Roo.bootstrap.ProgressBar = function(config){
20301 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20304 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20308 aria_valuemax : 100,
20314 getAutoCreate : function()
20319 cls: 'progress-bar',
20320 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20332 cfg.role = this.role;
20335 if(this.aria_valuenow){
20336 cfg['aria-valuenow'] = this.aria_valuenow;
20339 if(this.aria_valuemin){
20340 cfg['aria-valuemin'] = this.aria_valuemin;
20343 if(this.aria_valuemax){
20344 cfg['aria-valuemax'] = this.aria_valuemax;
20347 if(this.label && !this.sr_only){
20348 cfg.html = this.label;
20352 cfg.cls += ' progress-bar-' + this.panel;
20358 update : function(aria_valuenow)
20360 this.aria_valuenow = aria_valuenow;
20362 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20377 * @class Roo.bootstrap.TabGroup
20378 * @extends Roo.bootstrap.Column
20379 * Bootstrap Column class
20380 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20381 * @cfg {Boolean} carousel true to make the group behave like a carousel
20382 * @cfg {Boolean} bullets show bullets for the panels
20383 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20384 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20385 * @cfg {Boolean} showarrow (true|false) show arrow default true
20388 * Create a new TabGroup
20389 * @param {Object} config The config object
20392 Roo.bootstrap.TabGroup = function(config){
20393 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20395 this.navId = Roo.id();
20398 Roo.bootstrap.TabGroup.register(this);
20402 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20405 transition : false,
20410 slideOnTouch : false,
20413 getAutoCreate : function()
20415 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20417 cfg.cls += ' tab-content';
20419 if (this.carousel) {
20420 cfg.cls += ' carousel slide';
20423 cls : 'carousel-inner',
20427 if(this.bullets && !Roo.isTouch){
20430 cls : 'carousel-bullets',
20434 if(this.bullets_cls){
20435 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20442 cfg.cn[0].cn.push(bullets);
20445 if(this.showarrow){
20446 cfg.cn[0].cn.push({
20448 class : 'carousel-arrow',
20452 class : 'carousel-prev',
20456 class : 'fa fa-chevron-left'
20462 class : 'carousel-next',
20466 class : 'fa fa-chevron-right'
20479 initEvents: function()
20481 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20482 // this.el.on("touchstart", this.onTouchStart, this);
20485 if(this.autoslide){
20488 this.slideFn = window.setInterval(function() {
20489 _this.showPanelNext();
20493 if(this.showarrow){
20494 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20495 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20501 // onTouchStart : function(e, el, o)
20503 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20507 // this.showPanelNext();
20511 getChildContainer : function()
20513 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20517 * register a Navigation item
20518 * @param {Roo.bootstrap.NavItem} the navitem to add
20520 register : function(item)
20522 this.tabs.push( item);
20523 item.navId = this.navId; // not really needed..
20528 getActivePanel : function()
20531 Roo.each(this.tabs, function(t) {
20541 getPanelByName : function(n)
20544 Roo.each(this.tabs, function(t) {
20545 if (t.tabId == n) {
20553 indexOfPanel : function(p)
20556 Roo.each(this.tabs, function(t,i) {
20557 if (t.tabId == p.tabId) {
20566 * show a specific panel
20567 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20568 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20570 showPanel : function (pan)
20572 if(this.transition || typeof(pan) == 'undefined'){
20573 Roo.log("waiting for the transitionend");
20577 if (typeof(pan) == 'number') {
20578 pan = this.tabs[pan];
20581 if (typeof(pan) == 'string') {
20582 pan = this.getPanelByName(pan);
20585 var cur = this.getActivePanel();
20588 Roo.log('pan or acitve pan is undefined');
20592 if (pan.tabId == this.getActivePanel().tabId) {
20596 if (false === cur.fireEvent('beforedeactivate')) {
20600 if(this.bullets > 0 && !Roo.isTouch){
20601 this.setActiveBullet(this.indexOfPanel(pan));
20604 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20606 //class="carousel-item carousel-item-next carousel-item-left"
20608 this.transition = true;
20609 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20610 var lr = dir == 'next' ? 'left' : 'right';
20611 pan.el.addClass(dir); // or prev
20612 pan.el.addClass('carousel-item-' + dir); // or prev
20613 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20614 cur.el.addClass(lr); // or right
20615 pan.el.addClass(lr);
20616 cur.el.addClass('carousel-item-' +lr); // or right
20617 pan.el.addClass('carousel-item-' +lr);
20621 cur.el.on('transitionend', function() {
20622 Roo.log("trans end?");
20624 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20625 pan.setActive(true);
20627 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20628 cur.setActive(false);
20630 _this.transition = false;
20632 }, this, { single: true } );
20637 cur.setActive(false);
20638 pan.setActive(true);
20643 showPanelNext : function()
20645 var i = this.indexOfPanel(this.getActivePanel());
20647 if (i >= this.tabs.length - 1 && !this.autoslide) {
20651 if (i >= this.tabs.length - 1 && this.autoslide) {
20655 this.showPanel(this.tabs[i+1]);
20658 showPanelPrev : function()
20660 var i = this.indexOfPanel(this.getActivePanel());
20662 if (i < 1 && !this.autoslide) {
20666 if (i < 1 && this.autoslide) {
20667 i = this.tabs.length;
20670 this.showPanel(this.tabs[i-1]);
20674 addBullet: function()
20676 if(!this.bullets || Roo.isTouch){
20679 var ctr = this.el.select('.carousel-bullets',true).first();
20680 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20681 var bullet = ctr.createChild({
20682 cls : 'bullet bullet-' + i
20683 },ctr.dom.lastChild);
20688 bullet.on('click', (function(e, el, o, ii, t){
20690 e.preventDefault();
20692 this.showPanel(ii);
20694 if(this.autoslide && this.slideFn){
20695 clearInterval(this.slideFn);
20696 this.slideFn = window.setInterval(function() {
20697 _this.showPanelNext();
20701 }).createDelegate(this, [i, bullet], true));
20706 setActiveBullet : function(i)
20712 Roo.each(this.el.select('.bullet', true).elements, function(el){
20713 el.removeClass('selected');
20716 var bullet = this.el.select('.bullet-' + i, true).first();
20722 bullet.addClass('selected');
20733 Roo.apply(Roo.bootstrap.TabGroup, {
20737 * register a Navigation Group
20738 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20740 register : function(navgrp)
20742 this.groups[navgrp.navId] = navgrp;
20746 * fetch a Navigation Group based on the navigation ID
20747 * if one does not exist , it will get created.
20748 * @param {string} the navgroup to add
20749 * @returns {Roo.bootstrap.NavGroup} the navgroup
20751 get: function(navId) {
20752 if (typeof(this.groups[navId]) == 'undefined') {
20753 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20755 return this.groups[navId] ;
20770 * @class Roo.bootstrap.TabPanel
20771 * @extends Roo.bootstrap.Component
20772 * Bootstrap TabPanel class
20773 * @cfg {Boolean} active panel active
20774 * @cfg {String} html panel content
20775 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20776 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20777 * @cfg {String} href click to link..
20778 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20782 * Create a new TabPanel
20783 * @param {Object} config The config object
20786 Roo.bootstrap.TabPanel = function(config){
20787 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20791 * Fires when the active status changes
20792 * @param {Roo.bootstrap.TabPanel} this
20793 * @param {Boolean} state the new state
20798 * @event beforedeactivate
20799 * Fires before a tab is de-activated - can be used to do validation on a form.
20800 * @param {Roo.bootstrap.TabPanel} this
20801 * @return {Boolean} false if there is an error
20804 'beforedeactivate': true
20807 this.tabId = this.tabId || Roo.id();
20811 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20818 touchSlide : false,
20819 getAutoCreate : function(){
20824 // item is needed for carousel - not sure if it has any effect otherwise
20825 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20826 html: this.html || ''
20830 cfg.cls += ' active';
20834 cfg.tabId = this.tabId;
20842 initEvents: function()
20844 var p = this.parent();
20846 this.navId = this.navId || p.navId;
20848 if (typeof(this.navId) != 'undefined') {
20849 // not really needed.. but just in case.. parent should be a NavGroup.
20850 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20854 var i = tg.tabs.length - 1;
20856 if(this.active && tg.bullets > 0 && i < tg.bullets){
20857 tg.setActiveBullet(i);
20861 this.el.on('click', this.onClick, this);
20863 if(Roo.isTouch && this.touchSlide){
20864 this.el.on("touchstart", this.onTouchStart, this);
20865 this.el.on("touchmove", this.onTouchMove, this);
20866 this.el.on("touchend", this.onTouchEnd, this);
20871 onRender : function(ct, position)
20873 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20876 setActive : function(state)
20878 Roo.log("panel - set active " + this.tabId + "=" + state);
20880 this.active = state;
20882 this.el.removeClass('active');
20884 } else if (!this.el.hasClass('active')) {
20885 this.el.addClass('active');
20888 this.fireEvent('changed', this, state);
20891 onClick : function(e)
20893 e.preventDefault();
20895 if(!this.href.length){
20899 window.location.href = this.href;
20908 onTouchStart : function(e)
20910 this.swiping = false;
20912 this.startX = e.browserEvent.touches[0].clientX;
20913 this.startY = e.browserEvent.touches[0].clientY;
20916 onTouchMove : function(e)
20918 this.swiping = true;
20920 this.endX = e.browserEvent.touches[0].clientX;
20921 this.endY = e.browserEvent.touches[0].clientY;
20924 onTouchEnd : function(e)
20931 var tabGroup = this.parent();
20933 if(this.endX > this.startX){ // swiping right
20934 tabGroup.showPanelPrev();
20938 if(this.startX > this.endX){ // swiping left
20939 tabGroup.showPanelNext();
20958 * @class Roo.bootstrap.DateField
20959 * @extends Roo.bootstrap.Input
20960 * Bootstrap DateField class
20961 * @cfg {Number} weekStart default 0
20962 * @cfg {String} viewMode default empty, (months|years)
20963 * @cfg {String} minViewMode default empty, (months|years)
20964 * @cfg {Number} startDate default -Infinity
20965 * @cfg {Number} endDate default Infinity
20966 * @cfg {Boolean} todayHighlight default false
20967 * @cfg {Boolean} todayBtn default false
20968 * @cfg {Boolean} calendarWeeks default false
20969 * @cfg {Object} daysOfWeekDisabled default empty
20970 * @cfg {Boolean} singleMode default false (true | false)
20972 * @cfg {Boolean} keyboardNavigation default true
20973 * @cfg {String} language default en
20976 * Create a new DateField
20977 * @param {Object} config The config object
20980 Roo.bootstrap.DateField = function(config){
20981 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20985 * Fires when this field show.
20986 * @param {Roo.bootstrap.DateField} this
20987 * @param {Mixed} date The date value
20992 * Fires when this field hide.
20993 * @param {Roo.bootstrap.DateField} this
20994 * @param {Mixed} date The date value
20999 * Fires when select a date.
21000 * @param {Roo.bootstrap.DateField} this
21001 * @param {Mixed} date The date value
21005 * @event beforeselect
21006 * Fires when before select a date.
21007 * @param {Roo.bootstrap.DateField} this
21008 * @param {Mixed} date The date value
21010 beforeselect : true
21014 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21017 * @cfg {String} format
21018 * The default date format string which can be overriden for localization support. The format must be
21019 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21023 * @cfg {String} altFormats
21024 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21025 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21027 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21035 todayHighlight : false,
21041 keyboardNavigation: true,
21043 calendarWeeks: false,
21045 startDate: -Infinity,
21049 daysOfWeekDisabled: [],
21053 singleMode : false,
21055 UTCDate: function()
21057 return new Date(Date.UTC.apply(Date, arguments));
21060 UTCToday: function()
21062 var today = new Date();
21063 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21066 getDate: function() {
21067 var d = this.getUTCDate();
21068 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21071 getUTCDate: function() {
21075 setDate: function(d) {
21076 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21079 setUTCDate: function(d) {
21081 this.setValue(this.formatDate(this.date));
21084 onRender: function(ct, position)
21087 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21089 this.language = this.language || 'en';
21090 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21091 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21093 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21094 this.format = this.format || 'm/d/y';
21095 this.isInline = false;
21096 this.isInput = true;
21097 this.component = this.el.select('.add-on', true).first() || false;
21098 this.component = (this.component && this.component.length === 0) ? false : this.component;
21099 this.hasInput = this.component && this.inputEl().length;
21101 if (typeof(this.minViewMode === 'string')) {
21102 switch (this.minViewMode) {
21104 this.minViewMode = 1;
21107 this.minViewMode = 2;
21110 this.minViewMode = 0;
21115 if (typeof(this.viewMode === 'string')) {
21116 switch (this.viewMode) {
21129 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21131 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21133 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21135 this.picker().on('mousedown', this.onMousedown, this);
21136 this.picker().on('click', this.onClick, this);
21138 this.picker().addClass('datepicker-dropdown');
21140 this.startViewMode = this.viewMode;
21142 if(this.singleMode){
21143 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21144 v.setVisibilityMode(Roo.Element.DISPLAY);
21148 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21149 v.setStyle('width', '189px');
21153 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21154 if(!this.calendarWeeks){
21159 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21160 v.attr('colspan', function(i, val){
21161 return parseInt(val) + 1;
21166 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21168 this.setStartDate(this.startDate);
21169 this.setEndDate(this.endDate);
21171 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21178 if(this.isInline) {
21183 picker : function()
21185 return this.pickerEl;
21186 // return this.el.select('.datepicker', true).first();
21189 fillDow: function()
21191 var dowCnt = this.weekStart;
21200 if(this.calendarWeeks){
21208 while (dowCnt < this.weekStart + 7) {
21212 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21216 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21219 fillMonths: function()
21222 var months = this.picker().select('>.datepicker-months td', true).first();
21224 months.dom.innerHTML = '';
21230 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21233 months.createChild(month);
21240 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;
21242 if (this.date < this.startDate) {
21243 this.viewDate = new Date(this.startDate);
21244 } else if (this.date > this.endDate) {
21245 this.viewDate = new Date(this.endDate);
21247 this.viewDate = new Date(this.date);
21255 var d = new Date(this.viewDate),
21256 year = d.getUTCFullYear(),
21257 month = d.getUTCMonth(),
21258 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21259 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21260 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21261 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21262 currentDate = this.date && this.date.valueOf(),
21263 today = this.UTCToday();
21265 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21267 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21269 // this.picker.select('>tfoot th.today').
21270 // .text(dates[this.language].today)
21271 // .toggle(this.todayBtn !== false);
21273 this.updateNavArrows();
21276 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21278 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21280 prevMonth.setUTCDate(day);
21282 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21284 var nextMonth = new Date(prevMonth);
21286 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21288 nextMonth = nextMonth.valueOf();
21290 var fillMonths = false;
21292 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21294 while(prevMonth.valueOf() <= nextMonth) {
21297 if (prevMonth.getUTCDay() === this.weekStart) {
21299 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21307 if(this.calendarWeeks){
21308 // ISO 8601: First week contains first thursday.
21309 // ISO also states week starts on Monday, but we can be more abstract here.
21311 // Start of current week: based on weekstart/current date
21312 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21313 // Thursday of this week
21314 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21315 // First Thursday of year, year from thursday
21316 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21317 // Calendar week: ms between thursdays, div ms per day, div 7 days
21318 calWeek = (th - yth) / 864e5 / 7 + 1;
21320 fillMonths.cn.push({
21328 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21330 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21333 if (this.todayHighlight &&
21334 prevMonth.getUTCFullYear() == today.getFullYear() &&
21335 prevMonth.getUTCMonth() == today.getMonth() &&
21336 prevMonth.getUTCDate() == today.getDate()) {
21337 clsName += ' today';
21340 if (currentDate && prevMonth.valueOf() === currentDate) {
21341 clsName += ' active';
21344 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21345 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21346 clsName += ' disabled';
21349 fillMonths.cn.push({
21351 cls: 'day ' + clsName,
21352 html: prevMonth.getDate()
21355 prevMonth.setDate(prevMonth.getDate()+1);
21358 var currentYear = this.date && this.date.getUTCFullYear();
21359 var currentMonth = this.date && this.date.getUTCMonth();
21361 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21363 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21364 v.removeClass('active');
21366 if(currentYear === year && k === currentMonth){
21367 v.addClass('active');
21370 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21371 v.addClass('disabled');
21377 year = parseInt(year/10, 10) * 10;
21379 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21381 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21384 for (var i = -1; i < 11; i++) {
21385 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21387 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21395 showMode: function(dir)
21398 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21401 Roo.each(this.picker().select('>div',true).elements, function(v){
21402 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21405 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21410 if(this.isInline) {
21414 this.picker().removeClass(['bottom', 'top']);
21416 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21418 * place to the top of element!
21422 this.picker().addClass('top');
21423 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21428 this.picker().addClass('bottom');
21430 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21433 parseDate : function(value)
21435 if(!value || value instanceof Date){
21438 var v = Date.parseDate(value, this.format);
21439 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21440 v = Date.parseDate(value, 'Y-m-d');
21442 if(!v && this.altFormats){
21443 if(!this.altFormatsArray){
21444 this.altFormatsArray = this.altFormats.split("|");
21446 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21447 v = Date.parseDate(value, this.altFormatsArray[i]);
21453 formatDate : function(date, fmt)
21455 return (!date || !(date instanceof Date)) ?
21456 date : date.dateFormat(fmt || this.format);
21459 onFocus : function()
21461 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21465 onBlur : function()
21467 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21469 var d = this.inputEl().getValue();
21476 showPopup : function()
21478 this.picker().show();
21482 this.fireEvent('showpopup', this, this.date);
21485 hidePopup : function()
21487 if(this.isInline) {
21490 this.picker().hide();
21491 this.viewMode = this.startViewMode;
21494 this.fireEvent('hidepopup', this, this.date);
21498 onMousedown: function(e)
21500 e.stopPropagation();
21501 e.preventDefault();
21506 Roo.bootstrap.DateField.superclass.keyup.call(this);
21510 setValue: function(v)
21512 if(this.fireEvent('beforeselect', this, v) !== false){
21513 var d = new Date(this.parseDate(v) ).clearTime();
21515 if(isNaN(d.getTime())){
21516 this.date = this.viewDate = '';
21517 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21521 v = this.formatDate(d);
21523 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21525 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21529 this.fireEvent('select', this, this.date);
21533 getValue: function()
21535 return this.formatDate(this.date);
21538 fireKey: function(e)
21540 if (!this.picker().isVisible()){
21541 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21547 var dateChanged = false,
21549 newDate, newViewDate;
21554 e.preventDefault();
21558 if (!this.keyboardNavigation) {
21561 dir = e.keyCode == 37 ? -1 : 1;
21564 newDate = this.moveYear(this.date, dir);
21565 newViewDate = this.moveYear(this.viewDate, dir);
21566 } else if (e.shiftKey){
21567 newDate = this.moveMonth(this.date, dir);
21568 newViewDate = this.moveMonth(this.viewDate, dir);
21570 newDate = new Date(this.date);
21571 newDate.setUTCDate(this.date.getUTCDate() + dir);
21572 newViewDate = new Date(this.viewDate);
21573 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21575 if (this.dateWithinRange(newDate)){
21576 this.date = newDate;
21577 this.viewDate = newViewDate;
21578 this.setValue(this.formatDate(this.date));
21580 e.preventDefault();
21581 dateChanged = true;
21586 if (!this.keyboardNavigation) {
21589 dir = e.keyCode == 38 ? -1 : 1;
21591 newDate = this.moveYear(this.date, dir);
21592 newViewDate = this.moveYear(this.viewDate, dir);
21593 } else if (e.shiftKey){
21594 newDate = this.moveMonth(this.date, dir);
21595 newViewDate = this.moveMonth(this.viewDate, dir);
21597 newDate = new Date(this.date);
21598 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21599 newViewDate = new Date(this.viewDate);
21600 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21602 if (this.dateWithinRange(newDate)){
21603 this.date = newDate;
21604 this.viewDate = newViewDate;
21605 this.setValue(this.formatDate(this.date));
21607 e.preventDefault();
21608 dateChanged = true;
21612 this.setValue(this.formatDate(this.date));
21614 e.preventDefault();
21617 this.setValue(this.formatDate(this.date));
21631 onClick: function(e)
21633 e.stopPropagation();
21634 e.preventDefault();
21636 var target = e.getTarget();
21638 if(target.nodeName.toLowerCase() === 'i'){
21639 target = Roo.get(target).dom.parentNode;
21642 var nodeName = target.nodeName;
21643 var className = target.className;
21644 var html = target.innerHTML;
21645 //Roo.log(nodeName);
21647 switch(nodeName.toLowerCase()) {
21649 switch(className) {
21655 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21656 switch(this.viewMode){
21658 this.viewDate = this.moveMonth(this.viewDate, dir);
21662 this.viewDate = this.moveYear(this.viewDate, dir);
21668 var date = new Date();
21669 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21671 this.setValue(this.formatDate(this.date));
21678 if (className.indexOf('disabled') < 0) {
21679 this.viewDate.setUTCDate(1);
21680 if (className.indexOf('month') > -1) {
21681 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21683 var year = parseInt(html, 10) || 0;
21684 this.viewDate.setUTCFullYear(year);
21688 if(this.singleMode){
21689 this.setValue(this.formatDate(this.viewDate));
21700 //Roo.log(className);
21701 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21702 var day = parseInt(html, 10) || 1;
21703 var year = (this.viewDate || new Date()).getUTCFullYear(),
21704 month = (this.viewDate || new Date()).getUTCMonth();
21706 if (className.indexOf('old') > -1) {
21713 } else if (className.indexOf('new') > -1) {
21721 //Roo.log([year,month,day]);
21722 this.date = this.UTCDate(year, month, day,0,0,0,0);
21723 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21725 //Roo.log(this.formatDate(this.date));
21726 this.setValue(this.formatDate(this.date));
21733 setStartDate: function(startDate)
21735 this.startDate = startDate || -Infinity;
21736 if (this.startDate !== -Infinity) {
21737 this.startDate = this.parseDate(this.startDate);
21740 this.updateNavArrows();
21743 setEndDate: function(endDate)
21745 this.endDate = endDate || Infinity;
21746 if (this.endDate !== Infinity) {
21747 this.endDate = this.parseDate(this.endDate);
21750 this.updateNavArrows();
21753 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21755 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21756 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21757 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21759 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21760 return parseInt(d, 10);
21763 this.updateNavArrows();
21766 updateNavArrows: function()
21768 if(this.singleMode){
21772 var d = new Date(this.viewDate),
21773 year = d.getUTCFullYear(),
21774 month = d.getUTCMonth();
21776 Roo.each(this.picker().select('.prev', true).elements, function(v){
21778 switch (this.viewMode) {
21781 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21787 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21794 Roo.each(this.picker().select('.next', true).elements, function(v){
21796 switch (this.viewMode) {
21799 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21805 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21813 moveMonth: function(date, dir)
21818 var new_date = new Date(date.valueOf()),
21819 day = new_date.getUTCDate(),
21820 month = new_date.getUTCMonth(),
21821 mag = Math.abs(dir),
21823 dir = dir > 0 ? 1 : -1;
21826 // If going back one month, make sure month is not current month
21827 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21829 return new_date.getUTCMonth() == month;
21831 // If going forward one month, make sure month is as expected
21832 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21834 return new_date.getUTCMonth() != new_month;
21836 new_month = month + dir;
21837 new_date.setUTCMonth(new_month);
21838 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21839 if (new_month < 0 || new_month > 11) {
21840 new_month = (new_month + 12) % 12;
21843 // For magnitudes >1, move one month at a time...
21844 for (var i=0; i<mag; i++) {
21845 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21846 new_date = this.moveMonth(new_date, dir);
21848 // ...then reset the day, keeping it in the new month
21849 new_month = new_date.getUTCMonth();
21850 new_date.setUTCDate(day);
21852 return new_month != new_date.getUTCMonth();
21855 // Common date-resetting loop -- if date is beyond end of month, make it
21858 new_date.setUTCDate(--day);
21859 new_date.setUTCMonth(new_month);
21864 moveYear: function(date, dir)
21866 return this.moveMonth(date, dir*12);
21869 dateWithinRange: function(date)
21871 return date >= this.startDate && date <= this.endDate;
21877 this.picker().remove();
21880 validateValue : function(value)
21882 if(this.getVisibilityEl().hasClass('hidden')){
21886 if(value.length < 1) {
21887 if(this.allowBlank){
21893 if(value.length < this.minLength){
21896 if(value.length > this.maxLength){
21900 var vt = Roo.form.VTypes;
21901 if(!vt[this.vtype](value, this)){
21905 if(typeof this.validator == "function"){
21906 var msg = this.validator(value);
21912 if(this.regex && !this.regex.test(value)){
21916 if(typeof(this.parseDate(value)) == 'undefined'){
21920 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21924 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21934 this.date = this.viewDate = '';
21936 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21941 Roo.apply(Roo.bootstrap.DateField, {
21952 html: '<i class="fa fa-arrow-left"/>'
21962 html: '<i class="fa fa-arrow-right"/>'
22004 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22005 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22006 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22007 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22008 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22021 navFnc: 'FullYear',
22026 navFnc: 'FullYear',
22031 Roo.apply(Roo.bootstrap.DateField, {
22035 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22039 cls: 'datepicker-days',
22043 cls: 'table-condensed',
22045 Roo.bootstrap.DateField.head,
22049 Roo.bootstrap.DateField.footer
22056 cls: 'datepicker-months',
22060 cls: 'table-condensed',
22062 Roo.bootstrap.DateField.head,
22063 Roo.bootstrap.DateField.content,
22064 Roo.bootstrap.DateField.footer
22071 cls: 'datepicker-years',
22075 cls: 'table-condensed',
22077 Roo.bootstrap.DateField.head,
22078 Roo.bootstrap.DateField.content,
22079 Roo.bootstrap.DateField.footer
22098 * @class Roo.bootstrap.TimeField
22099 * @extends Roo.bootstrap.Input
22100 * Bootstrap DateField class
22104 * Create a new TimeField
22105 * @param {Object} config The config object
22108 Roo.bootstrap.TimeField = function(config){
22109 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22113 * Fires when this field show.
22114 * @param {Roo.bootstrap.DateField} thisthis
22115 * @param {Mixed} date The date value
22120 * Fires when this field hide.
22121 * @param {Roo.bootstrap.DateField} this
22122 * @param {Mixed} date The date value
22127 * Fires when select a date.
22128 * @param {Roo.bootstrap.DateField} this
22129 * @param {Mixed} date The date value
22135 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22138 * @cfg {String} format
22139 * The default time format string which can be overriden for localization support. The format must be
22140 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22144 getAutoCreate : function()
22146 this.after = '<i class="fa far fa-clock"></i>';
22147 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22151 onRender: function(ct, position)
22154 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22156 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22158 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22160 this.pop = this.picker().select('>.datepicker-time',true).first();
22161 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22163 this.picker().on('mousedown', this.onMousedown, this);
22164 this.picker().on('click', this.onClick, this);
22166 this.picker().addClass('datepicker-dropdown');
22171 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22172 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22173 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22174 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22175 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22176 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22180 fireKey: function(e){
22181 if (!this.picker().isVisible()){
22182 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22188 e.preventDefault();
22196 this.onTogglePeriod();
22199 this.onIncrementMinutes();
22202 this.onDecrementMinutes();
22211 onClick: function(e) {
22212 e.stopPropagation();
22213 e.preventDefault();
22216 picker : function()
22218 return this.pickerEl;
22221 fillTime: function()
22223 var time = this.pop.select('tbody', true).first();
22225 time.dom.innerHTML = '';
22240 cls: 'hours-up fa fas fa-chevron-up'
22260 cls: 'minutes-up fa fas fa-chevron-up'
22281 cls: 'timepicker-hour',
22296 cls: 'timepicker-minute',
22311 cls: 'btn btn-primary period',
22333 cls: 'hours-down fa fas fa-chevron-down'
22353 cls: 'minutes-down fa fas fa-chevron-down'
22371 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22378 var hours = this.time.getHours();
22379 var minutes = this.time.getMinutes();
22392 hours = hours - 12;
22396 hours = '0' + hours;
22400 minutes = '0' + minutes;
22403 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22404 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22405 this.pop.select('button', true).first().dom.innerHTML = period;
22411 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22413 var cls = ['bottom'];
22415 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22422 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22426 //this.picker().setXY(20000,20000);
22427 this.picker().addClass(cls.join('-'));
22431 Roo.each(cls, function(c){
22436 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22437 //_this.picker().setTop(_this.inputEl().getHeight());
22441 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22443 //_this.picker().setTop(0 - _this.picker().getHeight());
22448 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22452 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22460 onFocus : function()
22462 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22466 onBlur : function()
22468 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22474 this.picker().show();
22479 this.fireEvent('show', this, this.date);
22484 this.picker().hide();
22487 this.fireEvent('hide', this, this.date);
22490 setTime : function()
22493 this.setValue(this.time.format(this.format));
22495 this.fireEvent('select', this, this.date);
22500 onMousedown: function(e){
22501 e.stopPropagation();
22502 e.preventDefault();
22505 onIncrementHours: function()
22507 Roo.log('onIncrementHours');
22508 this.time = this.time.add(Date.HOUR, 1);
22513 onDecrementHours: function()
22515 Roo.log('onDecrementHours');
22516 this.time = this.time.add(Date.HOUR, -1);
22520 onIncrementMinutes: function()
22522 Roo.log('onIncrementMinutes');
22523 this.time = this.time.add(Date.MINUTE, 1);
22527 onDecrementMinutes: function()
22529 Roo.log('onDecrementMinutes');
22530 this.time = this.time.add(Date.MINUTE, -1);
22534 onTogglePeriod: function()
22536 Roo.log('onTogglePeriod');
22537 this.time = this.time.add(Date.HOUR, 12);
22545 Roo.apply(Roo.bootstrap.TimeField, {
22549 cls: 'datepicker dropdown-menu',
22553 cls: 'datepicker-time',
22557 cls: 'table-condensed',
22586 cls: 'btn btn-info ok',
22614 * @class Roo.bootstrap.MonthField
22615 * @extends Roo.bootstrap.Input
22616 * Bootstrap MonthField class
22618 * @cfg {String} language default en
22621 * Create a new MonthField
22622 * @param {Object} config The config object
22625 Roo.bootstrap.MonthField = function(config){
22626 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22631 * Fires when this field show.
22632 * @param {Roo.bootstrap.MonthField} this
22633 * @param {Mixed} date The date value
22638 * Fires when this field hide.
22639 * @param {Roo.bootstrap.MonthField} this
22640 * @param {Mixed} date The date value
22645 * Fires when select a date.
22646 * @param {Roo.bootstrap.MonthField} this
22647 * @param {String} oldvalue The old value
22648 * @param {String} newvalue The new value
22654 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22656 onRender: function(ct, position)
22659 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22661 this.language = this.language || 'en';
22662 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22663 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22665 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22666 this.isInline = false;
22667 this.isInput = true;
22668 this.component = this.el.select('.add-on', true).first() || false;
22669 this.component = (this.component && this.component.length === 0) ? false : this.component;
22670 this.hasInput = this.component && this.inputEL().length;
22672 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22674 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22676 this.picker().on('mousedown', this.onMousedown, this);
22677 this.picker().on('click', this.onClick, this);
22679 this.picker().addClass('datepicker-dropdown');
22681 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22682 v.setStyle('width', '189px');
22689 if(this.isInline) {
22695 setValue: function(v, suppressEvent)
22697 var o = this.getValue();
22699 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22703 if(suppressEvent !== true){
22704 this.fireEvent('select', this, o, v);
22709 getValue: function()
22714 onClick: function(e)
22716 e.stopPropagation();
22717 e.preventDefault();
22719 var target = e.getTarget();
22721 if(target.nodeName.toLowerCase() === 'i'){
22722 target = Roo.get(target).dom.parentNode;
22725 var nodeName = target.nodeName;
22726 var className = target.className;
22727 var html = target.innerHTML;
22729 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22733 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22735 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22741 picker : function()
22743 return this.pickerEl;
22746 fillMonths: function()
22749 var months = this.picker().select('>.datepicker-months td', true).first();
22751 months.dom.innerHTML = '';
22757 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22760 months.createChild(month);
22769 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22770 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22773 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22774 e.removeClass('active');
22776 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22777 e.addClass('active');
22784 if(this.isInline) {
22788 this.picker().removeClass(['bottom', 'top']);
22790 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22792 * place to the top of element!
22796 this.picker().addClass('top');
22797 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22802 this.picker().addClass('bottom');
22804 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22807 onFocus : function()
22809 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22813 onBlur : function()
22815 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22817 var d = this.inputEl().getValue();
22826 this.picker().show();
22827 this.picker().select('>.datepicker-months', true).first().show();
22831 this.fireEvent('show', this, this.date);
22836 if(this.isInline) {
22839 this.picker().hide();
22840 this.fireEvent('hide', this, this.date);
22844 onMousedown: function(e)
22846 e.stopPropagation();
22847 e.preventDefault();
22852 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22856 fireKey: function(e)
22858 if (!this.picker().isVisible()){
22859 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22870 e.preventDefault();
22874 dir = e.keyCode == 37 ? -1 : 1;
22876 this.vIndex = this.vIndex + dir;
22878 if(this.vIndex < 0){
22882 if(this.vIndex > 11){
22886 if(isNaN(this.vIndex)){
22890 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22896 dir = e.keyCode == 38 ? -1 : 1;
22898 this.vIndex = this.vIndex + dir * 4;
22900 if(this.vIndex < 0){
22904 if(this.vIndex > 11){
22908 if(isNaN(this.vIndex)){
22912 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22917 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22918 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22922 e.preventDefault();
22925 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22926 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22942 this.picker().remove();
22947 Roo.apply(Roo.bootstrap.MonthField, {
22966 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22967 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22972 Roo.apply(Roo.bootstrap.MonthField, {
22976 cls: 'datepicker dropdown-menu roo-dynamic',
22980 cls: 'datepicker-months',
22984 cls: 'table-condensed',
22986 Roo.bootstrap.DateField.content
23006 * @class Roo.bootstrap.CheckBox
23007 * @extends Roo.bootstrap.Input
23008 * Bootstrap CheckBox class
23010 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23011 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23012 * @cfg {String} boxLabel The text that appears beside the checkbox
23013 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23014 * @cfg {Boolean} checked initnal the element
23015 * @cfg {Boolean} inline inline the element (default false)
23016 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23017 * @cfg {String} tooltip label tooltip
23020 * Create a new CheckBox
23021 * @param {Object} config The config object
23024 Roo.bootstrap.CheckBox = function(config){
23025 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23030 * Fires when the element is checked or unchecked.
23031 * @param {Roo.bootstrap.CheckBox} this This input
23032 * @param {Boolean} checked The new checked value
23037 * Fires when the element is click.
23038 * @param {Roo.bootstrap.CheckBox} this This input
23045 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23047 inputType: 'checkbox',
23056 // checkbox success does not make any sense really..
23061 getAutoCreate : function()
23063 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23069 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23072 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23078 type : this.inputType,
23079 value : this.inputValue,
23080 cls : 'roo-' + this.inputType, //'form-box',
23081 placeholder : this.placeholder || ''
23085 if(this.inputType != 'radio'){
23089 cls : 'roo-hidden-value',
23090 value : this.checked ? this.inputValue : this.valueOff
23095 if (this.weight) { // Validity check?
23096 cfg.cls += " " + this.inputType + "-" + this.weight;
23099 if (this.disabled) {
23100 input.disabled=true;
23104 input.checked = this.checked;
23109 input.name = this.name;
23111 if(this.inputType != 'radio'){
23112 hidden.name = this.name;
23113 input.name = '_hidden_' + this.name;
23118 input.cls += ' input-' + this.size;
23123 ['xs','sm','md','lg'].map(function(size){
23124 if (settings[size]) {
23125 cfg.cls += ' col-' + size + '-' + settings[size];
23129 var inputblock = input;
23131 if (this.before || this.after) {
23134 cls : 'input-group',
23139 inputblock.cn.push({
23141 cls : 'input-group-addon',
23146 inputblock.cn.push(input);
23148 if(this.inputType != 'radio'){
23149 inputblock.cn.push(hidden);
23153 inputblock.cn.push({
23155 cls : 'input-group-addon',
23161 var boxLabelCfg = false;
23167 //'for': id, // box label is handled by onclick - so no for...
23169 html: this.boxLabel
23172 boxLabelCfg.tooltip = this.tooltip;
23178 if (align ==='left' && this.fieldLabel.length) {
23179 // Roo.log("left and has label");
23184 cls : 'control-label',
23185 html : this.fieldLabel
23196 cfg.cn[1].cn.push(boxLabelCfg);
23199 if(this.labelWidth > 12){
23200 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23203 if(this.labelWidth < 13 && this.labelmd == 0){
23204 this.labelmd = this.labelWidth;
23207 if(this.labellg > 0){
23208 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23209 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23212 if(this.labelmd > 0){
23213 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23214 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23217 if(this.labelsm > 0){
23218 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23219 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23222 if(this.labelxs > 0){
23223 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23224 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23227 } else if ( this.fieldLabel.length) {
23228 // Roo.log(" label");
23232 tag: this.boxLabel ? 'span' : 'label',
23234 cls: 'control-label box-input-label',
23235 //cls : 'input-group-addon',
23236 html : this.fieldLabel
23243 cfg.cn.push(boxLabelCfg);
23248 // Roo.log(" no label && no align");
23249 cfg.cn = [ inputblock ] ;
23251 cfg.cn.push(boxLabelCfg);
23259 if(this.inputType != 'radio'){
23260 cfg.cn.push(hidden);
23268 * return the real input element.
23270 inputEl: function ()
23272 return this.el.select('input.roo-' + this.inputType,true).first();
23274 hiddenEl: function ()
23276 return this.el.select('input.roo-hidden-value',true).first();
23279 labelEl: function()
23281 return this.el.select('label.control-label',true).first();
23283 /* depricated... */
23287 return this.labelEl();
23290 boxLabelEl: function()
23292 return this.el.select('label.box-label',true).first();
23295 initEvents : function()
23297 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23299 this.inputEl().on('click', this.onClick, this);
23301 if (this.boxLabel) {
23302 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23305 this.startValue = this.getValue();
23308 Roo.bootstrap.CheckBox.register(this);
23312 onClick : function(e)
23314 if(this.fireEvent('click', this, e) !== false){
23315 this.setChecked(!this.checked);
23320 setChecked : function(state,suppressEvent)
23322 this.startValue = this.getValue();
23324 if(this.inputType == 'radio'){
23326 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23327 e.dom.checked = false;
23330 this.inputEl().dom.checked = true;
23332 this.inputEl().dom.value = this.inputValue;
23334 if(suppressEvent !== true){
23335 this.fireEvent('check', this, true);
23343 this.checked = state;
23345 this.inputEl().dom.checked = state;
23348 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23350 if(suppressEvent !== true){
23351 this.fireEvent('check', this, state);
23357 getValue : function()
23359 if(this.inputType == 'radio'){
23360 return this.getGroupValue();
23363 return this.hiddenEl().dom.value;
23367 getGroupValue : function()
23369 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23373 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23376 setValue : function(v,suppressEvent)
23378 if(this.inputType == 'radio'){
23379 this.setGroupValue(v, suppressEvent);
23383 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23388 setGroupValue : function(v, suppressEvent)
23390 this.startValue = this.getValue();
23392 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23393 e.dom.checked = false;
23395 if(e.dom.value == v){
23396 e.dom.checked = true;
23400 if(suppressEvent !== true){
23401 this.fireEvent('check', this, true);
23409 validate : function()
23411 if(this.getVisibilityEl().hasClass('hidden')){
23417 (this.inputType == 'radio' && this.validateRadio()) ||
23418 (this.inputType == 'checkbox' && this.validateCheckbox())
23424 this.markInvalid();
23428 validateRadio : function()
23430 if(this.getVisibilityEl().hasClass('hidden')){
23434 if(this.allowBlank){
23440 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23441 if(!e.dom.checked){
23453 validateCheckbox : function()
23456 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23457 //return (this.getValue() == this.inputValue) ? true : false;
23460 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23468 for(var i in group){
23469 if(group[i].el.isVisible(true)){
23477 for(var i in group){
23482 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23489 * Mark this field as valid
23491 markValid : function()
23495 this.fireEvent('valid', this);
23497 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23500 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23507 if(this.inputType == 'radio'){
23508 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23509 var fg = e.findParent('.form-group', false, true);
23510 if (Roo.bootstrap.version == 3) {
23511 fg.removeClass([_this.invalidClass, _this.validClass]);
23512 fg.addClass(_this.validClass);
23514 fg.removeClass(['is-valid', 'is-invalid']);
23515 fg.addClass('is-valid');
23523 var fg = this.el.findParent('.form-group', false, true);
23524 if (Roo.bootstrap.version == 3) {
23525 fg.removeClass([this.invalidClass, this.validClass]);
23526 fg.addClass(this.validClass);
23528 fg.removeClass(['is-valid', 'is-invalid']);
23529 fg.addClass('is-valid');
23534 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23540 for(var i in group){
23541 var fg = group[i].el.findParent('.form-group', false, true);
23542 if (Roo.bootstrap.version == 3) {
23543 fg.removeClass([this.invalidClass, this.validClass]);
23544 fg.addClass(this.validClass);
23546 fg.removeClass(['is-valid', 'is-invalid']);
23547 fg.addClass('is-valid');
23553 * Mark this field as invalid
23554 * @param {String} msg The validation message
23556 markInvalid : function(msg)
23558 if(this.allowBlank){
23564 this.fireEvent('invalid', this, msg);
23566 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23569 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23573 label.markInvalid();
23576 if(this.inputType == 'radio'){
23578 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23579 var fg = e.findParent('.form-group', false, true);
23580 if (Roo.bootstrap.version == 3) {
23581 fg.removeClass([_this.invalidClass, _this.validClass]);
23582 fg.addClass(_this.invalidClass);
23584 fg.removeClass(['is-invalid', 'is-valid']);
23585 fg.addClass('is-invalid');
23593 var fg = this.el.findParent('.form-group', false, true);
23594 if (Roo.bootstrap.version == 3) {
23595 fg.removeClass([_this.invalidClass, _this.validClass]);
23596 fg.addClass(_this.invalidClass);
23598 fg.removeClass(['is-invalid', 'is-valid']);
23599 fg.addClass('is-invalid');
23604 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23610 for(var i in group){
23611 var fg = group[i].el.findParent('.form-group', false, true);
23612 if (Roo.bootstrap.version == 3) {
23613 fg.removeClass([_this.invalidClass, _this.validClass]);
23614 fg.addClass(_this.invalidClass);
23616 fg.removeClass(['is-invalid', 'is-valid']);
23617 fg.addClass('is-invalid');
23623 clearInvalid : function()
23625 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23627 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23629 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23631 if (label && label.iconEl) {
23632 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23633 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23637 disable : function()
23639 if(this.inputType != 'radio'){
23640 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23647 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23648 _this.getActionEl().addClass(this.disabledClass);
23649 e.dom.disabled = true;
23653 this.disabled = true;
23654 this.fireEvent("disable", this);
23658 enable : function()
23660 if(this.inputType != 'radio'){
23661 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23668 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23669 _this.getActionEl().removeClass(this.disabledClass);
23670 e.dom.disabled = false;
23674 this.disabled = false;
23675 this.fireEvent("enable", this);
23679 setBoxLabel : function(v)
23684 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23690 Roo.apply(Roo.bootstrap.CheckBox, {
23695 * register a CheckBox Group
23696 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23698 register : function(checkbox)
23700 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23701 this.groups[checkbox.groupId] = {};
23704 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23708 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23712 * fetch a CheckBox Group based on the group ID
23713 * @param {string} the group ID
23714 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23716 get: function(groupId) {
23717 if (typeof(this.groups[groupId]) == 'undefined') {
23721 return this.groups[groupId] ;
23734 * @class Roo.bootstrap.Radio
23735 * @extends Roo.bootstrap.Component
23736 * Bootstrap Radio class
23737 * @cfg {String} boxLabel - the label associated
23738 * @cfg {String} value - the value of radio
23741 * Create a new Radio
23742 * @param {Object} config The config object
23744 Roo.bootstrap.Radio = function(config){
23745 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23749 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23755 getAutoCreate : function()
23759 cls : 'form-group radio',
23764 html : this.boxLabel
23772 initEvents : function()
23774 this.parent().register(this);
23776 this.el.on('click', this.onClick, this);
23780 onClick : function(e)
23782 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23783 this.setChecked(true);
23787 setChecked : function(state, suppressEvent)
23789 this.parent().setValue(this.value, suppressEvent);
23793 setBoxLabel : function(v)
23798 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23813 * @class Roo.bootstrap.SecurePass
23814 * @extends Roo.bootstrap.Input
23815 * Bootstrap SecurePass class
23819 * Create a new SecurePass
23820 * @param {Object} config The config object
23823 Roo.bootstrap.SecurePass = function (config) {
23824 // these go here, so the translation tool can replace them..
23826 PwdEmpty: "Please type a password, and then retype it to confirm.",
23827 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23828 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23829 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23830 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23831 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23832 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23833 TooWeak: "Your password is Too Weak."
23835 this.meterLabel = "Password strength:";
23836 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23837 this.meterClass = [
23838 "roo-password-meter-tooweak",
23839 "roo-password-meter-weak",
23840 "roo-password-meter-medium",
23841 "roo-password-meter-strong",
23842 "roo-password-meter-grey"
23847 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23850 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23852 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23854 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23855 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23856 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23857 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23858 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23859 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23860 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23870 * @cfg {String/Object} Label for the strength meter (defaults to
23871 * 'Password strength:')
23876 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23877 * ['Weak', 'Medium', 'Strong'])
23880 pwdStrengths: false,
23893 initEvents: function ()
23895 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23897 if (this.el.is('input[type=password]') && Roo.isSafari) {
23898 this.el.on('keydown', this.SafariOnKeyDown, this);
23901 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23904 onRender: function (ct, position)
23906 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23907 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23908 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23910 this.trigger.createChild({
23915 cls: 'roo-password-meter-grey col-xs-12',
23918 //width: this.meterWidth + 'px'
23922 cls: 'roo-password-meter-text'
23928 if (this.hideTrigger) {
23929 this.trigger.setDisplayed(false);
23931 this.setSize(this.width || '', this.height || '');
23934 onDestroy: function ()
23936 if (this.trigger) {
23937 this.trigger.removeAllListeners();
23938 this.trigger.remove();
23941 this.wrap.remove();
23943 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23946 checkStrength: function ()
23948 var pwd = this.inputEl().getValue();
23949 if (pwd == this._lastPwd) {
23954 if (this.ClientSideStrongPassword(pwd)) {
23956 } else if (this.ClientSideMediumPassword(pwd)) {
23958 } else if (this.ClientSideWeakPassword(pwd)) {
23964 Roo.log('strength1: ' + strength);
23966 //var pm = this.trigger.child('div/div/div').dom;
23967 var pm = this.trigger.child('div/div');
23968 pm.removeClass(this.meterClass);
23969 pm.addClass(this.meterClass[strength]);
23972 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23974 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23976 this._lastPwd = pwd;
23980 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23982 this._lastPwd = '';
23984 var pm = this.trigger.child('div/div');
23985 pm.removeClass(this.meterClass);
23986 pm.addClass('roo-password-meter-grey');
23989 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23992 this.inputEl().dom.type='password';
23995 validateValue: function (value)
23997 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24000 if (value.length == 0) {
24001 if (this.allowBlank) {
24002 this.clearInvalid();
24006 this.markInvalid(this.errors.PwdEmpty);
24007 this.errorMsg = this.errors.PwdEmpty;
24015 if (!value.match(/[\x21-\x7e]+/)) {
24016 this.markInvalid(this.errors.PwdBadChar);
24017 this.errorMsg = this.errors.PwdBadChar;
24020 if (value.length < 6) {
24021 this.markInvalid(this.errors.PwdShort);
24022 this.errorMsg = this.errors.PwdShort;
24025 if (value.length > 16) {
24026 this.markInvalid(this.errors.PwdLong);
24027 this.errorMsg = this.errors.PwdLong;
24031 if (this.ClientSideStrongPassword(value)) {
24033 } else if (this.ClientSideMediumPassword(value)) {
24035 } else if (this.ClientSideWeakPassword(value)) {
24042 if (strength < 2) {
24043 //this.markInvalid(this.errors.TooWeak);
24044 this.errorMsg = this.errors.TooWeak;
24049 console.log('strength2: ' + strength);
24051 //var pm = this.trigger.child('div/div/div').dom;
24053 var pm = this.trigger.child('div/div');
24054 pm.removeClass(this.meterClass);
24055 pm.addClass(this.meterClass[strength]);
24057 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24059 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24061 this.errorMsg = '';
24065 CharacterSetChecks: function (type)
24068 this.fResult = false;
24071 isctype: function (character, type)
24074 case this.kCapitalLetter:
24075 if (character >= 'A' && character <= 'Z') {
24080 case this.kSmallLetter:
24081 if (character >= 'a' && character <= 'z') {
24087 if (character >= '0' && character <= '9') {
24092 case this.kPunctuation:
24093 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24104 IsLongEnough: function (pwd, size)
24106 return !(pwd == null || isNaN(size) || pwd.length < size);
24109 SpansEnoughCharacterSets: function (word, nb)
24111 if (!this.IsLongEnough(word, nb))
24116 var characterSetChecks = new Array(
24117 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24118 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24121 for (var index = 0; index < word.length; ++index) {
24122 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24123 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24124 characterSetChecks[nCharSet].fResult = true;
24131 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24132 if (characterSetChecks[nCharSet].fResult) {
24137 if (nCharSets < nb) {
24143 ClientSideStrongPassword: function (pwd)
24145 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24148 ClientSideMediumPassword: function (pwd)
24150 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24153 ClientSideWeakPassword: function (pwd)
24155 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24158 })//<script type="text/javascript">
24161 * Based Ext JS Library 1.1.1
24162 * Copyright(c) 2006-2007, Ext JS, LLC.
24168 * @class Roo.HtmlEditorCore
24169 * @extends Roo.Component
24170 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24172 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24175 Roo.HtmlEditorCore = function(config){
24178 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24183 * @event initialize
24184 * Fires when the editor is fully initialized (including the iframe)
24185 * @param {Roo.HtmlEditorCore} this
24190 * Fires when the editor is first receives the focus. Any insertion must wait
24191 * until after this event.
24192 * @param {Roo.HtmlEditorCore} this
24196 * @event beforesync
24197 * Fires before the textarea is updated with content from the editor iframe. Return false
24198 * to cancel the sync.
24199 * @param {Roo.HtmlEditorCore} this
24200 * @param {String} html
24204 * @event beforepush
24205 * Fires before the iframe editor is updated with content from the textarea. Return false
24206 * to cancel the push.
24207 * @param {Roo.HtmlEditorCore} this
24208 * @param {String} html
24213 * Fires when the textarea is updated with content from the editor iframe.
24214 * @param {Roo.HtmlEditorCore} this
24215 * @param {String} html
24220 * Fires when the iframe editor is updated with content from the textarea.
24221 * @param {Roo.HtmlEditorCore} this
24222 * @param {String} html
24227 * @event editorevent
24228 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24229 * @param {Roo.HtmlEditorCore} this
24235 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24237 // defaults : white / black...
24238 this.applyBlacklists();
24245 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24249 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24255 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24260 * @cfg {Number} height (in pixels)
24264 * @cfg {Number} width (in pixels)
24269 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24272 stylesheets: false,
24277 // private properties
24278 validationEvent : false,
24280 initialized : false,
24282 sourceEditMode : false,
24283 onFocus : Roo.emptyFn,
24285 hideMode:'offsets',
24289 // blacklist + whitelisted elements..
24296 * Protected method that will not generally be called directly. It
24297 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24298 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24300 getDocMarkup : function(){
24304 // inherit styels from page...??
24305 if (this.stylesheets === false) {
24307 Roo.get(document.head).select('style').each(function(node) {
24308 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24311 Roo.get(document.head).select('link').each(function(node) {
24312 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24315 } else if (!this.stylesheets.length) {
24317 st = '<style type="text/css">' +
24318 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24321 for (var i in this.stylesheets) {
24322 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24327 st += '<style type="text/css">' +
24328 'IMG { cursor: pointer } ' +
24331 var cls = 'roo-htmleditor-body';
24333 if(this.bodyCls.length){
24334 cls += ' ' + this.bodyCls;
24337 return '<html><head>' + st +
24338 //<style type="text/css">' +
24339 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24341 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24345 onRender : function(ct, position)
24348 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24349 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24352 this.el.dom.style.border = '0 none';
24353 this.el.dom.setAttribute('tabIndex', -1);
24354 this.el.addClass('x-hidden hide');
24358 if(Roo.isIE){ // fix IE 1px bogus margin
24359 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24363 this.frameId = Roo.id();
24367 var iframe = this.owner.wrap.createChild({
24369 cls: 'form-control', // bootstrap..
24371 name: this.frameId,
24372 frameBorder : 'no',
24373 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24378 this.iframe = iframe.dom;
24380 this.assignDocWin();
24382 this.doc.designMode = 'on';
24385 this.doc.write(this.getDocMarkup());
24389 var task = { // must defer to wait for browser to be ready
24391 //console.log("run task?" + this.doc.readyState);
24392 this.assignDocWin();
24393 if(this.doc.body || this.doc.readyState == 'complete'){
24395 this.doc.designMode="on";
24399 Roo.TaskMgr.stop(task);
24400 this.initEditor.defer(10, this);
24407 Roo.TaskMgr.start(task);
24412 onResize : function(w, h)
24414 Roo.log('resize: ' +w + ',' + h );
24415 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24419 if(typeof w == 'number'){
24421 this.iframe.style.width = w + 'px';
24423 if(typeof h == 'number'){
24425 this.iframe.style.height = h + 'px';
24427 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24434 * Toggles the editor between standard and source edit mode.
24435 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24437 toggleSourceEdit : function(sourceEditMode){
24439 this.sourceEditMode = sourceEditMode === true;
24441 if(this.sourceEditMode){
24443 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24446 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24447 //this.iframe.className = '';
24450 //this.setSize(this.owner.wrap.getSize());
24451 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24458 * Protected method that will not generally be called directly. If you need/want
24459 * custom HTML cleanup, this is the method you should override.
24460 * @param {String} html The HTML to be cleaned
24461 * return {String} The cleaned HTML
24463 cleanHtml : function(html){
24464 html = String(html);
24465 if(html.length > 5){
24466 if(Roo.isSafari){ // strip safari nonsense
24467 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24470 if(html == ' '){
24477 * HTML Editor -> Textarea
24478 * Protected method that will not generally be called directly. Syncs the contents
24479 * of the editor iframe with the textarea.
24481 syncValue : function(){
24482 if(this.initialized){
24483 var bd = (this.doc.body || this.doc.documentElement);
24484 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24485 var html = bd.innerHTML;
24487 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24488 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24490 html = '<div style="'+m[0]+'">' + html + '</div>';
24493 html = this.cleanHtml(html);
24494 // fix up the special chars.. normaly like back quotes in word...
24495 // however we do not want to do this with chinese..
24496 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24498 var cc = match.charCodeAt();
24500 // Get the character value, handling surrogate pairs
24501 if (match.length == 2) {
24502 // It's a surrogate pair, calculate the Unicode code point
24503 var high = match.charCodeAt(0) - 0xD800;
24504 var low = match.charCodeAt(1) - 0xDC00;
24505 cc = (high * 0x400) + low + 0x10000;
24507 (cc >= 0x4E00 && cc < 0xA000 ) ||
24508 (cc >= 0x3400 && cc < 0x4E00 ) ||
24509 (cc >= 0xf900 && cc < 0xfb00 )
24514 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24515 return "&#" + cc + ";";
24522 if(this.owner.fireEvent('beforesync', this, html) !== false){
24523 this.el.dom.value = html;
24524 this.owner.fireEvent('sync', this, html);
24530 * Protected method that will not generally be called directly. Pushes the value of the textarea
24531 * into the iframe editor.
24533 pushValue : function(){
24534 if(this.initialized){
24535 var v = this.el.dom.value.trim();
24537 // if(v.length < 1){
24541 if(this.owner.fireEvent('beforepush', this, v) !== false){
24542 var d = (this.doc.body || this.doc.documentElement);
24544 this.cleanUpPaste();
24545 this.el.dom.value = d.innerHTML;
24546 this.owner.fireEvent('push', this, v);
24552 deferFocus : function(){
24553 this.focus.defer(10, this);
24557 focus : function(){
24558 if(this.win && !this.sourceEditMode){
24565 assignDocWin: function()
24567 var iframe = this.iframe;
24570 this.doc = iframe.contentWindow.document;
24571 this.win = iframe.contentWindow;
24573 // if (!Roo.get(this.frameId)) {
24576 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24577 // this.win = Roo.get(this.frameId).dom.contentWindow;
24579 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24583 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24584 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24589 initEditor : function(){
24590 //console.log("INIT EDITOR");
24591 this.assignDocWin();
24595 this.doc.designMode="on";
24597 this.doc.write(this.getDocMarkup());
24600 var dbody = (this.doc.body || this.doc.documentElement);
24601 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24602 // this copies styles from the containing element into thsi one..
24603 // not sure why we need all of this..
24604 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24606 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24607 //ss['background-attachment'] = 'fixed'; // w3c
24608 dbody.bgProperties = 'fixed'; // ie
24609 //Roo.DomHelper.applyStyles(dbody, ss);
24610 Roo.EventManager.on(this.doc, {
24611 //'mousedown': this.onEditorEvent,
24612 'mouseup': this.onEditorEvent,
24613 'dblclick': this.onEditorEvent,
24614 'click': this.onEditorEvent,
24615 'keyup': this.onEditorEvent,
24620 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24622 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24623 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24625 this.initialized = true;
24627 this.owner.fireEvent('initialize', this);
24632 onDestroy : function(){
24638 //for (var i =0; i < this.toolbars.length;i++) {
24639 // // fixme - ask toolbars for heights?
24640 // this.toolbars[i].onDestroy();
24643 //this.wrap.dom.innerHTML = '';
24644 //this.wrap.remove();
24649 onFirstFocus : function(){
24651 this.assignDocWin();
24654 this.activated = true;
24657 if(Roo.isGecko){ // prevent silly gecko errors
24659 var s = this.win.getSelection();
24660 if(!s.focusNode || s.focusNode.nodeType != 3){
24661 var r = s.getRangeAt(0);
24662 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24667 this.execCmd('useCSS', true);
24668 this.execCmd('styleWithCSS', false);
24671 this.owner.fireEvent('activate', this);
24675 adjustFont: function(btn){
24676 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24677 //if(Roo.isSafari){ // safari
24680 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24681 if(Roo.isSafari){ // safari
24682 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24683 v = (v < 10) ? 10 : v;
24684 v = (v > 48) ? 48 : v;
24685 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24690 v = Math.max(1, v+adjust);
24692 this.execCmd('FontSize', v );
24695 onEditorEvent : function(e)
24697 this.owner.fireEvent('editorevent', this, e);
24698 // this.updateToolbar();
24699 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24702 insertTag : function(tg)
24704 // could be a bit smarter... -> wrap the current selected tRoo..
24705 if (tg.toLowerCase() == 'span' ||
24706 tg.toLowerCase() == 'code' ||
24707 tg.toLowerCase() == 'sup' ||
24708 tg.toLowerCase() == 'sub'
24711 range = this.createRange(this.getSelection());
24712 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24713 wrappingNode.appendChild(range.extractContents());
24714 range.insertNode(wrappingNode);
24721 this.execCmd("formatblock", tg);
24725 insertText : function(txt)
24729 var range = this.createRange();
24730 range.deleteContents();
24731 //alert(Sender.getAttribute('label'));
24733 range.insertNode(this.doc.createTextNode(txt));
24739 * Executes a Midas editor command on the editor document and performs necessary focus and
24740 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24741 * @param {String} cmd The Midas command
24742 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24744 relayCmd : function(cmd, value){
24746 this.execCmd(cmd, value);
24747 this.owner.fireEvent('editorevent', this);
24748 //this.updateToolbar();
24749 this.owner.deferFocus();
24753 * Executes a Midas editor command directly on the editor document.
24754 * For visual commands, you should use {@link #relayCmd} instead.
24755 * <b>This should only be called after the editor is initialized.</b>
24756 * @param {String} cmd The Midas command
24757 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24759 execCmd : function(cmd, value){
24760 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24767 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24769 * @param {String} text | dom node..
24771 insertAtCursor : function(text)
24774 if(!this.activated){
24780 var r = this.doc.selection.createRange();
24791 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24795 // from jquery ui (MIT licenced)
24797 var win = this.win;
24799 if (win.getSelection && win.getSelection().getRangeAt) {
24800 range = win.getSelection().getRangeAt(0);
24801 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24802 range.insertNode(node);
24803 } else if (win.document.selection && win.document.selection.createRange) {
24804 // no firefox support
24805 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24806 win.document.selection.createRange().pasteHTML(txt);
24808 // no firefox support
24809 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24810 this.execCmd('InsertHTML', txt);
24819 mozKeyPress : function(e){
24821 var c = e.getCharCode(), cmd;
24824 c = String.fromCharCode(c).toLowerCase();
24838 this.cleanUpPaste.defer(100, this);
24846 e.preventDefault();
24854 fixKeys : function(){ // load time branching for fastest keydown performance
24856 return function(e){
24857 var k = e.getKey(), r;
24860 r = this.doc.selection.createRange();
24863 r.pasteHTML('    ');
24870 r = this.doc.selection.createRange();
24872 var target = r.parentElement();
24873 if(!target || target.tagName.toLowerCase() != 'li'){
24875 r.pasteHTML('<br />');
24881 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24882 this.cleanUpPaste.defer(100, this);
24888 }else if(Roo.isOpera){
24889 return function(e){
24890 var k = e.getKey();
24894 this.execCmd('InsertHTML','    ');
24897 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24898 this.cleanUpPaste.defer(100, this);
24903 }else if(Roo.isSafari){
24904 return function(e){
24905 var k = e.getKey();
24909 this.execCmd('InsertText','\t');
24913 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24914 this.cleanUpPaste.defer(100, this);
24922 getAllAncestors: function()
24924 var p = this.getSelectedNode();
24927 a.push(p); // push blank onto stack..
24928 p = this.getParentElement();
24932 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24936 a.push(this.doc.body);
24940 lastSelNode : false,
24943 getSelection : function()
24945 this.assignDocWin();
24946 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24949 getSelectedNode: function()
24951 // this may only work on Gecko!!!
24953 // should we cache this!!!!
24958 var range = this.createRange(this.getSelection()).cloneRange();
24961 var parent = range.parentElement();
24963 var testRange = range.duplicate();
24964 testRange.moveToElementText(parent);
24965 if (testRange.inRange(range)) {
24968 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24971 parent = parent.parentElement;
24976 // is ancestor a text element.
24977 var ac = range.commonAncestorContainer;
24978 if (ac.nodeType == 3) {
24979 ac = ac.parentNode;
24982 var ar = ac.childNodes;
24985 var other_nodes = [];
24986 var has_other_nodes = false;
24987 for (var i=0;i<ar.length;i++) {
24988 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
24991 // fullly contained node.
24993 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24998 // probably selected..
24999 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25000 other_nodes.push(ar[i]);
25004 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25009 has_other_nodes = true;
25011 if (!nodes.length && other_nodes.length) {
25012 nodes= other_nodes;
25014 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25020 createRange: function(sel)
25022 // this has strange effects when using with
25023 // top toolbar - not sure if it's a great idea.
25024 //this.editor.contentWindow.focus();
25025 if (typeof sel != "undefined") {
25027 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25029 return this.doc.createRange();
25032 return this.doc.createRange();
25035 getParentElement: function()
25038 this.assignDocWin();
25039 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25041 var range = this.createRange(sel);
25044 var p = range.commonAncestorContainer;
25045 while (p.nodeType == 3) { // text node
25056 * Range intersection.. the hard stuff...
25060 * [ -- selected range --- ]
25064 * if end is before start or hits it. fail.
25065 * if start is after end or hits it fail.
25067 * if either hits (but other is outside. - then it's not
25073 // @see http://www.thismuchiknow.co.uk/?p=64.
25074 rangeIntersectsNode : function(range, node)
25076 var nodeRange = node.ownerDocument.createRange();
25078 nodeRange.selectNode(node);
25080 nodeRange.selectNodeContents(node);
25083 var rangeStartRange = range.cloneRange();
25084 rangeStartRange.collapse(true);
25086 var rangeEndRange = range.cloneRange();
25087 rangeEndRange.collapse(false);
25089 var nodeStartRange = nodeRange.cloneRange();
25090 nodeStartRange.collapse(true);
25092 var nodeEndRange = nodeRange.cloneRange();
25093 nodeEndRange.collapse(false);
25095 return rangeStartRange.compareBoundaryPoints(
25096 Range.START_TO_START, nodeEndRange) == -1 &&
25097 rangeEndRange.compareBoundaryPoints(
25098 Range.START_TO_START, nodeStartRange) == 1;
25102 rangeCompareNode : function(range, node)
25104 var nodeRange = node.ownerDocument.createRange();
25106 nodeRange.selectNode(node);
25108 nodeRange.selectNodeContents(node);
25112 range.collapse(true);
25114 nodeRange.collapse(true);
25116 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25117 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25119 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25121 var nodeIsBefore = ss == 1;
25122 var nodeIsAfter = ee == -1;
25124 if (nodeIsBefore && nodeIsAfter) {
25127 if (!nodeIsBefore && nodeIsAfter) {
25128 return 1; //right trailed.
25131 if (nodeIsBefore && !nodeIsAfter) {
25132 return 2; // left trailed.
25138 // private? - in a new class?
25139 cleanUpPaste : function()
25141 // cleans up the whole document..
25142 Roo.log('cleanuppaste');
25144 this.cleanUpChildren(this.doc.body);
25145 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25146 if (clean != this.doc.body.innerHTML) {
25147 this.doc.body.innerHTML = clean;
25152 cleanWordChars : function(input) {// change the chars to hex code
25153 var he = Roo.HtmlEditorCore;
25155 var output = input;
25156 Roo.each(he.swapCodes, function(sw) {
25157 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25159 output = output.replace(swapper, sw[1]);
25166 cleanUpChildren : function (n)
25168 if (!n.childNodes.length) {
25171 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25172 this.cleanUpChild(n.childNodes[i]);
25179 cleanUpChild : function (node)
25182 //console.log(node);
25183 if (node.nodeName == "#text") {
25184 // clean up silly Windows -- stuff?
25187 if (node.nodeName == "#comment") {
25188 node.parentNode.removeChild(node);
25189 // clean up silly Windows -- stuff?
25192 var lcname = node.tagName.toLowerCase();
25193 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25194 // whitelist of tags..
25196 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25198 node.parentNode.removeChild(node);
25203 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25205 // spans with no attributes - just remove them..
25206 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25207 remove_keep_children = true;
25210 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25211 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25213 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25214 // remove_keep_children = true;
25217 if (remove_keep_children) {
25218 this.cleanUpChildren(node);
25219 // inserts everything just before this node...
25220 while (node.childNodes.length) {
25221 var cn = node.childNodes[0];
25222 node.removeChild(cn);
25223 node.parentNode.insertBefore(cn, node);
25225 node.parentNode.removeChild(node);
25229 if (!node.attributes || !node.attributes.length) {
25234 this.cleanUpChildren(node);
25238 function cleanAttr(n,v)
25241 if (v.match(/^\./) || v.match(/^\//)) {
25244 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25247 if (v.match(/^#/)) {
25250 if (v.match(/^\{/)) { // allow template editing.
25253 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25254 node.removeAttribute(n);
25258 var cwhite = this.cwhite;
25259 var cblack = this.cblack;
25261 function cleanStyle(n,v)
25263 if (v.match(/expression/)) { //XSS?? should we even bother..
25264 node.removeAttribute(n);
25268 var parts = v.split(/;/);
25271 Roo.each(parts, function(p) {
25272 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25276 var l = p.split(':').shift().replace(/\s+/g,'');
25277 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25279 if ( cwhite.length && cblack.indexOf(l) > -1) {
25280 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25281 //node.removeAttribute(n);
25285 // only allow 'c whitelisted system attributes'
25286 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25287 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25288 //node.removeAttribute(n);
25298 if (clean.length) {
25299 node.setAttribute(n, clean.join(';'));
25301 node.removeAttribute(n);
25307 for (var i = node.attributes.length-1; i > -1 ; i--) {
25308 var a = node.attributes[i];
25311 if (a.name.toLowerCase().substr(0,2)=='on') {
25312 node.removeAttribute(a.name);
25315 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25316 node.removeAttribute(a.name);
25319 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25320 cleanAttr(a.name,a.value); // fixme..
25323 if (a.name == 'style') {
25324 cleanStyle(a.name,a.value);
25327 /// clean up MS crap..
25328 // tecnically this should be a list of valid class'es..
25331 if (a.name == 'class') {
25332 if (a.value.match(/^Mso/)) {
25333 node.removeAttribute('class');
25336 if (a.value.match(/^body$/)) {
25337 node.removeAttribute('class');
25348 this.cleanUpChildren(node);
25354 * Clean up MS wordisms...
25356 cleanWord : function(node)
25359 this.cleanWord(this.doc.body);
25364 node.nodeName == 'SPAN' &&
25365 !node.hasAttributes() &&
25366 node.childNodes.length == 1 &&
25367 node.firstChild.nodeName == "#text"
25369 var textNode = node.firstChild;
25370 node.removeChild(textNode);
25371 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25372 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25374 node.parentNode.insertBefore(textNode, node);
25375 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25376 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25378 node.parentNode.removeChild(node);
25381 if (node.nodeName == "#text") {
25382 // clean up silly Windows -- stuff?
25385 if (node.nodeName == "#comment") {
25386 node.parentNode.removeChild(node);
25387 // clean up silly Windows -- stuff?
25391 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25392 node.parentNode.removeChild(node);
25395 //Roo.log(node.tagName);
25396 // remove - but keep children..
25397 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25398 //Roo.log('-- removed');
25399 while (node.childNodes.length) {
25400 var cn = node.childNodes[0];
25401 node.removeChild(cn);
25402 node.parentNode.insertBefore(cn, node);
25403 // move node to parent - and clean it..
25404 this.cleanWord(cn);
25406 node.parentNode.removeChild(node);
25407 /// no need to iterate chidlren = it's got none..
25408 //this.iterateChildren(node, this.cleanWord);
25412 if (node.className.length) {
25414 var cn = node.className.split(/\W+/);
25416 Roo.each(cn, function(cls) {
25417 if (cls.match(/Mso[a-zA-Z]+/)) {
25422 node.className = cna.length ? cna.join(' ') : '';
25424 node.removeAttribute("class");
25428 if (node.hasAttribute("lang")) {
25429 node.removeAttribute("lang");
25432 if (node.hasAttribute("style")) {
25434 var styles = node.getAttribute("style").split(";");
25436 Roo.each(styles, function(s) {
25437 if (!s.match(/:/)) {
25440 var kv = s.split(":");
25441 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25444 // what ever is left... we allow.
25447 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25448 if (!nstyle.length) {
25449 node.removeAttribute('style');
25452 this.iterateChildren(node, this.cleanWord);
25458 * iterateChildren of a Node, calling fn each time, using this as the scole..
25459 * @param {DomNode} node node to iterate children of.
25460 * @param {Function} fn method of this class to call on each item.
25462 iterateChildren : function(node, fn)
25464 if (!node.childNodes.length) {
25467 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25468 fn.call(this, node.childNodes[i])
25474 * cleanTableWidths.
25476 * Quite often pasting from word etc.. results in tables with column and widths.
25477 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25480 cleanTableWidths : function(node)
25485 this.cleanTableWidths(this.doc.body);
25490 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25493 Roo.log(node.tagName);
25494 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25495 this.iterateChildren(node, this.cleanTableWidths);
25498 if (node.hasAttribute('width')) {
25499 node.removeAttribute('width');
25503 if (node.hasAttribute("style")) {
25506 var styles = node.getAttribute("style").split(";");
25508 Roo.each(styles, function(s) {
25509 if (!s.match(/:/)) {
25512 var kv = s.split(":");
25513 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25516 // what ever is left... we allow.
25519 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25520 if (!nstyle.length) {
25521 node.removeAttribute('style');
25525 this.iterateChildren(node, this.cleanTableWidths);
25533 domToHTML : function(currentElement, depth, nopadtext) {
25535 depth = depth || 0;
25536 nopadtext = nopadtext || false;
25538 if (!currentElement) {
25539 return this.domToHTML(this.doc.body);
25542 //Roo.log(currentElement);
25544 var allText = false;
25545 var nodeName = currentElement.nodeName;
25546 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25548 if (nodeName == '#text') {
25550 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25555 if (nodeName != 'BODY') {
25558 // Prints the node tagName, such as <A>, <IMG>, etc
25561 for(i = 0; i < currentElement.attributes.length;i++) {
25563 var aname = currentElement.attributes.item(i).name;
25564 if (!currentElement.attributes.item(i).value.length) {
25567 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25570 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25579 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25582 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25587 // Traverse the tree
25589 var currentElementChild = currentElement.childNodes.item(i);
25590 var allText = true;
25591 var innerHTML = '';
25593 while (currentElementChild) {
25594 // Formatting code (indent the tree so it looks nice on the screen)
25595 var nopad = nopadtext;
25596 if (lastnode == 'SPAN') {
25600 if (currentElementChild.nodeName == '#text') {
25601 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25602 toadd = nopadtext ? toadd : toadd.trim();
25603 if (!nopad && toadd.length > 80) {
25604 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25606 innerHTML += toadd;
25609 currentElementChild = currentElement.childNodes.item(i);
25615 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25617 // Recursively traverse the tree structure of the child node
25618 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25619 lastnode = currentElementChild.nodeName;
25621 currentElementChild=currentElement.childNodes.item(i);
25627 // The remaining code is mostly for formatting the tree
25628 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25633 ret+= "</"+tagName+">";
25639 applyBlacklists : function()
25641 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25642 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25646 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25647 if (b.indexOf(tag) > -1) {
25650 this.white.push(tag);
25654 Roo.each(w, function(tag) {
25655 if (b.indexOf(tag) > -1) {
25658 if (this.white.indexOf(tag) > -1) {
25661 this.white.push(tag);
25666 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25667 if (w.indexOf(tag) > -1) {
25670 this.black.push(tag);
25674 Roo.each(b, function(tag) {
25675 if (w.indexOf(tag) > -1) {
25678 if (this.black.indexOf(tag) > -1) {
25681 this.black.push(tag);
25686 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25687 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25691 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25692 if (b.indexOf(tag) > -1) {
25695 this.cwhite.push(tag);
25699 Roo.each(w, function(tag) {
25700 if (b.indexOf(tag) > -1) {
25703 if (this.cwhite.indexOf(tag) > -1) {
25706 this.cwhite.push(tag);
25711 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25712 if (w.indexOf(tag) > -1) {
25715 this.cblack.push(tag);
25719 Roo.each(b, function(tag) {
25720 if (w.indexOf(tag) > -1) {
25723 if (this.cblack.indexOf(tag) > -1) {
25726 this.cblack.push(tag);
25731 setStylesheets : function(stylesheets)
25733 if(typeof(stylesheets) == 'string'){
25734 Roo.get(this.iframe.contentDocument.head).createChild({
25736 rel : 'stylesheet',
25745 Roo.each(stylesheets, function(s) {
25750 Roo.get(_this.iframe.contentDocument.head).createChild({
25752 rel : 'stylesheet',
25761 removeStylesheets : function()
25765 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25770 setStyle : function(style)
25772 Roo.get(this.iframe.contentDocument.head).createChild({
25781 // hide stuff that is not compatible
25795 * @event specialkey
25799 * @cfg {String} fieldClass @hide
25802 * @cfg {String} focusClass @hide
25805 * @cfg {String} autoCreate @hide
25808 * @cfg {String} inputType @hide
25811 * @cfg {String} invalidClass @hide
25814 * @cfg {String} invalidText @hide
25817 * @cfg {String} msgFx @hide
25820 * @cfg {String} validateOnBlur @hide
25824 Roo.HtmlEditorCore.white = [
25825 'area', 'br', 'img', 'input', 'hr', 'wbr',
25827 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25828 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25829 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25830 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25831 'table', 'ul', 'xmp',
25833 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25836 'dir', 'menu', 'ol', 'ul', 'dl',
25842 Roo.HtmlEditorCore.black = [
25843 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25845 'base', 'basefont', 'bgsound', 'blink', 'body',
25846 'frame', 'frameset', 'head', 'html', 'ilayer',
25847 'iframe', 'layer', 'link', 'meta', 'object',
25848 'script', 'style' ,'title', 'xml' // clean later..
25850 Roo.HtmlEditorCore.clean = [
25851 'script', 'style', 'title', 'xml'
25853 Roo.HtmlEditorCore.remove = [
25858 Roo.HtmlEditorCore.ablack = [
25862 Roo.HtmlEditorCore.aclean = [
25863 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25867 Roo.HtmlEditorCore.pwhite= [
25868 'http', 'https', 'mailto'
25871 // white listed style attributes.
25872 Roo.HtmlEditorCore.cwhite= [
25873 // 'text-align', /// default is to allow most things..
25879 // black listed style attributes.
25880 Roo.HtmlEditorCore.cblack= [
25881 // 'font-size' -- this can be set by the project
25885 Roo.HtmlEditorCore.swapCodes =[
25886 [ 8211, "–" ],
25887 [ 8212, "—" ],
25904 * @class Roo.bootstrap.HtmlEditor
25905 * @extends Roo.bootstrap.TextArea
25906 * Bootstrap HtmlEditor class
25909 * Create a new HtmlEditor
25910 * @param {Object} config The config object
25913 Roo.bootstrap.HtmlEditor = function(config){
25914 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25915 if (!this.toolbars) {
25916 this.toolbars = [];
25919 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25922 * @event initialize
25923 * Fires when the editor is fully initialized (including the iframe)
25924 * @param {HtmlEditor} this
25929 * Fires when the editor is first receives the focus. Any insertion must wait
25930 * until after this event.
25931 * @param {HtmlEditor} this
25935 * @event beforesync
25936 * Fires before the textarea is updated with content from the editor iframe. Return false
25937 * to cancel the sync.
25938 * @param {HtmlEditor} this
25939 * @param {String} html
25943 * @event beforepush
25944 * Fires before the iframe editor is updated with content from the textarea. Return false
25945 * to cancel the push.
25946 * @param {HtmlEditor} this
25947 * @param {String} html
25952 * Fires when the textarea is updated with content from the editor iframe.
25953 * @param {HtmlEditor} this
25954 * @param {String} html
25959 * Fires when the iframe editor is updated with content from the textarea.
25960 * @param {HtmlEditor} this
25961 * @param {String} html
25965 * @event editmodechange
25966 * Fires when the editor switches edit modes
25967 * @param {HtmlEditor} this
25968 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25970 editmodechange: true,
25972 * @event editorevent
25973 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25974 * @param {HtmlEditor} this
25978 * @event firstfocus
25979 * Fires when on first focus - needed by toolbars..
25980 * @param {HtmlEditor} this
25985 * Auto save the htmlEditor value as a file into Events
25986 * @param {HtmlEditor} this
25990 * @event savedpreview
25991 * preview the saved version of htmlEditor
25992 * @param {HtmlEditor} this
25999 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26003 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26008 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26013 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26018 * @cfg {Number} height (in pixels)
26022 * @cfg {Number} width (in pixels)
26027 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26030 stylesheets: false,
26035 // private properties
26036 validationEvent : false,
26038 initialized : false,
26041 onFocus : Roo.emptyFn,
26043 hideMode:'offsets',
26045 tbContainer : false,
26049 toolbarContainer :function() {
26050 return this.wrap.select('.x-html-editor-tb',true).first();
26054 * Protected method that will not generally be called directly. It
26055 * is called when the editor creates its toolbar. Override this method if you need to
26056 * add custom toolbar buttons.
26057 * @param {HtmlEditor} editor
26059 createToolbar : function(){
26060 Roo.log('renewing');
26061 Roo.log("create toolbars");
26063 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26064 this.toolbars[0].render(this.toolbarContainer());
26068 // if (!editor.toolbars || !editor.toolbars.length) {
26069 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26072 // for (var i =0 ; i < editor.toolbars.length;i++) {
26073 // editor.toolbars[i] = Roo.factory(
26074 // typeof(editor.toolbars[i]) == 'string' ?
26075 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26076 // Roo.bootstrap.HtmlEditor);
26077 // editor.toolbars[i].init(editor);
26083 onRender : function(ct, position)
26085 // Roo.log("Call onRender: " + this.xtype);
26087 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26089 this.wrap = this.inputEl().wrap({
26090 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26093 this.editorcore.onRender(ct, position);
26095 if (this.resizable) {
26096 this.resizeEl = new Roo.Resizable(this.wrap, {
26100 minHeight : this.height,
26101 height: this.height,
26102 handles : this.resizable,
26105 resize : function(r, w, h) {
26106 _t.onResize(w,h); // -something
26112 this.createToolbar(this);
26115 if(!this.width && this.resizable){
26116 this.setSize(this.wrap.getSize());
26118 if (this.resizeEl) {
26119 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26120 // should trigger onReize..
26126 onResize : function(w, h)
26128 Roo.log('resize: ' +w + ',' + h );
26129 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26133 if(this.inputEl() ){
26134 if(typeof w == 'number'){
26135 var aw = w - this.wrap.getFrameWidth('lr');
26136 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26139 if(typeof h == 'number'){
26140 var tbh = -11; // fixme it needs to tool bar size!
26141 for (var i =0; i < this.toolbars.length;i++) {
26142 // fixme - ask toolbars for heights?
26143 tbh += this.toolbars[i].el.getHeight();
26144 //if (this.toolbars[i].footer) {
26145 // tbh += this.toolbars[i].footer.el.getHeight();
26153 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26154 ah -= 5; // knock a few pixes off for look..
26155 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26159 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26160 this.editorcore.onResize(ew,eh);
26165 * Toggles the editor between standard and source edit mode.
26166 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26168 toggleSourceEdit : function(sourceEditMode)
26170 this.editorcore.toggleSourceEdit(sourceEditMode);
26172 if(this.editorcore.sourceEditMode){
26173 Roo.log('editor - showing textarea');
26176 // Roo.log(this.syncValue());
26178 this.inputEl().removeClass(['hide', 'x-hidden']);
26179 this.inputEl().dom.removeAttribute('tabIndex');
26180 this.inputEl().focus();
26182 Roo.log('editor - hiding textarea');
26184 // Roo.log(this.pushValue());
26187 this.inputEl().addClass(['hide', 'x-hidden']);
26188 this.inputEl().dom.setAttribute('tabIndex', -1);
26189 //this.deferFocus();
26192 if(this.resizable){
26193 this.setSize(this.wrap.getSize());
26196 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26199 // private (for BoxComponent)
26200 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26202 // private (for BoxComponent)
26203 getResizeEl : function(){
26207 // private (for BoxComponent)
26208 getPositionEl : function(){
26213 initEvents : function(){
26214 this.originalValue = this.getValue();
26218 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26221 // markInvalid : Roo.emptyFn,
26223 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26226 // clearInvalid : Roo.emptyFn,
26228 setValue : function(v){
26229 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26230 this.editorcore.pushValue();
26235 deferFocus : function(){
26236 this.focus.defer(10, this);
26240 focus : function(){
26241 this.editorcore.focus();
26247 onDestroy : function(){
26253 for (var i =0; i < this.toolbars.length;i++) {
26254 // fixme - ask toolbars for heights?
26255 this.toolbars[i].onDestroy();
26258 this.wrap.dom.innerHTML = '';
26259 this.wrap.remove();
26264 onFirstFocus : function(){
26265 //Roo.log("onFirstFocus");
26266 this.editorcore.onFirstFocus();
26267 for (var i =0; i < this.toolbars.length;i++) {
26268 this.toolbars[i].onFirstFocus();
26274 syncValue : function()
26276 this.editorcore.syncValue();
26279 pushValue : function()
26281 this.editorcore.pushValue();
26285 // hide stuff that is not compatible
26299 * @event specialkey
26303 * @cfg {String} fieldClass @hide
26306 * @cfg {String} focusClass @hide
26309 * @cfg {String} autoCreate @hide
26312 * @cfg {String} inputType @hide
26316 * @cfg {String} invalidText @hide
26319 * @cfg {String} msgFx @hide
26322 * @cfg {String} validateOnBlur @hide
26331 Roo.namespace('Roo.bootstrap.htmleditor');
26333 * @class Roo.bootstrap.HtmlEditorToolbar1
26339 new Roo.bootstrap.HtmlEditor({
26342 new Roo.bootstrap.HtmlEditorToolbar1({
26343 disable : { fonts: 1 , format: 1, ..., ... , ...],
26349 * @cfg {Object} disable List of elements to disable..
26350 * @cfg {Array} btns List of additional buttons.
26354 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26357 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26360 Roo.apply(this, config);
26362 // default disabled, based on 'good practice'..
26363 this.disable = this.disable || {};
26364 Roo.applyIf(this.disable, {
26367 specialElements : true
26369 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26371 this.editor = config.editor;
26372 this.editorcore = config.editor.editorcore;
26374 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26376 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26377 // dont call parent... till later.
26379 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26384 editorcore : false,
26389 "h1","h2","h3","h4","h5","h6",
26391 "abbr", "acronym", "address", "cite", "samp", "var",
26395 onRender : function(ct, position)
26397 // Roo.log("Call onRender: " + this.xtype);
26399 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26401 this.el.dom.style.marginBottom = '0';
26403 var editorcore = this.editorcore;
26404 var editor= this.editor;
26407 var btn = function(id,cmd , toggle, handler, html){
26409 var event = toggle ? 'toggle' : 'click';
26414 xns: Roo.bootstrap,
26418 enableToggle:toggle !== false,
26420 pressed : toggle ? false : null,
26423 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26424 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26430 // var cb_box = function...
26435 xns: Roo.bootstrap,
26440 xns: Roo.bootstrap,
26444 Roo.each(this.formats, function(f) {
26445 style.menu.items.push({
26447 xns: Roo.bootstrap,
26448 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26453 editorcore.insertTag(this.tagname);
26460 children.push(style);
26462 btn('bold',false,true);
26463 btn('italic',false,true);
26464 btn('align-left', 'justifyleft',true);
26465 btn('align-center', 'justifycenter',true);
26466 btn('align-right' , 'justifyright',true);
26467 btn('link', false, false, function(btn) {
26468 //Roo.log("create link?");
26469 var url = prompt(this.createLinkText, this.defaultLinkValue);
26470 if(url && url != 'http:/'+'/'){
26471 this.editorcore.relayCmd('createlink', url);
26474 btn('list','insertunorderedlist',true);
26475 btn('pencil', false,true, function(btn){
26477 this.toggleSourceEdit(btn.pressed);
26480 if (this.editor.btns.length > 0) {
26481 for (var i = 0; i<this.editor.btns.length; i++) {
26482 children.push(this.editor.btns[i]);
26490 xns: Roo.bootstrap,
26495 xns: Roo.bootstrap,
26500 cog.menu.items.push({
26502 xns: Roo.bootstrap,
26503 html : Clean styles,
26508 editorcore.insertTag(this.tagname);
26517 this.xtype = 'NavSimplebar';
26519 for(var i=0;i< children.length;i++) {
26521 this.buttons.add(this.addxtypeChild(children[i]));
26525 editor.on('editorevent', this.updateToolbar, this);
26527 onBtnClick : function(id)
26529 this.editorcore.relayCmd(id);
26530 this.editorcore.focus();
26534 * Protected method that will not generally be called directly. It triggers
26535 * a toolbar update by reading the markup state of the current selection in the editor.
26537 updateToolbar: function(){
26539 if(!this.editorcore.activated){
26540 this.editor.onFirstFocus(); // is this neeed?
26544 var btns = this.buttons;
26545 var doc = this.editorcore.doc;
26546 btns.get('bold').setActive(doc.queryCommandState('bold'));
26547 btns.get('italic').setActive(doc.queryCommandState('italic'));
26548 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26550 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26551 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26552 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26554 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26555 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26558 var ans = this.editorcore.getAllAncestors();
26559 if (this.formatCombo) {
26562 var store = this.formatCombo.store;
26563 this.formatCombo.setValue("");
26564 for (var i =0; i < ans.length;i++) {
26565 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26567 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26575 // hides menus... - so this cant be on a menu...
26576 Roo.bootstrap.MenuMgr.hideAll();
26578 Roo.bootstrap.MenuMgr.hideAll();
26579 //this.editorsyncValue();
26581 onFirstFocus: function() {
26582 this.buttons.each(function(item){
26586 toggleSourceEdit : function(sourceEditMode){
26589 if(sourceEditMode){
26590 Roo.log("disabling buttons");
26591 this.buttons.each( function(item){
26592 if(item.cmd != 'pencil'){
26598 Roo.log("enabling buttons");
26599 if(this.editorcore.initialized){
26600 this.buttons.each( function(item){
26606 Roo.log("calling toggole on editor");
26607 // tell the editor that it's been pressed..
26608 this.editor.toggleSourceEdit(sourceEditMode);
26622 * @class Roo.bootstrap.Markdown
26623 * @extends Roo.bootstrap.TextArea
26624 * Bootstrap Showdown editable area
26625 * @cfg {string} content
26628 * Create a new Showdown
26631 Roo.bootstrap.Markdown = function(config){
26632 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26636 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26640 initEvents : function()
26643 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26644 this.markdownEl = this.el.createChild({
26645 cls : 'roo-markdown-area'
26647 this.inputEl().addClass('d-none');
26648 if (this.getValue() == '') {
26649 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26652 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26654 this.markdownEl.on('click', this.toggleTextEdit, this);
26655 this.on('blur', this.toggleTextEdit, this);
26656 this.on('specialkey', this.resizeTextArea, this);
26659 toggleTextEdit : function()
26661 var sh = this.markdownEl.getHeight();
26662 this.inputEl().addClass('d-none');
26663 this.markdownEl.addClass('d-none');
26664 if (!this.editing) {
26666 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26667 this.inputEl().removeClass('d-none');
26668 this.inputEl().focus();
26669 this.editing = true;
26672 // show showdown...
26673 this.updateMarkdown();
26674 this.markdownEl.removeClass('d-none');
26675 this.editing = false;
26678 updateMarkdown : function()
26680 if (this.getValue() == '') {
26681 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26685 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26688 resizeTextArea: function () {
26691 Roo.log([sh, this.getValue().split("\n").length * 30]);
26692 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26694 setValue : function(val)
26696 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26697 if (!this.editing) {
26698 this.updateMarkdown();
26704 if (!this.editing) {
26705 this.toggleTextEdit();
26713 * @class Roo.bootstrap.Table.AbstractSelectionModel
26714 * @extends Roo.util.Observable
26715 * Abstract base class for grid SelectionModels. It provides the interface that should be
26716 * implemented by descendant classes. This class should not be directly instantiated.
26719 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26720 this.locked = false;
26721 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26725 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26726 /** @ignore Called by the grid automatically. Do not call directly. */
26727 init : function(grid){
26733 * Locks the selections.
26736 this.locked = true;
26740 * Unlocks the selections.
26742 unlock : function(){
26743 this.locked = false;
26747 * Returns true if the selections are locked.
26748 * @return {Boolean}
26750 isLocked : function(){
26751 return this.locked;
26755 initEvents : function ()
26761 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26762 * @class Roo.bootstrap.Table.RowSelectionModel
26763 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26764 * It supports multiple selections and keyboard selection/navigation.
26766 * @param {Object} config
26769 Roo.bootstrap.Table.RowSelectionModel = function(config){
26770 Roo.apply(this, config);
26771 this.selections = new Roo.util.MixedCollection(false, function(o){
26776 this.lastActive = false;
26780 * @event selectionchange
26781 * Fires when the selection changes
26782 * @param {SelectionModel} this
26784 "selectionchange" : true,
26786 * @event afterselectionchange
26787 * Fires after the selection changes (eg. by key press or clicking)
26788 * @param {SelectionModel} this
26790 "afterselectionchange" : true,
26792 * @event beforerowselect
26793 * Fires when a row is selected being selected, return false to cancel.
26794 * @param {SelectionModel} this
26795 * @param {Number} rowIndex The selected index
26796 * @param {Boolean} keepExisting False if other selections will be cleared
26798 "beforerowselect" : true,
26801 * Fires when a row is selected.
26802 * @param {SelectionModel} this
26803 * @param {Number} rowIndex The selected index
26804 * @param {Roo.data.Record} r The record
26806 "rowselect" : true,
26808 * @event rowdeselect
26809 * Fires when a row is deselected.
26810 * @param {SelectionModel} this
26811 * @param {Number} rowIndex The selected index
26813 "rowdeselect" : true
26815 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26816 this.locked = false;
26819 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26821 * @cfg {Boolean} singleSelect
26822 * True to allow selection of only one row at a time (defaults to false)
26824 singleSelect : false,
26827 initEvents : function()
26830 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26831 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26832 //}else{ // allow click to work like normal
26833 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26835 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26836 this.grid.on("rowclick", this.handleMouseDown, this);
26838 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26839 "up" : function(e){
26841 this.selectPrevious(e.shiftKey);
26842 }else if(this.last !== false && this.lastActive !== false){
26843 var last = this.last;
26844 this.selectRange(this.last, this.lastActive-1);
26845 this.grid.getView().focusRow(this.lastActive);
26846 if(last !== false){
26850 this.selectFirstRow();
26852 this.fireEvent("afterselectionchange", this);
26854 "down" : function(e){
26856 this.selectNext(e.shiftKey);
26857 }else if(this.last !== false && this.lastActive !== false){
26858 var last = this.last;
26859 this.selectRange(this.last, this.lastActive+1);
26860 this.grid.getView().focusRow(this.lastActive);
26861 if(last !== false){
26865 this.selectFirstRow();
26867 this.fireEvent("afterselectionchange", this);
26871 this.grid.store.on('load', function(){
26872 this.selections.clear();
26875 var view = this.grid.view;
26876 view.on("refresh", this.onRefresh, this);
26877 view.on("rowupdated", this.onRowUpdated, this);
26878 view.on("rowremoved", this.onRemove, this);
26883 onRefresh : function()
26885 var ds = this.grid.store, i, v = this.grid.view;
26886 var s = this.selections;
26887 s.each(function(r){
26888 if((i = ds.indexOfId(r.id)) != -1){
26897 onRemove : function(v, index, r){
26898 this.selections.remove(r);
26902 onRowUpdated : function(v, index, r){
26903 if(this.isSelected(r)){
26904 v.onRowSelect(index);
26910 * @param {Array} records The records to select
26911 * @param {Boolean} keepExisting (optional) True to keep existing selections
26913 selectRecords : function(records, keepExisting)
26916 this.clearSelections();
26918 var ds = this.grid.store;
26919 for(var i = 0, len = records.length; i < len; i++){
26920 this.selectRow(ds.indexOf(records[i]), true);
26925 * Gets the number of selected rows.
26928 getCount : function(){
26929 return this.selections.length;
26933 * Selects the first row in the grid.
26935 selectFirstRow : function(){
26940 * Select the last row.
26941 * @param {Boolean} keepExisting (optional) True to keep existing selections
26943 selectLastRow : function(keepExisting){
26944 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26945 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26949 * Selects the row immediately following the last selected row.
26950 * @param {Boolean} keepExisting (optional) True to keep existing selections
26952 selectNext : function(keepExisting)
26954 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26955 this.selectRow(this.last+1, keepExisting);
26956 this.grid.getView().focusRow(this.last);
26961 * Selects the row that precedes the last selected row.
26962 * @param {Boolean} keepExisting (optional) True to keep existing selections
26964 selectPrevious : function(keepExisting){
26966 this.selectRow(this.last-1, keepExisting);
26967 this.grid.getView().focusRow(this.last);
26972 * Returns the selected records
26973 * @return {Array} Array of selected records
26975 getSelections : function(){
26976 return [].concat(this.selections.items);
26980 * Returns the first selected record.
26983 getSelected : function(){
26984 return this.selections.itemAt(0);
26989 * Clears all selections.
26991 clearSelections : function(fast)
26997 var ds = this.grid.store;
26998 var s = this.selections;
26999 s.each(function(r){
27000 this.deselectRow(ds.indexOfId(r.id));
27004 this.selections.clear();
27011 * Selects all rows.
27013 selectAll : function(){
27017 this.selections.clear();
27018 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27019 this.selectRow(i, true);
27024 * Returns True if there is a selection.
27025 * @return {Boolean}
27027 hasSelection : function(){
27028 return this.selections.length > 0;
27032 * Returns True if the specified row is selected.
27033 * @param {Number/Record} record The record or index of the record to check
27034 * @return {Boolean}
27036 isSelected : function(index){
27037 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27038 return (r && this.selections.key(r.id) ? true : false);
27042 * Returns True if the specified record id is selected.
27043 * @param {String} id The id of record to check
27044 * @return {Boolean}
27046 isIdSelected : function(id){
27047 return (this.selections.key(id) ? true : false);
27052 handleMouseDBClick : function(e, t){
27056 handleMouseDown : function(e, t)
27058 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27059 if(this.isLocked() || rowIndex < 0 ){
27062 if(e.shiftKey && this.last !== false){
27063 var last = this.last;
27064 this.selectRange(last, rowIndex, e.ctrlKey);
27065 this.last = last; // reset the last
27069 var isSelected = this.isSelected(rowIndex);
27070 //Roo.log("select row:" + rowIndex);
27072 this.deselectRow(rowIndex);
27074 this.selectRow(rowIndex, true);
27078 if(e.button !== 0 && isSelected){
27079 alert('rowIndex 2: ' + rowIndex);
27080 view.focusRow(rowIndex);
27081 }else if(e.ctrlKey && isSelected){
27082 this.deselectRow(rowIndex);
27083 }else if(!isSelected){
27084 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27085 view.focusRow(rowIndex);
27089 this.fireEvent("afterselectionchange", this);
27092 handleDragableRowClick : function(grid, rowIndex, e)
27094 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27095 this.selectRow(rowIndex, false);
27096 grid.view.focusRow(rowIndex);
27097 this.fireEvent("afterselectionchange", this);
27102 * Selects multiple rows.
27103 * @param {Array} rows Array of the indexes of the row to select
27104 * @param {Boolean} keepExisting (optional) True to keep existing selections
27106 selectRows : function(rows, keepExisting){
27108 this.clearSelections();
27110 for(var i = 0, len = rows.length; i < len; i++){
27111 this.selectRow(rows[i], true);
27116 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27117 * @param {Number} startRow The index of the first row in the range
27118 * @param {Number} endRow The index of the last row in the range
27119 * @param {Boolean} keepExisting (optional) True to retain existing selections
27121 selectRange : function(startRow, endRow, keepExisting){
27126 this.clearSelections();
27128 if(startRow <= endRow){
27129 for(var i = startRow; i <= endRow; i++){
27130 this.selectRow(i, true);
27133 for(var i = startRow; i >= endRow; i--){
27134 this.selectRow(i, true);
27140 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27141 * @param {Number} startRow The index of the first row in the range
27142 * @param {Number} endRow The index of the last row in the range
27144 deselectRange : function(startRow, endRow, preventViewNotify){
27148 for(var i = startRow; i <= endRow; i++){
27149 this.deselectRow(i, preventViewNotify);
27155 * @param {Number} row The index of the row to select
27156 * @param {Boolean} keepExisting (optional) True to keep existing selections
27158 selectRow : function(index, keepExisting, preventViewNotify)
27160 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27163 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27164 if(!keepExisting || this.singleSelect){
27165 this.clearSelections();
27168 var r = this.grid.store.getAt(index);
27169 //console.log('selectRow - record id :' + r.id);
27171 this.selections.add(r);
27172 this.last = this.lastActive = index;
27173 if(!preventViewNotify){
27174 var proxy = new Roo.Element(
27175 this.grid.getRowDom(index)
27177 proxy.addClass('bg-info info');
27179 this.fireEvent("rowselect", this, index, r);
27180 this.fireEvent("selectionchange", this);
27186 * @param {Number} row The index of the row to deselect
27188 deselectRow : function(index, preventViewNotify)
27193 if(this.last == index){
27196 if(this.lastActive == index){
27197 this.lastActive = false;
27200 var r = this.grid.store.getAt(index);
27205 this.selections.remove(r);
27206 //.console.log('deselectRow - record id :' + r.id);
27207 if(!preventViewNotify){
27209 var proxy = new Roo.Element(
27210 this.grid.getRowDom(index)
27212 proxy.removeClass('bg-info info');
27214 this.fireEvent("rowdeselect", this, index);
27215 this.fireEvent("selectionchange", this);
27219 restoreLast : function(){
27221 this.last = this._last;
27226 acceptsNav : function(row, col, cm){
27227 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27231 onEditorKey : function(field, e){
27232 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27237 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27239 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27241 }else if(k == e.ENTER && !e.ctrlKey){
27245 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27247 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27249 }else if(k == e.ESC){
27253 g.startEditing(newCell[0], newCell[1]);
27259 * Ext JS Library 1.1.1
27260 * Copyright(c) 2006-2007, Ext JS, LLC.
27262 * Originally Released Under LGPL - original licence link has changed is not relivant.
27265 * <script type="text/javascript">
27269 * @class Roo.bootstrap.PagingToolbar
27270 * @extends Roo.bootstrap.NavSimplebar
27271 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27273 * Create a new PagingToolbar
27274 * @param {Object} config The config object
27275 * @param {Roo.data.Store} store
27277 Roo.bootstrap.PagingToolbar = function(config)
27279 // old args format still supported... - xtype is prefered..
27280 // created from xtype...
27282 this.ds = config.dataSource;
27284 if (config.store && !this.ds) {
27285 this.store= Roo.factory(config.store, Roo.data);
27286 this.ds = this.store;
27287 this.ds.xmodule = this.xmodule || false;
27290 this.toolbarItems = [];
27291 if (config.items) {
27292 this.toolbarItems = config.items;
27295 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27300 this.bind(this.ds);
27303 if (Roo.bootstrap.version == 4) {
27304 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27306 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27311 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27313 * @cfg {Roo.data.Store} dataSource
27314 * The underlying data store providing the paged data
27317 * @cfg {String/HTMLElement/Element} container
27318 * container The id or element that will contain the toolbar
27321 * @cfg {Boolean} displayInfo
27322 * True to display the displayMsg (defaults to false)
27325 * @cfg {Number} pageSize
27326 * The number of records to display per page (defaults to 20)
27330 * @cfg {String} displayMsg
27331 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27333 displayMsg : 'Displaying {0} - {1} of {2}',
27335 * @cfg {String} emptyMsg
27336 * The message to display when no records are found (defaults to "No data to display")
27338 emptyMsg : 'No data to display',
27340 * Customizable piece of the default paging text (defaults to "Page")
27343 beforePageText : "Page",
27345 * Customizable piece of the default paging text (defaults to "of %0")
27348 afterPageText : "of {0}",
27350 * Customizable piece of the default paging text (defaults to "First Page")
27353 firstText : "First Page",
27355 * Customizable piece of the default paging text (defaults to "Previous Page")
27358 prevText : "Previous Page",
27360 * Customizable piece of the default paging text (defaults to "Next Page")
27363 nextText : "Next Page",
27365 * Customizable piece of the default paging text (defaults to "Last Page")
27368 lastText : "Last Page",
27370 * Customizable piece of the default paging text (defaults to "Refresh")
27373 refreshText : "Refresh",
27377 onRender : function(ct, position)
27379 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27380 this.navgroup.parentId = this.id;
27381 this.navgroup.onRender(this.el, null);
27382 // add the buttons to the navgroup
27384 if(this.displayInfo){
27385 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27386 this.displayEl = this.el.select('.x-paging-info', true).first();
27387 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27388 // this.displayEl = navel.el.select('span',true).first();
27394 Roo.each(_this.buttons, function(e){ // this might need to use render????
27395 Roo.factory(e).render(_this.el);
27399 Roo.each(_this.toolbarItems, function(e) {
27400 _this.navgroup.addItem(e);
27404 this.first = this.navgroup.addItem({
27405 tooltip: this.firstText,
27406 cls: "prev btn-outline-secondary",
27407 html : ' <i class="fa fa-step-backward"></i>',
27409 preventDefault: true,
27410 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27413 this.prev = this.navgroup.addItem({
27414 tooltip: this.prevText,
27415 cls: "prev btn-outline-secondary",
27416 html : ' <i class="fa fa-backward"></i>',
27418 preventDefault: true,
27419 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27421 //this.addSeparator();
27424 var field = this.navgroup.addItem( {
27426 cls : 'x-paging-position btn-outline-secondary',
27428 html : this.beforePageText +
27429 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27430 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27433 this.field = field.el.select('input', true).first();
27434 this.field.on("keydown", this.onPagingKeydown, this);
27435 this.field.on("focus", function(){this.dom.select();});
27438 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27439 //this.field.setHeight(18);
27440 //this.addSeparator();
27441 this.next = this.navgroup.addItem({
27442 tooltip: this.nextText,
27443 cls: "next btn-outline-secondary",
27444 html : ' <i class="fa fa-forward"></i>',
27446 preventDefault: true,
27447 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27449 this.last = this.navgroup.addItem({
27450 tooltip: this.lastText,
27451 html : ' <i class="fa fa-step-forward"></i>',
27452 cls: "next btn-outline-secondary",
27454 preventDefault: true,
27455 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27457 //this.addSeparator();
27458 this.loading = this.navgroup.addItem({
27459 tooltip: this.refreshText,
27460 cls: "btn-outline-secondary",
27461 html : ' <i class="fa fa-refresh"></i>',
27462 preventDefault: true,
27463 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27469 updateInfo : function(){
27470 if(this.displayEl){
27471 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27472 var msg = count == 0 ?
27476 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27478 this.displayEl.update(msg);
27483 onLoad : function(ds, r, o)
27485 this.cursor = o.params && o.params.start ? o.params.start : 0;
27487 var d = this.getPageData(),
27492 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27493 this.field.dom.value = ap;
27494 this.first.setDisabled(ap == 1);
27495 this.prev.setDisabled(ap == 1);
27496 this.next.setDisabled(ap == ps);
27497 this.last.setDisabled(ap == ps);
27498 this.loading.enable();
27503 getPageData : function(){
27504 var total = this.ds.getTotalCount();
27507 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27508 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27513 onLoadError : function(){
27514 this.loading.enable();
27518 onPagingKeydown : function(e){
27519 var k = e.getKey();
27520 var d = this.getPageData();
27522 var v = this.field.dom.value, pageNum;
27523 if(!v || isNaN(pageNum = parseInt(v, 10))){
27524 this.field.dom.value = d.activePage;
27527 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27528 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27531 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))
27533 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27534 this.field.dom.value = pageNum;
27535 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27538 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27540 var v = this.field.dom.value, pageNum;
27541 var increment = (e.shiftKey) ? 10 : 1;
27542 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27545 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27546 this.field.dom.value = d.activePage;
27549 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27551 this.field.dom.value = parseInt(v, 10) + increment;
27552 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27553 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27560 beforeLoad : function(){
27562 this.loading.disable();
27567 onClick : function(which){
27576 ds.load({params:{start: 0, limit: this.pageSize}});
27579 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27582 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27585 var total = ds.getTotalCount();
27586 var extra = total % this.pageSize;
27587 var lastStart = extra ? (total - extra) : total-this.pageSize;
27588 ds.load({params:{start: lastStart, limit: this.pageSize}});
27591 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27597 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27598 * @param {Roo.data.Store} store The data store to unbind
27600 unbind : function(ds){
27601 ds.un("beforeload", this.beforeLoad, this);
27602 ds.un("load", this.onLoad, this);
27603 ds.un("loadexception", this.onLoadError, this);
27604 ds.un("remove", this.updateInfo, this);
27605 ds.un("add", this.updateInfo, this);
27606 this.ds = undefined;
27610 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27611 * @param {Roo.data.Store} store The data store to bind
27613 bind : function(ds){
27614 ds.on("beforeload", this.beforeLoad, this);
27615 ds.on("load", this.onLoad, this);
27616 ds.on("loadexception", this.onLoadError, this);
27617 ds.on("remove", this.updateInfo, this);
27618 ds.on("add", this.updateInfo, this);
27629 * @class Roo.bootstrap.MessageBar
27630 * @extends Roo.bootstrap.Component
27631 * Bootstrap MessageBar class
27632 * @cfg {String} html contents of the MessageBar
27633 * @cfg {String} weight (info | success | warning | danger) default info
27634 * @cfg {String} beforeClass insert the bar before the given class
27635 * @cfg {Boolean} closable (true | false) default false
27636 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27639 * Create a new Element
27640 * @param {Object} config The config object
27643 Roo.bootstrap.MessageBar = function(config){
27644 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27647 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27653 beforeClass: 'bootstrap-sticky-wrap',
27655 getAutoCreate : function(){
27659 cls: 'alert alert-dismissable alert-' + this.weight,
27664 html: this.html || ''
27670 cfg.cls += ' alert-messages-fixed';
27684 onRender : function(ct, position)
27686 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27689 var cfg = Roo.apply({}, this.getAutoCreate());
27693 cfg.cls += ' ' + this.cls;
27696 cfg.style = this.style;
27698 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27700 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27703 this.el.select('>button.close').on('click', this.hide, this);
27709 if (!this.rendered) {
27715 this.fireEvent('show', this);
27721 if (!this.rendered) {
27727 this.fireEvent('hide', this);
27730 update : function()
27732 // var e = this.el.dom.firstChild;
27734 // if(this.closable){
27735 // e = e.nextSibling;
27738 // e.data = this.html || '';
27740 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27756 * @class Roo.bootstrap.Graph
27757 * @extends Roo.bootstrap.Component
27758 * Bootstrap Graph class
27762 @cfg {String} graphtype bar | vbar | pie
27763 @cfg {number} g_x coodinator | centre x (pie)
27764 @cfg {number} g_y coodinator | centre y (pie)
27765 @cfg {number} g_r radius (pie)
27766 @cfg {number} g_height height of the chart (respected by all elements in the set)
27767 @cfg {number} g_width width of the chart (respected by all elements in the set)
27768 @cfg {Object} title The title of the chart
27771 -opts (object) options for the chart
27773 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27774 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27776 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.
27777 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27779 o stretch (boolean)
27781 -opts (object) options for the pie
27784 o startAngle (number)
27785 o endAngle (number)
27789 * Create a new Input
27790 * @param {Object} config The config object
27793 Roo.bootstrap.Graph = function(config){
27794 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27800 * The img click event for the img.
27801 * @param {Roo.EventObject} e
27807 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27818 //g_colors: this.colors,
27825 getAutoCreate : function(){
27836 onRender : function(ct,position){
27839 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27841 if (typeof(Raphael) == 'undefined') {
27842 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27846 this.raphael = Raphael(this.el.dom);
27848 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27849 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27850 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27851 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27853 r.text(160, 10, "Single Series Chart").attr(txtattr);
27854 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27855 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27856 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27858 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27859 r.barchart(330, 10, 300, 220, data1);
27860 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27861 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27864 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27865 // r.barchart(30, 30, 560, 250, xdata, {
27866 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27867 // axis : "0 0 1 1",
27868 // axisxlabels : xdata
27869 // //yvalues : cols,
27872 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27874 // this.load(null,xdata,{
27875 // axis : "0 0 1 1",
27876 // axisxlabels : xdata
27881 load : function(graphtype,xdata,opts)
27883 this.raphael.clear();
27885 graphtype = this.graphtype;
27890 var r = this.raphael,
27891 fin = function () {
27892 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27894 fout = function () {
27895 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27897 pfin = function() {
27898 this.sector.stop();
27899 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27902 this.label[0].stop();
27903 this.label[0].attr({ r: 7.5 });
27904 this.label[1].attr({ "font-weight": 800 });
27907 pfout = function() {
27908 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27911 this.label[0].animate({ r: 5 }, 500, "bounce");
27912 this.label[1].attr({ "font-weight": 400 });
27918 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27921 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27924 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27925 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27927 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27934 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27939 setTitle: function(o)
27944 initEvents: function() {
27947 this.el.on('click', this.onClick, this);
27951 onClick : function(e)
27953 Roo.log('img onclick');
27954 this.fireEvent('click', this, e);
27966 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27969 * @class Roo.bootstrap.dash.NumberBox
27970 * @extends Roo.bootstrap.Component
27971 * Bootstrap NumberBox class
27972 * @cfg {String} headline Box headline
27973 * @cfg {String} content Box content
27974 * @cfg {String} icon Box icon
27975 * @cfg {String} footer Footer text
27976 * @cfg {String} fhref Footer href
27979 * Create a new NumberBox
27980 * @param {Object} config The config object
27984 Roo.bootstrap.dash.NumberBox = function(config){
27985 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27989 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
27998 getAutoCreate : function(){
28002 cls : 'small-box ',
28010 cls : 'roo-headline',
28011 html : this.headline
28015 cls : 'roo-content',
28016 html : this.content
28030 cls : 'ion ' + this.icon
28039 cls : 'small-box-footer',
28040 href : this.fhref || '#',
28044 cfg.cn.push(footer);
28051 onRender : function(ct,position){
28052 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28059 setHeadline: function (value)
28061 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28064 setFooter: function (value, href)
28066 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28069 this.el.select('a.small-box-footer',true).first().attr('href', href);
28074 setContent: function (value)
28076 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28079 initEvents: function()
28093 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28096 * @class Roo.bootstrap.dash.TabBox
28097 * @extends Roo.bootstrap.Component
28098 * Bootstrap TabBox class
28099 * @cfg {String} title Title of the TabBox
28100 * @cfg {String} icon Icon of the TabBox
28101 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28102 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28105 * Create a new TabBox
28106 * @param {Object} config The config object
28110 Roo.bootstrap.dash.TabBox = function(config){
28111 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28116 * When a pane is added
28117 * @param {Roo.bootstrap.dash.TabPane} pane
28121 * @event activatepane
28122 * When a pane is activated
28123 * @param {Roo.bootstrap.dash.TabPane} pane
28125 "activatepane" : true
28133 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28138 tabScrollable : false,
28140 getChildContainer : function()
28142 return this.el.select('.tab-content', true).first();
28145 getAutoCreate : function(){
28149 cls: 'pull-left header',
28157 cls: 'fa ' + this.icon
28163 cls: 'nav nav-tabs pull-right',
28169 if(this.tabScrollable){
28176 cls: 'nav nav-tabs pull-right',
28187 cls: 'nav-tabs-custom',
28192 cls: 'tab-content no-padding',
28200 initEvents : function()
28202 //Roo.log('add add pane handler');
28203 this.on('addpane', this.onAddPane, this);
28206 * Updates the box title
28207 * @param {String} html to set the title to.
28209 setTitle : function(value)
28211 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28213 onAddPane : function(pane)
28215 this.panes.push(pane);
28216 //Roo.log('addpane');
28218 // tabs are rendere left to right..
28219 if(!this.showtabs){
28223 var ctr = this.el.select('.nav-tabs', true).first();
28226 var existing = ctr.select('.nav-tab',true);
28227 var qty = existing.getCount();;
28230 var tab = ctr.createChild({
28232 cls : 'nav-tab' + (qty ? '' : ' active'),
28240 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28243 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28245 pane.el.addClass('active');
28250 onTabClick : function(ev,un,ob,pane)
28252 //Roo.log('tab - prev default');
28253 ev.preventDefault();
28256 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28257 pane.tab.addClass('active');
28258 //Roo.log(pane.title);
28259 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28260 // technically we should have a deactivate event.. but maybe add later.
28261 // and it should not de-activate the selected tab...
28262 this.fireEvent('activatepane', pane);
28263 pane.el.addClass('active');
28264 pane.fireEvent('activate');
28269 getActivePane : function()
28272 Roo.each(this.panes, function(p) {
28273 if(p.el.hasClass('active')){
28294 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28296 * @class Roo.bootstrap.TabPane
28297 * @extends Roo.bootstrap.Component
28298 * Bootstrap TabPane class
28299 * @cfg {Boolean} active (false | true) Default false
28300 * @cfg {String} title title of panel
28304 * Create a new TabPane
28305 * @param {Object} config The config object
28308 Roo.bootstrap.dash.TabPane = function(config){
28309 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28315 * When a pane is activated
28316 * @param {Roo.bootstrap.dash.TabPane} pane
28323 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28328 // the tabBox that this is attached to.
28331 getAutoCreate : function()
28339 cfg.cls += ' active';
28344 initEvents : function()
28346 //Roo.log('trigger add pane handler');
28347 this.parent().fireEvent('addpane', this)
28351 * Updates the tab title
28352 * @param {String} html to set the title to.
28354 setTitle: function(str)
28360 this.tab.select('a', true).first().dom.innerHTML = str;
28377 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28380 * @class Roo.bootstrap.menu.Menu
28381 * @extends Roo.bootstrap.Component
28382 * Bootstrap Menu class - container for Menu
28383 * @cfg {String} html Text of the menu
28384 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28385 * @cfg {String} icon Font awesome icon
28386 * @cfg {String} pos Menu align to (top | bottom) default bottom
28390 * Create a new Menu
28391 * @param {Object} config The config object
28395 Roo.bootstrap.menu.Menu = function(config){
28396 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28400 * @event beforeshow
28401 * Fires before this menu is displayed
28402 * @param {Roo.bootstrap.menu.Menu} this
28406 * @event beforehide
28407 * Fires before this menu is hidden
28408 * @param {Roo.bootstrap.menu.Menu} this
28413 * Fires after this menu is displayed
28414 * @param {Roo.bootstrap.menu.Menu} this
28419 * Fires after this menu is hidden
28420 * @param {Roo.bootstrap.menu.Menu} this
28425 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28426 * @param {Roo.bootstrap.menu.Menu} this
28427 * @param {Roo.EventObject} e
28434 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28438 weight : 'default',
28443 getChildContainer : function() {
28444 if(this.isSubMenu){
28448 return this.el.select('ul.dropdown-menu', true).first();
28451 getAutoCreate : function()
28456 cls : 'roo-menu-text',
28464 cls : 'fa ' + this.icon
28475 cls : 'dropdown-button btn btn-' + this.weight,
28480 cls : 'dropdown-toggle btn btn-' + this.weight,
28490 cls : 'dropdown-menu'
28496 if(this.pos == 'top'){
28497 cfg.cls += ' dropup';
28500 if(this.isSubMenu){
28503 cls : 'dropdown-menu'
28510 onRender : function(ct, position)
28512 this.isSubMenu = ct.hasClass('dropdown-submenu');
28514 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28517 initEvents : function()
28519 if(this.isSubMenu){
28523 this.hidden = true;
28525 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28526 this.triggerEl.on('click', this.onTriggerPress, this);
28528 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28529 this.buttonEl.on('click', this.onClick, this);
28535 if(this.isSubMenu){
28539 return this.el.select('ul.dropdown-menu', true).first();
28542 onClick : function(e)
28544 this.fireEvent("click", this, e);
28547 onTriggerPress : function(e)
28549 if (this.isVisible()) {
28556 isVisible : function(){
28557 return !this.hidden;
28562 this.fireEvent("beforeshow", this);
28564 this.hidden = false;
28565 this.el.addClass('open');
28567 Roo.get(document).on("mouseup", this.onMouseUp, this);
28569 this.fireEvent("show", this);
28576 this.fireEvent("beforehide", this);
28578 this.hidden = true;
28579 this.el.removeClass('open');
28581 Roo.get(document).un("mouseup", this.onMouseUp);
28583 this.fireEvent("hide", this);
28586 onMouseUp : function()
28600 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28603 * @class Roo.bootstrap.menu.Item
28604 * @extends Roo.bootstrap.Component
28605 * Bootstrap MenuItem class
28606 * @cfg {Boolean} submenu (true | false) default false
28607 * @cfg {String} html text of the item
28608 * @cfg {String} href the link
28609 * @cfg {Boolean} disable (true | false) default false
28610 * @cfg {Boolean} preventDefault (true | false) default true
28611 * @cfg {String} icon Font awesome icon
28612 * @cfg {String} pos Submenu align to (left | right) default right
28616 * Create a new Item
28617 * @param {Object} config The config object
28621 Roo.bootstrap.menu.Item = function(config){
28622 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28626 * Fires when the mouse is hovering over this menu
28627 * @param {Roo.bootstrap.menu.Item} this
28628 * @param {Roo.EventObject} e
28633 * Fires when the mouse exits this menu
28634 * @param {Roo.bootstrap.menu.Item} this
28635 * @param {Roo.EventObject} e
28641 * The raw click event for the entire grid.
28642 * @param {Roo.EventObject} e
28648 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28653 preventDefault: true,
28658 getAutoCreate : function()
28663 cls : 'roo-menu-item-text',
28671 cls : 'fa ' + this.icon
28680 href : this.href || '#',
28687 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28691 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28693 if(this.pos == 'left'){
28694 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28701 initEvents : function()
28703 this.el.on('mouseover', this.onMouseOver, this);
28704 this.el.on('mouseout', this.onMouseOut, this);
28706 this.el.select('a', true).first().on('click', this.onClick, this);
28710 onClick : function(e)
28712 if(this.preventDefault){
28713 e.preventDefault();
28716 this.fireEvent("click", this, e);
28719 onMouseOver : function(e)
28721 if(this.submenu && this.pos == 'left'){
28722 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28725 this.fireEvent("mouseover", this, e);
28728 onMouseOut : function(e)
28730 this.fireEvent("mouseout", this, e);
28742 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28745 * @class Roo.bootstrap.menu.Separator
28746 * @extends Roo.bootstrap.Component
28747 * Bootstrap Separator class
28750 * Create a new Separator
28751 * @param {Object} config The config object
28755 Roo.bootstrap.menu.Separator = function(config){
28756 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28759 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28761 getAutoCreate : function(){
28782 * @class Roo.bootstrap.Tooltip
28783 * Bootstrap Tooltip class
28784 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28785 * to determine which dom element triggers the tooltip.
28787 * It needs to add support for additional attributes like tooltip-position
28790 * Create a new Toolti
28791 * @param {Object} config The config object
28794 Roo.bootstrap.Tooltip = function(config){
28795 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28797 this.alignment = Roo.bootstrap.Tooltip.alignment;
28799 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28800 this.alignment = config.alignment;
28805 Roo.apply(Roo.bootstrap.Tooltip, {
28807 * @function init initialize tooltip monitoring.
28811 currentTip : false,
28812 currentRegion : false,
28818 Roo.get(document).on('mouseover', this.enter ,this);
28819 Roo.get(document).on('mouseout', this.leave, this);
28822 this.currentTip = new Roo.bootstrap.Tooltip();
28825 enter : function(ev)
28827 var dom = ev.getTarget();
28829 //Roo.log(['enter',dom]);
28830 var el = Roo.fly(dom);
28831 if (this.currentEl) {
28833 //Roo.log(this.currentEl);
28834 //Roo.log(this.currentEl.contains(dom));
28835 if (this.currentEl == el) {
28838 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28844 if (this.currentTip.el) {
28845 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28849 if(!el || el.dom == document){
28855 // you can not look for children, as if el is the body.. then everythign is the child..
28856 if (!el.attr('tooltip')) { //
28857 if (!el.select("[tooltip]").elements.length) {
28860 // is the mouse over this child...?
28861 bindEl = el.select("[tooltip]").first();
28862 var xy = ev.getXY();
28863 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28864 //Roo.log("not in region.");
28867 //Roo.log("child element over..");
28870 this.currentEl = bindEl;
28871 this.currentTip.bind(bindEl);
28872 this.currentRegion = Roo.lib.Region.getRegion(dom);
28873 this.currentTip.enter();
28876 leave : function(ev)
28878 var dom = ev.getTarget();
28879 //Roo.log(['leave',dom]);
28880 if (!this.currentEl) {
28885 if (dom != this.currentEl.dom) {
28888 var xy = ev.getXY();
28889 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28892 // only activate leave if mouse cursor is outside... bounding box..
28897 if (this.currentTip) {
28898 this.currentTip.leave();
28900 //Roo.log('clear currentEl');
28901 this.currentEl = false;
28906 'left' : ['r-l', [-2,0], 'right'],
28907 'right' : ['l-r', [2,0], 'left'],
28908 'bottom' : ['t-b', [0,2], 'top'],
28909 'top' : [ 'b-t', [0,-2], 'bottom']
28915 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28920 delay : null, // can be { show : 300 , hide: 500}
28924 hoverState : null, //???
28926 placement : 'bottom',
28930 getAutoCreate : function(){
28937 cls : 'tooltip-arrow arrow'
28940 cls : 'tooltip-inner'
28947 bind : function(el)
28952 initEvents : function()
28954 this.arrowEl = this.el.select('.arrow', true).first();
28955 this.innerEl = this.el.select('.tooltip-inner', true).first();
28958 enter : function () {
28960 if (this.timeout != null) {
28961 clearTimeout(this.timeout);
28964 this.hoverState = 'in';
28965 //Roo.log("enter - show");
28966 if (!this.delay || !this.delay.show) {
28971 this.timeout = setTimeout(function () {
28972 if (_t.hoverState == 'in') {
28975 }, this.delay.show);
28979 clearTimeout(this.timeout);
28981 this.hoverState = 'out';
28982 if (!this.delay || !this.delay.hide) {
28988 this.timeout = setTimeout(function () {
28989 //Roo.log("leave - timeout");
28991 if (_t.hoverState == 'out') {
28993 Roo.bootstrap.Tooltip.currentEl = false;
28998 show : function (msg)
29001 this.render(document.body);
29004 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29006 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29008 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29010 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29011 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29013 var placement = typeof this.placement == 'function' ?
29014 this.placement.call(this, this.el, on_el) :
29017 var autoToken = /\s?auto?\s?/i;
29018 var autoPlace = autoToken.test(placement);
29020 placement = placement.replace(autoToken, '') || 'top';
29024 //this.el.setXY([0,0]);
29026 //this.el.dom.style.display='block';
29028 //this.el.appendTo(on_el);
29030 var p = this.getPosition();
29031 var box = this.el.getBox();
29037 var align = this.alignment[placement];
29039 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29041 if(placement == 'top' || placement == 'bottom'){
29043 placement = 'right';
29046 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29047 placement = 'left';
29050 var scroll = Roo.select('body', true).first().getScroll();
29052 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29056 align = this.alignment[placement];
29058 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29062 this.el.alignTo(this.bindEl, align[0],align[1]);
29063 //var arrow = this.el.select('.arrow',true).first();
29064 //arrow.set(align[2],
29066 this.el.addClass(placement);
29067 this.el.addClass("bs-tooltip-"+ placement);
29069 this.el.addClass('in fade show');
29071 this.hoverState = null;
29073 if (this.el.hasClass('fade')) {
29088 //this.el.setXY([0,0]);
29089 this.el.removeClass(['show', 'in']);
29105 * @class Roo.bootstrap.LocationPicker
29106 * @extends Roo.bootstrap.Component
29107 * Bootstrap LocationPicker class
29108 * @cfg {Number} latitude Position when init default 0
29109 * @cfg {Number} longitude Position when init default 0
29110 * @cfg {Number} zoom default 15
29111 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29112 * @cfg {Boolean} mapTypeControl default false
29113 * @cfg {Boolean} disableDoubleClickZoom default false
29114 * @cfg {Boolean} scrollwheel default true
29115 * @cfg {Boolean} streetViewControl default false
29116 * @cfg {Number} radius default 0
29117 * @cfg {String} locationName
29118 * @cfg {Boolean} draggable default true
29119 * @cfg {Boolean} enableAutocomplete default false
29120 * @cfg {Boolean} enableReverseGeocode default true
29121 * @cfg {String} markerTitle
29124 * Create a new LocationPicker
29125 * @param {Object} config The config object
29129 Roo.bootstrap.LocationPicker = function(config){
29131 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29136 * Fires when the picker initialized.
29137 * @param {Roo.bootstrap.LocationPicker} this
29138 * @param {Google Location} location
29142 * @event positionchanged
29143 * Fires when the picker position changed.
29144 * @param {Roo.bootstrap.LocationPicker} this
29145 * @param {Google Location} location
29147 positionchanged : true,
29150 * Fires when the map resize.
29151 * @param {Roo.bootstrap.LocationPicker} this
29156 * Fires when the map show.
29157 * @param {Roo.bootstrap.LocationPicker} this
29162 * Fires when the map hide.
29163 * @param {Roo.bootstrap.LocationPicker} this
29168 * Fires when click the map.
29169 * @param {Roo.bootstrap.LocationPicker} this
29170 * @param {Map event} e
29174 * @event mapRightClick
29175 * Fires when right click the map.
29176 * @param {Roo.bootstrap.LocationPicker} this
29177 * @param {Map event} e
29179 mapRightClick : true,
29181 * @event markerClick
29182 * Fires when click the marker.
29183 * @param {Roo.bootstrap.LocationPicker} this
29184 * @param {Map event} e
29186 markerClick : true,
29188 * @event markerRightClick
29189 * Fires when right click the marker.
29190 * @param {Roo.bootstrap.LocationPicker} this
29191 * @param {Map event} e
29193 markerRightClick : true,
29195 * @event OverlayViewDraw
29196 * Fires when OverlayView Draw
29197 * @param {Roo.bootstrap.LocationPicker} this
29199 OverlayViewDraw : true,
29201 * @event OverlayViewOnAdd
29202 * Fires when OverlayView Draw
29203 * @param {Roo.bootstrap.LocationPicker} this
29205 OverlayViewOnAdd : true,
29207 * @event OverlayViewOnRemove
29208 * Fires when OverlayView Draw
29209 * @param {Roo.bootstrap.LocationPicker} this
29211 OverlayViewOnRemove : true,
29213 * @event OverlayViewShow
29214 * Fires when OverlayView Draw
29215 * @param {Roo.bootstrap.LocationPicker} this
29216 * @param {Pixel} cpx
29218 OverlayViewShow : true,
29220 * @event OverlayViewHide
29221 * Fires when OverlayView Draw
29222 * @param {Roo.bootstrap.LocationPicker} this
29224 OverlayViewHide : true,
29226 * @event loadexception
29227 * Fires when load google lib failed.
29228 * @param {Roo.bootstrap.LocationPicker} this
29230 loadexception : true
29235 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29237 gMapContext: false,
29243 mapTypeControl: false,
29244 disableDoubleClickZoom: false,
29246 streetViewControl: false,
29250 enableAutocomplete: false,
29251 enableReverseGeocode: true,
29254 getAutoCreate: function()
29259 cls: 'roo-location-picker'
29265 initEvents: function(ct, position)
29267 if(!this.el.getWidth() || this.isApplied()){
29271 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29276 initial: function()
29278 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29279 this.fireEvent('loadexception', this);
29283 if(!this.mapTypeId){
29284 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29287 this.gMapContext = this.GMapContext();
29289 this.initOverlayView();
29291 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29295 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29296 _this.setPosition(_this.gMapContext.marker.position);
29299 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29300 _this.fireEvent('mapClick', this, event);
29304 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29305 _this.fireEvent('mapRightClick', this, event);
29309 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29310 _this.fireEvent('markerClick', this, event);
29314 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29315 _this.fireEvent('markerRightClick', this, event);
29319 this.setPosition(this.gMapContext.location);
29321 this.fireEvent('initial', this, this.gMapContext.location);
29324 initOverlayView: function()
29328 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29332 _this.fireEvent('OverlayViewDraw', _this);
29337 _this.fireEvent('OverlayViewOnAdd', _this);
29340 onRemove: function()
29342 _this.fireEvent('OverlayViewOnRemove', _this);
29345 show: function(cpx)
29347 _this.fireEvent('OverlayViewShow', _this, cpx);
29352 _this.fireEvent('OverlayViewHide', _this);
29358 fromLatLngToContainerPixel: function(event)
29360 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29363 isApplied: function()
29365 return this.getGmapContext() == false ? false : true;
29368 getGmapContext: function()
29370 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29373 GMapContext: function()
29375 var position = new google.maps.LatLng(this.latitude, this.longitude);
29377 var _map = new google.maps.Map(this.el.dom, {
29380 mapTypeId: this.mapTypeId,
29381 mapTypeControl: this.mapTypeControl,
29382 disableDoubleClickZoom: this.disableDoubleClickZoom,
29383 scrollwheel: this.scrollwheel,
29384 streetViewControl: this.streetViewControl,
29385 locationName: this.locationName,
29386 draggable: this.draggable,
29387 enableAutocomplete: this.enableAutocomplete,
29388 enableReverseGeocode: this.enableReverseGeocode
29391 var _marker = new google.maps.Marker({
29392 position: position,
29394 title: this.markerTitle,
29395 draggable: this.draggable
29402 location: position,
29403 radius: this.radius,
29404 locationName: this.locationName,
29405 addressComponents: {
29406 formatted_address: null,
29407 addressLine1: null,
29408 addressLine2: null,
29410 streetNumber: null,
29414 stateOrProvince: null
29417 domContainer: this.el.dom,
29418 geodecoder: new google.maps.Geocoder()
29422 drawCircle: function(center, radius, options)
29424 if (this.gMapContext.circle != null) {
29425 this.gMapContext.circle.setMap(null);
29429 options = Roo.apply({}, options, {
29430 strokeColor: "#0000FF",
29431 strokeOpacity: .35,
29433 fillColor: "#0000FF",
29437 options.map = this.gMapContext.map;
29438 options.radius = radius;
29439 options.center = center;
29440 this.gMapContext.circle = new google.maps.Circle(options);
29441 return this.gMapContext.circle;
29447 setPosition: function(location)
29449 this.gMapContext.location = location;
29450 this.gMapContext.marker.setPosition(location);
29451 this.gMapContext.map.panTo(location);
29452 this.drawCircle(location, this.gMapContext.radius, {});
29456 if (this.gMapContext.settings.enableReverseGeocode) {
29457 this.gMapContext.geodecoder.geocode({
29458 latLng: this.gMapContext.location
29459 }, function(results, status) {
29461 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29462 _this.gMapContext.locationName = results[0].formatted_address;
29463 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29465 _this.fireEvent('positionchanged', this, location);
29472 this.fireEvent('positionchanged', this, location);
29477 google.maps.event.trigger(this.gMapContext.map, "resize");
29479 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29481 this.fireEvent('resize', this);
29484 setPositionByLatLng: function(latitude, longitude)
29486 this.setPosition(new google.maps.LatLng(latitude, longitude));
29489 getCurrentPosition: function()
29492 latitude: this.gMapContext.location.lat(),
29493 longitude: this.gMapContext.location.lng()
29497 getAddressName: function()
29499 return this.gMapContext.locationName;
29502 getAddressComponents: function()
29504 return this.gMapContext.addressComponents;
29507 address_component_from_google_geocode: function(address_components)
29511 for (var i = 0; i < address_components.length; i++) {
29512 var component = address_components[i];
29513 if (component.types.indexOf("postal_code") >= 0) {
29514 result.postalCode = component.short_name;
29515 } else if (component.types.indexOf("street_number") >= 0) {
29516 result.streetNumber = component.short_name;
29517 } else if (component.types.indexOf("route") >= 0) {
29518 result.streetName = component.short_name;
29519 } else if (component.types.indexOf("neighborhood") >= 0) {
29520 result.city = component.short_name;
29521 } else if (component.types.indexOf("locality") >= 0) {
29522 result.city = component.short_name;
29523 } else if (component.types.indexOf("sublocality") >= 0) {
29524 result.district = component.short_name;
29525 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29526 result.stateOrProvince = component.short_name;
29527 } else if (component.types.indexOf("country") >= 0) {
29528 result.country = component.short_name;
29532 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29533 result.addressLine2 = "";
29537 setZoomLevel: function(zoom)
29539 this.gMapContext.map.setZoom(zoom);
29552 this.fireEvent('show', this);
29563 this.fireEvent('hide', this);
29568 Roo.apply(Roo.bootstrap.LocationPicker, {
29570 OverlayView : function(map, options)
29572 options = options || {};
29579 * @class Roo.bootstrap.Alert
29580 * @extends Roo.bootstrap.Component
29581 * Bootstrap Alert class - shows an alert area box
29583 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29584 Enter a valid email address
29587 * @cfg {String} title The title of alert
29588 * @cfg {String} html The content of alert
29589 * @cfg {String} weight ( success | info | warning | danger )
29590 * @cfg {String} faicon font-awesomeicon
29593 * Create a new alert
29594 * @param {Object} config The config object
29598 Roo.bootstrap.Alert = function(config){
29599 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29603 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29610 getAutoCreate : function()
29619 cls : 'roo-alert-icon'
29624 cls : 'roo-alert-title',
29629 cls : 'roo-alert-text',
29636 cfg.cn[0].cls += ' fa ' + this.faicon;
29640 cfg.cls += ' alert-' + this.weight;
29646 initEvents: function()
29648 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29651 setTitle : function(str)
29653 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29656 setText : function(str)
29658 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29661 setWeight : function(weight)
29664 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29667 this.weight = weight;
29669 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29672 setIcon : function(icon)
29675 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29678 this.faicon = icon;
29680 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29701 * @class Roo.bootstrap.UploadCropbox
29702 * @extends Roo.bootstrap.Component
29703 * Bootstrap UploadCropbox class
29704 * @cfg {String} emptyText show when image has been loaded
29705 * @cfg {String} rotateNotify show when image too small to rotate
29706 * @cfg {Number} errorTimeout default 3000
29707 * @cfg {Number} minWidth default 300
29708 * @cfg {Number} minHeight default 300
29709 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29710 * @cfg {Boolean} isDocument (true|false) default false
29711 * @cfg {String} url action url
29712 * @cfg {String} paramName default 'imageUpload'
29713 * @cfg {String} method default POST
29714 * @cfg {Boolean} loadMask (true|false) default true
29715 * @cfg {Boolean} loadingText default 'Loading...'
29718 * Create a new UploadCropbox
29719 * @param {Object} config The config object
29722 Roo.bootstrap.UploadCropbox = function(config){
29723 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29727 * @event beforeselectfile
29728 * Fire before select file
29729 * @param {Roo.bootstrap.UploadCropbox} this
29731 "beforeselectfile" : true,
29734 * Fire after initEvent
29735 * @param {Roo.bootstrap.UploadCropbox} this
29740 * Fire after initEvent
29741 * @param {Roo.bootstrap.UploadCropbox} this
29742 * @param {String} data
29747 * Fire when preparing the file data
29748 * @param {Roo.bootstrap.UploadCropbox} this
29749 * @param {Object} file
29754 * Fire when get exception
29755 * @param {Roo.bootstrap.UploadCropbox} this
29756 * @param {XMLHttpRequest} xhr
29758 "exception" : true,
29760 * @event beforeloadcanvas
29761 * Fire before load the canvas
29762 * @param {Roo.bootstrap.UploadCropbox} this
29763 * @param {String} src
29765 "beforeloadcanvas" : true,
29768 * Fire when trash image
29769 * @param {Roo.bootstrap.UploadCropbox} this
29774 * Fire when download the image
29775 * @param {Roo.bootstrap.UploadCropbox} this
29779 * @event footerbuttonclick
29780 * Fire when footerbuttonclick
29781 * @param {Roo.bootstrap.UploadCropbox} this
29782 * @param {String} type
29784 "footerbuttonclick" : true,
29788 * @param {Roo.bootstrap.UploadCropbox} this
29793 * Fire when rotate the image
29794 * @param {Roo.bootstrap.UploadCropbox} this
29795 * @param {String} pos
29800 * Fire when inspect the file
29801 * @param {Roo.bootstrap.UploadCropbox} this
29802 * @param {Object} file
29807 * Fire when xhr upload the file
29808 * @param {Roo.bootstrap.UploadCropbox} this
29809 * @param {Object} data
29814 * Fire when arrange the file data
29815 * @param {Roo.bootstrap.UploadCropbox} this
29816 * @param {Object} formData
29821 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29824 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29826 emptyText : 'Click to upload image',
29827 rotateNotify : 'Image is too small to rotate',
29828 errorTimeout : 3000,
29842 cropType : 'image/jpeg',
29844 canvasLoaded : false,
29845 isDocument : false,
29847 paramName : 'imageUpload',
29849 loadingText : 'Loading...',
29852 getAutoCreate : function()
29856 cls : 'roo-upload-cropbox',
29860 cls : 'roo-upload-cropbox-selector',
29865 cls : 'roo-upload-cropbox-body',
29866 style : 'cursor:pointer',
29870 cls : 'roo-upload-cropbox-preview'
29874 cls : 'roo-upload-cropbox-thumb'
29878 cls : 'roo-upload-cropbox-empty-notify',
29879 html : this.emptyText
29883 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29884 html : this.rotateNotify
29890 cls : 'roo-upload-cropbox-footer',
29893 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29903 onRender : function(ct, position)
29905 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29907 if (this.buttons.length) {
29909 Roo.each(this.buttons, function(bb) {
29911 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29913 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29919 this.maskEl = this.el;
29923 initEvents : function()
29925 this.urlAPI = (window.createObjectURL && window) ||
29926 (window.URL && URL.revokeObjectURL && URL) ||
29927 (window.webkitURL && webkitURL);
29929 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29930 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29932 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29933 this.selectorEl.hide();
29935 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29936 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29938 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29939 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29940 this.thumbEl.hide();
29942 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29943 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29945 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29946 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29947 this.errorEl.hide();
29949 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29950 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29951 this.footerEl.hide();
29953 this.setThumbBoxSize();
29959 this.fireEvent('initial', this);
29966 window.addEventListener("resize", function() { _this.resize(); } );
29968 this.bodyEl.on('click', this.beforeSelectFile, this);
29971 this.bodyEl.on('touchstart', this.onTouchStart, this);
29972 this.bodyEl.on('touchmove', this.onTouchMove, this);
29973 this.bodyEl.on('touchend', this.onTouchEnd, this);
29977 this.bodyEl.on('mousedown', this.onMouseDown, this);
29978 this.bodyEl.on('mousemove', this.onMouseMove, this);
29979 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29980 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29981 Roo.get(document).on('mouseup', this.onMouseUp, this);
29984 this.selectorEl.on('change', this.onFileSelected, this);
29990 this.baseScale = 1;
29992 this.baseRotate = 1;
29993 this.dragable = false;
29994 this.pinching = false;
29997 this.cropData = false;
29998 this.notifyEl.dom.innerHTML = this.emptyText;
30000 this.selectorEl.dom.value = '';
30004 resize : function()
30006 if(this.fireEvent('resize', this) != false){
30007 this.setThumbBoxPosition();
30008 this.setCanvasPosition();
30012 onFooterButtonClick : function(e, el, o, type)
30015 case 'rotate-left' :
30016 this.onRotateLeft(e);
30018 case 'rotate-right' :
30019 this.onRotateRight(e);
30022 this.beforeSelectFile(e);
30037 this.fireEvent('footerbuttonclick', this, type);
30040 beforeSelectFile : function(e)
30042 e.preventDefault();
30044 if(this.fireEvent('beforeselectfile', this) != false){
30045 this.selectorEl.dom.click();
30049 onFileSelected : function(e)
30051 e.preventDefault();
30053 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30057 var file = this.selectorEl.dom.files[0];
30059 if(this.fireEvent('inspect', this, file) != false){
30060 this.prepare(file);
30065 trash : function(e)
30067 this.fireEvent('trash', this);
30070 download : function(e)
30072 this.fireEvent('download', this);
30075 loadCanvas : function(src)
30077 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30081 this.imageEl = document.createElement('img');
30085 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30087 this.imageEl.src = src;
30091 onLoadCanvas : function()
30093 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30094 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30096 this.bodyEl.un('click', this.beforeSelectFile, this);
30098 this.notifyEl.hide();
30099 this.thumbEl.show();
30100 this.footerEl.show();
30102 this.baseRotateLevel();
30104 if(this.isDocument){
30105 this.setThumbBoxSize();
30108 this.setThumbBoxPosition();
30110 this.baseScaleLevel();
30116 this.canvasLoaded = true;
30119 this.maskEl.unmask();
30124 setCanvasPosition : function()
30126 if(!this.canvasEl){
30130 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30131 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30133 this.previewEl.setLeft(pw);
30134 this.previewEl.setTop(ph);
30138 onMouseDown : function(e)
30142 this.dragable = true;
30143 this.pinching = false;
30145 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30146 this.dragable = false;
30150 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30151 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30155 onMouseMove : function(e)
30159 if(!this.canvasLoaded){
30163 if (!this.dragable){
30167 var minX = Math.ceil(this.thumbEl.getLeft(true));
30168 var minY = Math.ceil(this.thumbEl.getTop(true));
30170 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30171 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30173 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30174 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30176 x = x - this.mouseX;
30177 y = y - this.mouseY;
30179 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30180 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30182 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30183 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30185 this.previewEl.setLeft(bgX);
30186 this.previewEl.setTop(bgY);
30188 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30189 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30192 onMouseUp : function(e)
30196 this.dragable = false;
30199 onMouseWheel : function(e)
30203 this.startScale = this.scale;
30205 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30207 if(!this.zoomable()){
30208 this.scale = this.startScale;
30217 zoomable : function()
30219 var minScale = this.thumbEl.getWidth() / this.minWidth;
30221 if(this.minWidth < this.minHeight){
30222 minScale = this.thumbEl.getHeight() / this.minHeight;
30225 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30226 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30230 (this.rotate == 0 || this.rotate == 180) &&
30232 width > this.imageEl.OriginWidth ||
30233 height > this.imageEl.OriginHeight ||
30234 (width < this.minWidth && height < this.minHeight)
30242 (this.rotate == 90 || this.rotate == 270) &&
30244 width > this.imageEl.OriginWidth ||
30245 height > this.imageEl.OriginHeight ||
30246 (width < this.minHeight && height < this.minWidth)
30253 !this.isDocument &&
30254 (this.rotate == 0 || this.rotate == 180) &&
30256 width < this.minWidth ||
30257 width > this.imageEl.OriginWidth ||
30258 height < this.minHeight ||
30259 height > this.imageEl.OriginHeight
30266 !this.isDocument &&
30267 (this.rotate == 90 || this.rotate == 270) &&
30269 width < this.minHeight ||
30270 width > this.imageEl.OriginWidth ||
30271 height < this.minWidth ||
30272 height > this.imageEl.OriginHeight
30282 onRotateLeft : function(e)
30284 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30286 var minScale = this.thumbEl.getWidth() / this.minWidth;
30288 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30289 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30291 this.startScale = this.scale;
30293 while (this.getScaleLevel() < minScale){
30295 this.scale = this.scale + 1;
30297 if(!this.zoomable()){
30302 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30303 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30308 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30315 this.scale = this.startScale;
30317 this.onRotateFail();
30322 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30324 if(this.isDocument){
30325 this.setThumbBoxSize();
30326 this.setThumbBoxPosition();
30327 this.setCanvasPosition();
30332 this.fireEvent('rotate', this, 'left');
30336 onRotateRight : function(e)
30338 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30340 var minScale = this.thumbEl.getWidth() / this.minWidth;
30342 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30343 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30345 this.startScale = this.scale;
30347 while (this.getScaleLevel() < minScale){
30349 this.scale = this.scale + 1;
30351 if(!this.zoomable()){
30356 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30357 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30362 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30369 this.scale = this.startScale;
30371 this.onRotateFail();
30376 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30378 if(this.isDocument){
30379 this.setThumbBoxSize();
30380 this.setThumbBoxPosition();
30381 this.setCanvasPosition();
30386 this.fireEvent('rotate', this, 'right');
30389 onRotateFail : function()
30391 this.errorEl.show(true);
30395 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30400 this.previewEl.dom.innerHTML = '';
30402 var canvasEl = document.createElement("canvas");
30404 var contextEl = canvasEl.getContext("2d");
30406 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30407 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30408 var center = this.imageEl.OriginWidth / 2;
30410 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30411 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30412 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30413 center = this.imageEl.OriginHeight / 2;
30416 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30418 contextEl.translate(center, center);
30419 contextEl.rotate(this.rotate * Math.PI / 180);
30421 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30423 this.canvasEl = document.createElement("canvas");
30425 this.contextEl = this.canvasEl.getContext("2d");
30427 switch (this.rotate) {
30430 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30431 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30433 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30438 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30439 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30441 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30442 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);
30446 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30451 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30452 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30454 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30455 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);
30459 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);
30464 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30465 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30467 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30468 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30472 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);
30479 this.previewEl.appendChild(this.canvasEl);
30481 this.setCanvasPosition();
30486 if(!this.canvasLoaded){
30490 var imageCanvas = document.createElement("canvas");
30492 var imageContext = imageCanvas.getContext("2d");
30494 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30495 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30497 var center = imageCanvas.width / 2;
30499 imageContext.translate(center, center);
30501 imageContext.rotate(this.rotate * Math.PI / 180);
30503 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30505 var canvas = document.createElement("canvas");
30507 var context = canvas.getContext("2d");
30509 canvas.width = this.minWidth;
30510 canvas.height = this.minHeight;
30512 switch (this.rotate) {
30515 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30516 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30518 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30519 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30521 var targetWidth = this.minWidth - 2 * x;
30522 var targetHeight = this.minHeight - 2 * y;
30526 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30527 scale = targetWidth / width;
30530 if(x > 0 && y == 0){
30531 scale = targetHeight / height;
30534 if(x > 0 && y > 0){
30535 scale = targetWidth / width;
30537 if(width < height){
30538 scale = targetHeight / height;
30542 context.scale(scale, scale);
30544 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30545 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30547 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30548 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30550 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30555 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30556 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30558 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30559 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30561 var targetWidth = this.minWidth - 2 * x;
30562 var targetHeight = this.minHeight - 2 * y;
30566 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30567 scale = targetWidth / width;
30570 if(x > 0 && y == 0){
30571 scale = targetHeight / height;
30574 if(x > 0 && y > 0){
30575 scale = targetWidth / width;
30577 if(width < height){
30578 scale = targetHeight / height;
30582 context.scale(scale, scale);
30584 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30585 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30587 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30588 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30590 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30592 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30597 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30598 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30600 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30601 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30603 var targetWidth = this.minWidth - 2 * x;
30604 var targetHeight = this.minHeight - 2 * y;
30608 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30609 scale = targetWidth / width;
30612 if(x > 0 && y == 0){
30613 scale = targetHeight / height;
30616 if(x > 0 && y > 0){
30617 scale = targetWidth / width;
30619 if(width < height){
30620 scale = targetHeight / height;
30624 context.scale(scale, scale);
30626 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30627 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30629 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30630 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30632 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30633 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30635 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30640 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30641 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30643 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30644 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30646 var targetWidth = this.minWidth - 2 * x;
30647 var targetHeight = this.minHeight - 2 * y;
30651 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30652 scale = targetWidth / width;
30655 if(x > 0 && y == 0){
30656 scale = targetHeight / height;
30659 if(x > 0 && y > 0){
30660 scale = targetWidth / width;
30662 if(width < height){
30663 scale = targetHeight / height;
30667 context.scale(scale, scale);
30669 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30670 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30672 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30673 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30675 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30677 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30684 this.cropData = canvas.toDataURL(this.cropType);
30686 if(this.fireEvent('crop', this, this.cropData) !== false){
30687 this.process(this.file, this.cropData);
30694 setThumbBoxSize : function()
30698 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30699 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30700 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30702 this.minWidth = width;
30703 this.minHeight = height;
30705 if(this.rotate == 90 || this.rotate == 270){
30706 this.minWidth = height;
30707 this.minHeight = width;
30712 width = Math.ceil(this.minWidth * height / this.minHeight);
30714 if(this.minWidth > this.minHeight){
30716 height = Math.ceil(this.minHeight * width / this.minWidth);
30719 this.thumbEl.setStyle({
30720 width : width + 'px',
30721 height : height + 'px'
30728 setThumbBoxPosition : function()
30730 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30731 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30733 this.thumbEl.setLeft(x);
30734 this.thumbEl.setTop(y);
30738 baseRotateLevel : function()
30740 this.baseRotate = 1;
30743 typeof(this.exif) != 'undefined' &&
30744 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30745 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30747 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30750 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30754 baseScaleLevel : function()
30758 if(this.isDocument){
30760 if(this.baseRotate == 6 || this.baseRotate == 8){
30762 height = this.thumbEl.getHeight();
30763 this.baseScale = height / this.imageEl.OriginWidth;
30765 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30766 width = this.thumbEl.getWidth();
30767 this.baseScale = width / this.imageEl.OriginHeight;
30773 height = this.thumbEl.getHeight();
30774 this.baseScale = height / this.imageEl.OriginHeight;
30776 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30777 width = this.thumbEl.getWidth();
30778 this.baseScale = width / this.imageEl.OriginWidth;
30784 if(this.baseRotate == 6 || this.baseRotate == 8){
30786 width = this.thumbEl.getHeight();
30787 this.baseScale = width / this.imageEl.OriginHeight;
30789 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30790 height = this.thumbEl.getWidth();
30791 this.baseScale = height / this.imageEl.OriginHeight;
30794 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30795 height = this.thumbEl.getWidth();
30796 this.baseScale = height / this.imageEl.OriginHeight;
30798 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30799 width = this.thumbEl.getHeight();
30800 this.baseScale = width / this.imageEl.OriginWidth;
30807 width = this.thumbEl.getWidth();
30808 this.baseScale = width / this.imageEl.OriginWidth;
30810 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30811 height = this.thumbEl.getHeight();
30812 this.baseScale = height / this.imageEl.OriginHeight;
30815 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30817 height = this.thumbEl.getHeight();
30818 this.baseScale = height / this.imageEl.OriginHeight;
30820 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30821 width = this.thumbEl.getWidth();
30822 this.baseScale = width / this.imageEl.OriginWidth;
30830 getScaleLevel : function()
30832 return this.baseScale * Math.pow(1.1, this.scale);
30835 onTouchStart : function(e)
30837 if(!this.canvasLoaded){
30838 this.beforeSelectFile(e);
30842 var touches = e.browserEvent.touches;
30848 if(touches.length == 1){
30849 this.onMouseDown(e);
30853 if(touches.length != 2){
30859 for(var i = 0, finger; finger = touches[i]; i++){
30860 coords.push(finger.pageX, finger.pageY);
30863 var x = Math.pow(coords[0] - coords[2], 2);
30864 var y = Math.pow(coords[1] - coords[3], 2);
30866 this.startDistance = Math.sqrt(x + y);
30868 this.startScale = this.scale;
30870 this.pinching = true;
30871 this.dragable = false;
30875 onTouchMove : function(e)
30877 if(!this.pinching && !this.dragable){
30881 var touches = e.browserEvent.touches;
30888 this.onMouseMove(e);
30894 for(var i = 0, finger; finger = touches[i]; i++){
30895 coords.push(finger.pageX, finger.pageY);
30898 var x = Math.pow(coords[0] - coords[2], 2);
30899 var y = Math.pow(coords[1] - coords[3], 2);
30901 this.endDistance = Math.sqrt(x + y);
30903 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30905 if(!this.zoomable()){
30906 this.scale = this.startScale;
30914 onTouchEnd : function(e)
30916 this.pinching = false;
30917 this.dragable = false;
30921 process : function(file, crop)
30924 this.maskEl.mask(this.loadingText);
30927 this.xhr = new XMLHttpRequest();
30929 file.xhr = this.xhr;
30931 this.xhr.open(this.method, this.url, true);
30934 "Accept": "application/json",
30935 "Cache-Control": "no-cache",
30936 "X-Requested-With": "XMLHttpRequest"
30939 for (var headerName in headers) {
30940 var headerValue = headers[headerName];
30942 this.xhr.setRequestHeader(headerName, headerValue);
30948 this.xhr.onload = function()
30950 _this.xhrOnLoad(_this.xhr);
30953 this.xhr.onerror = function()
30955 _this.xhrOnError(_this.xhr);
30958 var formData = new FormData();
30960 formData.append('returnHTML', 'NO');
30963 formData.append('crop', crop);
30966 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30967 formData.append(this.paramName, file, file.name);
30970 if(typeof(file.filename) != 'undefined'){
30971 formData.append('filename', file.filename);
30974 if(typeof(file.mimetype) != 'undefined'){
30975 formData.append('mimetype', file.mimetype);
30978 if(this.fireEvent('arrange', this, formData) != false){
30979 this.xhr.send(formData);
30983 xhrOnLoad : function(xhr)
30986 this.maskEl.unmask();
30989 if (xhr.readyState !== 4) {
30990 this.fireEvent('exception', this, xhr);
30994 var response = Roo.decode(xhr.responseText);
30996 if(!response.success){
30997 this.fireEvent('exception', this, xhr);
31001 var response = Roo.decode(xhr.responseText);
31003 this.fireEvent('upload', this, response);
31007 xhrOnError : function()
31010 this.maskEl.unmask();
31013 Roo.log('xhr on error');
31015 var response = Roo.decode(xhr.responseText);
31021 prepare : function(file)
31024 this.maskEl.mask(this.loadingText);
31030 if(typeof(file) === 'string'){
31031 this.loadCanvas(file);
31035 if(!file || !this.urlAPI){
31040 this.cropType = file.type;
31044 if(this.fireEvent('prepare', this, this.file) != false){
31046 var reader = new FileReader();
31048 reader.onload = function (e) {
31049 if (e.target.error) {
31050 Roo.log(e.target.error);
31054 var buffer = e.target.result,
31055 dataView = new DataView(buffer),
31057 maxOffset = dataView.byteLength - 4,
31061 if (dataView.getUint16(0) === 0xffd8) {
31062 while (offset < maxOffset) {
31063 markerBytes = dataView.getUint16(offset);
31065 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31066 markerLength = dataView.getUint16(offset + 2) + 2;
31067 if (offset + markerLength > dataView.byteLength) {
31068 Roo.log('Invalid meta data: Invalid segment size.');
31072 if(markerBytes == 0xffe1){
31073 _this.parseExifData(
31080 offset += markerLength;
31090 var url = _this.urlAPI.createObjectURL(_this.file);
31092 _this.loadCanvas(url);
31097 reader.readAsArrayBuffer(this.file);
31103 parseExifData : function(dataView, offset, length)
31105 var tiffOffset = offset + 10,
31109 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31110 // No Exif data, might be XMP data instead
31114 // Check for the ASCII code for "Exif" (0x45786966):
31115 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31116 // No Exif data, might be XMP data instead
31119 if (tiffOffset + 8 > dataView.byteLength) {
31120 Roo.log('Invalid Exif data: Invalid segment size.');
31123 // Check for the two null bytes:
31124 if (dataView.getUint16(offset + 8) !== 0x0000) {
31125 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31128 // Check the byte alignment:
31129 switch (dataView.getUint16(tiffOffset)) {
31131 littleEndian = true;
31134 littleEndian = false;
31137 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31140 // Check for the TIFF tag marker (0x002A):
31141 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31142 Roo.log('Invalid Exif data: Missing TIFF marker.');
31145 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31146 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31148 this.parseExifTags(
31151 tiffOffset + dirOffset,
31156 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31161 if (dirOffset + 6 > dataView.byteLength) {
31162 Roo.log('Invalid Exif data: Invalid directory offset.');
31165 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31166 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31167 if (dirEndOffset + 4 > dataView.byteLength) {
31168 Roo.log('Invalid Exif data: Invalid directory size.');
31171 for (i = 0; i < tagsNumber; i += 1) {
31175 dirOffset + 2 + 12 * i, // tag offset
31179 // Return the offset to the next directory:
31180 return dataView.getUint32(dirEndOffset, littleEndian);
31183 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31185 var tag = dataView.getUint16(offset, littleEndian);
31187 this.exif[tag] = this.getExifValue(
31191 dataView.getUint16(offset + 2, littleEndian), // tag type
31192 dataView.getUint32(offset + 4, littleEndian), // tag length
31197 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31199 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31208 Roo.log('Invalid Exif data: Invalid tag type.');
31212 tagSize = tagType.size * length;
31213 // Determine if the value is contained in the dataOffset bytes,
31214 // or if the value at the dataOffset is a pointer to the actual data:
31215 dataOffset = tagSize > 4 ?
31216 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31217 if (dataOffset + tagSize > dataView.byteLength) {
31218 Roo.log('Invalid Exif data: Invalid data offset.');
31221 if (length === 1) {
31222 return tagType.getValue(dataView, dataOffset, littleEndian);
31225 for (i = 0; i < length; i += 1) {
31226 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31229 if (tagType.ascii) {
31231 // Concatenate the chars:
31232 for (i = 0; i < values.length; i += 1) {
31234 // Ignore the terminating NULL byte(s):
31235 if (c === '\u0000') {
31247 Roo.apply(Roo.bootstrap.UploadCropbox, {
31249 'Orientation': 0x0112
31253 1: 0, //'top-left',
31255 3: 180, //'bottom-right',
31256 // 4: 'bottom-left',
31258 6: 90, //'right-top',
31259 // 7: 'right-bottom',
31260 8: 270 //'left-bottom'
31264 // byte, 8-bit unsigned int:
31266 getValue: function (dataView, dataOffset) {
31267 return dataView.getUint8(dataOffset);
31271 // ascii, 8-bit byte:
31273 getValue: function (dataView, dataOffset) {
31274 return String.fromCharCode(dataView.getUint8(dataOffset));
31279 // short, 16 bit int:
31281 getValue: function (dataView, dataOffset, littleEndian) {
31282 return dataView.getUint16(dataOffset, littleEndian);
31286 // long, 32 bit int:
31288 getValue: function (dataView, dataOffset, littleEndian) {
31289 return dataView.getUint32(dataOffset, littleEndian);
31293 // rational = two long values, first is numerator, second is denominator:
31295 getValue: function (dataView, dataOffset, littleEndian) {
31296 return dataView.getUint32(dataOffset, littleEndian) /
31297 dataView.getUint32(dataOffset + 4, littleEndian);
31301 // slong, 32 bit signed int:
31303 getValue: function (dataView, dataOffset, littleEndian) {
31304 return dataView.getInt32(dataOffset, littleEndian);
31308 // srational, two slongs, first is numerator, second is denominator:
31310 getValue: function (dataView, dataOffset, littleEndian) {
31311 return dataView.getInt32(dataOffset, littleEndian) /
31312 dataView.getInt32(dataOffset + 4, littleEndian);
31322 cls : 'btn-group roo-upload-cropbox-rotate-left',
31323 action : 'rotate-left',
31327 cls : 'btn btn-default',
31328 html : '<i class="fa fa-undo"></i>'
31334 cls : 'btn-group roo-upload-cropbox-picture',
31335 action : 'picture',
31339 cls : 'btn btn-default',
31340 html : '<i class="fa fa-picture-o"></i>'
31346 cls : 'btn-group roo-upload-cropbox-rotate-right',
31347 action : 'rotate-right',
31351 cls : 'btn btn-default',
31352 html : '<i class="fa fa-repeat"></i>'
31360 cls : 'btn-group roo-upload-cropbox-rotate-left',
31361 action : 'rotate-left',
31365 cls : 'btn btn-default',
31366 html : '<i class="fa fa-undo"></i>'
31372 cls : 'btn-group roo-upload-cropbox-download',
31373 action : 'download',
31377 cls : 'btn btn-default',
31378 html : '<i class="fa fa-download"></i>'
31384 cls : 'btn-group roo-upload-cropbox-crop',
31389 cls : 'btn btn-default',
31390 html : '<i class="fa fa-crop"></i>'
31396 cls : 'btn-group roo-upload-cropbox-trash',
31401 cls : 'btn btn-default',
31402 html : '<i class="fa fa-trash"></i>'
31408 cls : 'btn-group roo-upload-cropbox-rotate-right',
31409 action : 'rotate-right',
31413 cls : 'btn btn-default',
31414 html : '<i class="fa fa-repeat"></i>'
31422 cls : 'btn-group roo-upload-cropbox-rotate-left',
31423 action : 'rotate-left',
31427 cls : 'btn btn-default',
31428 html : '<i class="fa fa-undo"></i>'
31434 cls : 'btn-group roo-upload-cropbox-rotate-right',
31435 action : 'rotate-right',
31439 cls : 'btn btn-default',
31440 html : '<i class="fa fa-repeat"></i>'
31453 * @class Roo.bootstrap.DocumentManager
31454 * @extends Roo.bootstrap.Component
31455 * Bootstrap DocumentManager class
31456 * @cfg {String} paramName default 'imageUpload'
31457 * @cfg {String} toolTipName default 'filename'
31458 * @cfg {String} method default POST
31459 * @cfg {String} url action url
31460 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31461 * @cfg {Boolean} multiple multiple upload default true
31462 * @cfg {Number} thumbSize default 300
31463 * @cfg {String} fieldLabel
31464 * @cfg {Number} labelWidth default 4
31465 * @cfg {String} labelAlign (left|top) default left
31466 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31467 * @cfg {Number} labellg set the width of label (1-12)
31468 * @cfg {Number} labelmd set the width of label (1-12)
31469 * @cfg {Number} labelsm set the width of label (1-12)
31470 * @cfg {Number} labelxs set the width of label (1-12)
31473 * Create a new DocumentManager
31474 * @param {Object} config The config object
31477 Roo.bootstrap.DocumentManager = function(config){
31478 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31481 this.delegates = [];
31486 * Fire when initial the DocumentManager
31487 * @param {Roo.bootstrap.DocumentManager} this
31492 * inspect selected file
31493 * @param {Roo.bootstrap.DocumentManager} this
31494 * @param {File} file
31499 * Fire when xhr load exception
31500 * @param {Roo.bootstrap.DocumentManager} this
31501 * @param {XMLHttpRequest} xhr
31503 "exception" : true,
31505 * @event afterupload
31506 * Fire when xhr load exception
31507 * @param {Roo.bootstrap.DocumentManager} this
31508 * @param {XMLHttpRequest} xhr
31510 "afterupload" : true,
31513 * prepare the form data
31514 * @param {Roo.bootstrap.DocumentManager} this
31515 * @param {Object} formData
31520 * Fire when remove the file
31521 * @param {Roo.bootstrap.DocumentManager} this
31522 * @param {Object} file
31527 * Fire after refresh the file
31528 * @param {Roo.bootstrap.DocumentManager} this
31533 * Fire after click the image
31534 * @param {Roo.bootstrap.DocumentManager} this
31535 * @param {Object} file
31540 * Fire when upload a image and editable set to true
31541 * @param {Roo.bootstrap.DocumentManager} this
31542 * @param {Object} file
31546 * @event beforeselectfile
31547 * Fire before select file
31548 * @param {Roo.bootstrap.DocumentManager} this
31550 "beforeselectfile" : true,
31553 * Fire before process file
31554 * @param {Roo.bootstrap.DocumentManager} this
31555 * @param {Object} file
31559 * @event previewrendered
31560 * Fire when preview rendered
31561 * @param {Roo.bootstrap.DocumentManager} this
31562 * @param {Object} file
31564 "previewrendered" : true,
31567 "previewResize" : true
31572 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31581 paramName : 'imageUpload',
31582 toolTipName : 'filename',
31585 labelAlign : 'left',
31595 getAutoCreate : function()
31597 var managerWidget = {
31599 cls : 'roo-document-manager',
31603 cls : 'roo-document-manager-selector',
31608 cls : 'roo-document-manager-uploader',
31612 cls : 'roo-document-manager-upload-btn',
31613 html : '<i class="fa fa-plus"></i>'
31624 cls : 'column col-md-12',
31629 if(this.fieldLabel.length){
31634 cls : 'column col-md-12',
31635 html : this.fieldLabel
31639 cls : 'column col-md-12',
31644 if(this.labelAlign == 'left'){
31649 html : this.fieldLabel
31658 if(this.labelWidth > 12){
31659 content[0].style = "width: " + this.labelWidth + 'px';
31662 if(this.labelWidth < 13 && this.labelmd == 0){
31663 this.labelmd = this.labelWidth;
31666 if(this.labellg > 0){
31667 content[0].cls += ' col-lg-' + this.labellg;
31668 content[1].cls += ' col-lg-' + (12 - this.labellg);
31671 if(this.labelmd > 0){
31672 content[0].cls += ' col-md-' + this.labelmd;
31673 content[1].cls += ' col-md-' + (12 - this.labelmd);
31676 if(this.labelsm > 0){
31677 content[0].cls += ' col-sm-' + this.labelsm;
31678 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31681 if(this.labelxs > 0){
31682 content[0].cls += ' col-xs-' + this.labelxs;
31683 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31691 cls : 'row clearfix',
31699 initEvents : function()
31701 this.managerEl = this.el.select('.roo-document-manager', true).first();
31702 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31704 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31705 this.selectorEl.hide();
31708 this.selectorEl.attr('multiple', 'multiple');
31711 this.selectorEl.on('change', this.onFileSelected, this);
31713 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31714 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31716 this.uploader.on('click', this.onUploaderClick, this);
31718 this.renderProgressDialog();
31722 window.addEventListener("resize", function() { _this.refresh(); } );
31724 this.fireEvent('initial', this);
31727 renderProgressDialog : function()
31731 this.progressDialog = new Roo.bootstrap.Modal({
31732 cls : 'roo-document-manager-progress-dialog',
31733 allow_close : false,
31744 btnclick : function() {
31745 _this.uploadCancel();
31751 this.progressDialog.render(Roo.get(document.body));
31753 this.progress = new Roo.bootstrap.Progress({
31754 cls : 'roo-document-manager-progress',
31759 this.progress.render(this.progressDialog.getChildContainer());
31761 this.progressBar = new Roo.bootstrap.ProgressBar({
31762 cls : 'roo-document-manager-progress-bar',
31765 aria_valuemax : 12,
31769 this.progressBar.render(this.progress.getChildContainer());
31772 onUploaderClick : function(e)
31774 e.preventDefault();
31776 if(this.fireEvent('beforeselectfile', this) != false){
31777 this.selectorEl.dom.click();
31782 onFileSelected : function(e)
31784 e.preventDefault();
31786 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31790 Roo.each(this.selectorEl.dom.files, function(file){
31791 if(this.fireEvent('inspect', this, file) != false){
31792 this.files.push(file);
31802 this.selectorEl.dom.value = '';
31804 if(!this.files || !this.files.length){
31808 if(this.boxes > 0 && this.files.length > this.boxes){
31809 this.files = this.files.slice(0, this.boxes);
31812 this.uploader.show();
31814 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31815 this.uploader.hide();
31824 Roo.each(this.files, function(file){
31826 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31827 var f = this.renderPreview(file);
31832 if(file.type.indexOf('image') != -1){
31833 this.delegates.push(
31835 _this.process(file);
31836 }).createDelegate(this)
31844 _this.process(file);
31845 }).createDelegate(this)
31850 this.files = files;
31852 this.delegates = this.delegates.concat(docs);
31854 if(!this.delegates.length){
31859 this.progressBar.aria_valuemax = this.delegates.length;
31866 arrange : function()
31868 if(!this.delegates.length){
31869 this.progressDialog.hide();
31874 var delegate = this.delegates.shift();
31876 this.progressDialog.show();
31878 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31880 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31885 refresh : function()
31887 this.uploader.show();
31889 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31890 this.uploader.hide();
31893 Roo.isTouch ? this.closable(false) : this.closable(true);
31895 this.fireEvent('refresh', this);
31898 onRemove : function(e, el, o)
31900 e.preventDefault();
31902 this.fireEvent('remove', this, o);
31906 remove : function(o)
31910 Roo.each(this.files, function(file){
31911 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31920 this.files = files;
31927 Roo.each(this.files, function(file){
31932 file.target.remove();
31941 onClick : function(e, el, o)
31943 e.preventDefault();
31945 this.fireEvent('click', this, o);
31949 closable : function(closable)
31951 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31953 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31965 xhrOnLoad : function(xhr)
31967 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31971 if (xhr.readyState !== 4) {
31973 this.fireEvent('exception', this, xhr);
31977 var response = Roo.decode(xhr.responseText);
31979 if(!response.success){
31981 this.fireEvent('exception', this, xhr);
31985 var file = this.renderPreview(response.data);
31987 this.files.push(file);
31991 this.fireEvent('afterupload', this, xhr);
31995 xhrOnError : function(xhr)
31997 Roo.log('xhr on error');
31999 var response = Roo.decode(xhr.responseText);
32006 process : function(file)
32008 if(this.fireEvent('process', this, file) !== false){
32009 if(this.editable && file.type.indexOf('image') != -1){
32010 this.fireEvent('edit', this, file);
32014 this.uploadStart(file, false);
32021 uploadStart : function(file, crop)
32023 this.xhr = new XMLHttpRequest();
32025 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32030 file.xhr = this.xhr;
32032 this.managerEl.createChild({
32034 cls : 'roo-document-manager-loading',
32038 tooltip : file.name,
32039 cls : 'roo-document-manager-thumb',
32040 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32046 this.xhr.open(this.method, this.url, true);
32049 "Accept": "application/json",
32050 "Cache-Control": "no-cache",
32051 "X-Requested-With": "XMLHttpRequest"
32054 for (var headerName in headers) {
32055 var headerValue = headers[headerName];
32057 this.xhr.setRequestHeader(headerName, headerValue);
32063 this.xhr.onload = function()
32065 _this.xhrOnLoad(_this.xhr);
32068 this.xhr.onerror = function()
32070 _this.xhrOnError(_this.xhr);
32073 var formData = new FormData();
32075 formData.append('returnHTML', 'NO');
32078 formData.append('crop', crop);
32081 formData.append(this.paramName, file, file.name);
32088 if(this.fireEvent('prepare', this, formData, options) != false){
32090 if(options.manually){
32094 this.xhr.send(formData);
32098 this.uploadCancel();
32101 uploadCancel : function()
32107 this.delegates = [];
32109 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32116 renderPreview : function(file)
32118 if(typeof(file.target) != 'undefined' && file.target){
32122 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32124 var previewEl = this.managerEl.createChild({
32126 cls : 'roo-document-manager-preview',
32130 tooltip : file[this.toolTipName],
32131 cls : 'roo-document-manager-thumb',
32132 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32137 html : '<i class="fa fa-times-circle"></i>'
32142 var close = previewEl.select('button.close', true).first();
32144 close.on('click', this.onRemove, this, file);
32146 file.target = previewEl;
32148 var image = previewEl.select('img', true).first();
32152 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32154 image.on('click', this.onClick, this, file);
32156 this.fireEvent('previewrendered', this, file);
32162 onPreviewLoad : function(file, image)
32164 if(typeof(file.target) == 'undefined' || !file.target){
32168 var width = image.dom.naturalWidth || image.dom.width;
32169 var height = image.dom.naturalHeight || image.dom.height;
32171 if(!this.previewResize) {
32175 if(width > height){
32176 file.target.addClass('wide');
32180 file.target.addClass('tall');
32185 uploadFromSource : function(file, crop)
32187 this.xhr = new XMLHttpRequest();
32189 this.managerEl.createChild({
32191 cls : 'roo-document-manager-loading',
32195 tooltip : file.name,
32196 cls : 'roo-document-manager-thumb',
32197 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32203 this.xhr.open(this.method, this.url, true);
32206 "Accept": "application/json",
32207 "Cache-Control": "no-cache",
32208 "X-Requested-With": "XMLHttpRequest"
32211 for (var headerName in headers) {
32212 var headerValue = headers[headerName];
32214 this.xhr.setRequestHeader(headerName, headerValue);
32220 this.xhr.onload = function()
32222 _this.xhrOnLoad(_this.xhr);
32225 this.xhr.onerror = function()
32227 _this.xhrOnError(_this.xhr);
32230 var formData = new FormData();
32232 formData.append('returnHTML', 'NO');
32234 formData.append('crop', crop);
32236 if(typeof(file.filename) != 'undefined'){
32237 formData.append('filename', file.filename);
32240 if(typeof(file.mimetype) != 'undefined'){
32241 formData.append('mimetype', file.mimetype);
32246 if(this.fireEvent('prepare', this, formData) != false){
32247 this.xhr.send(formData);
32257 * @class Roo.bootstrap.DocumentViewer
32258 * @extends Roo.bootstrap.Component
32259 * Bootstrap DocumentViewer class
32260 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32261 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32264 * Create a new DocumentViewer
32265 * @param {Object} config The config object
32268 Roo.bootstrap.DocumentViewer = function(config){
32269 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32274 * Fire after initEvent
32275 * @param {Roo.bootstrap.DocumentViewer} this
32281 * @param {Roo.bootstrap.DocumentViewer} this
32286 * Fire after download button
32287 * @param {Roo.bootstrap.DocumentViewer} this
32292 * Fire after trash button
32293 * @param {Roo.bootstrap.DocumentViewer} this
32300 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32302 showDownload : true,
32306 getAutoCreate : function()
32310 cls : 'roo-document-viewer',
32314 cls : 'roo-document-viewer-body',
32318 cls : 'roo-document-viewer-thumb',
32322 cls : 'roo-document-viewer-image'
32330 cls : 'roo-document-viewer-footer',
32333 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32337 cls : 'btn-group roo-document-viewer-download',
32341 cls : 'btn btn-default',
32342 html : '<i class="fa fa-download"></i>'
32348 cls : 'btn-group roo-document-viewer-trash',
32352 cls : 'btn btn-default',
32353 html : '<i class="fa fa-trash"></i>'
32366 initEvents : function()
32368 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32369 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32371 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32372 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32374 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32375 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32377 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32378 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32380 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32381 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32383 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32384 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32386 this.bodyEl.on('click', this.onClick, this);
32387 this.downloadBtn.on('click', this.onDownload, this);
32388 this.trashBtn.on('click', this.onTrash, this);
32390 this.downloadBtn.hide();
32391 this.trashBtn.hide();
32393 if(this.showDownload){
32394 this.downloadBtn.show();
32397 if(this.showTrash){
32398 this.trashBtn.show();
32401 if(!this.showDownload && !this.showTrash) {
32402 this.footerEl.hide();
32407 initial : function()
32409 this.fireEvent('initial', this);
32413 onClick : function(e)
32415 e.preventDefault();
32417 this.fireEvent('click', this);
32420 onDownload : function(e)
32422 e.preventDefault();
32424 this.fireEvent('download', this);
32427 onTrash : function(e)
32429 e.preventDefault();
32431 this.fireEvent('trash', this);
32443 * @class Roo.bootstrap.NavProgressBar
32444 * @extends Roo.bootstrap.Component
32445 * Bootstrap NavProgressBar class
32448 * Create a new nav progress bar
32449 * @param {Object} config The config object
32452 Roo.bootstrap.NavProgressBar = function(config){
32453 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32455 this.bullets = this.bullets || [];
32457 // Roo.bootstrap.NavProgressBar.register(this);
32461 * Fires when the active item changes
32462 * @param {Roo.bootstrap.NavProgressBar} this
32463 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32464 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32471 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32476 getAutoCreate : function()
32478 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32482 cls : 'roo-navigation-bar-group',
32486 cls : 'roo-navigation-top-bar'
32490 cls : 'roo-navigation-bullets-bar',
32494 cls : 'roo-navigation-bar'
32501 cls : 'roo-navigation-bottom-bar'
32511 initEvents: function()
32516 onRender : function(ct, position)
32518 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32520 if(this.bullets.length){
32521 Roo.each(this.bullets, function(b){
32530 addItem : function(cfg)
32532 var item = new Roo.bootstrap.NavProgressItem(cfg);
32534 item.parentId = this.id;
32535 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32538 var top = new Roo.bootstrap.Element({
32540 cls : 'roo-navigation-bar-text'
32543 var bottom = new Roo.bootstrap.Element({
32545 cls : 'roo-navigation-bar-text'
32548 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32549 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32551 var topText = new Roo.bootstrap.Element({
32553 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32556 var bottomText = new Roo.bootstrap.Element({
32558 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32561 topText.onRender(top.el, null);
32562 bottomText.onRender(bottom.el, null);
32565 item.bottomEl = bottom;
32568 this.barItems.push(item);
32573 getActive : function()
32575 var active = false;
32577 Roo.each(this.barItems, function(v){
32579 if (!v.isActive()) {
32591 setActiveItem : function(item)
32595 Roo.each(this.barItems, function(v){
32596 if (v.rid == item.rid) {
32600 if (v.isActive()) {
32601 v.setActive(false);
32606 item.setActive(true);
32608 this.fireEvent('changed', this, item, prev);
32611 getBarItem: function(rid)
32615 Roo.each(this.barItems, function(e) {
32616 if (e.rid != rid) {
32627 indexOfItem : function(item)
32631 Roo.each(this.barItems, function(v, i){
32633 if (v.rid != item.rid) {
32644 setActiveNext : function()
32646 var i = this.indexOfItem(this.getActive());
32648 if (i > this.barItems.length) {
32652 this.setActiveItem(this.barItems[i+1]);
32655 setActivePrev : function()
32657 var i = this.indexOfItem(this.getActive());
32663 this.setActiveItem(this.barItems[i-1]);
32666 format : function()
32668 if(!this.barItems.length){
32672 var width = 100 / this.barItems.length;
32674 Roo.each(this.barItems, function(i){
32675 i.el.setStyle('width', width + '%');
32676 i.topEl.el.setStyle('width', width + '%');
32677 i.bottomEl.el.setStyle('width', width + '%');
32686 * Nav Progress Item
32691 * @class Roo.bootstrap.NavProgressItem
32692 * @extends Roo.bootstrap.Component
32693 * Bootstrap NavProgressItem class
32694 * @cfg {String} rid the reference id
32695 * @cfg {Boolean} active (true|false) Is item active default false
32696 * @cfg {Boolean} disabled (true|false) Is item active default false
32697 * @cfg {String} html
32698 * @cfg {String} position (top|bottom) text position default bottom
32699 * @cfg {String} icon show icon instead of number
32702 * Create a new NavProgressItem
32703 * @param {Object} config The config object
32705 Roo.bootstrap.NavProgressItem = function(config){
32706 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32711 * The raw click event for the entire grid.
32712 * @param {Roo.bootstrap.NavProgressItem} this
32713 * @param {Roo.EventObject} e
32720 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32726 position : 'bottom',
32729 getAutoCreate : function()
32731 var iconCls = 'roo-navigation-bar-item-icon';
32733 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32737 cls: 'roo-navigation-bar-item',
32747 cfg.cls += ' active';
32750 cfg.cls += ' disabled';
32756 disable : function()
32758 this.setDisabled(true);
32761 enable : function()
32763 this.setDisabled(false);
32766 initEvents: function()
32768 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32770 this.iconEl.on('click', this.onClick, this);
32773 onClick : function(e)
32775 e.preventDefault();
32781 if(this.fireEvent('click', this, e) === false){
32785 this.parent().setActiveItem(this);
32788 isActive: function ()
32790 return this.active;
32793 setActive : function(state)
32795 if(this.active == state){
32799 this.active = state;
32802 this.el.addClass('active');
32806 this.el.removeClass('active');
32811 setDisabled : function(state)
32813 if(this.disabled == state){
32817 this.disabled = state;
32820 this.el.addClass('disabled');
32824 this.el.removeClass('disabled');
32827 tooltipEl : function()
32829 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32842 * @class Roo.bootstrap.FieldLabel
32843 * @extends Roo.bootstrap.Component
32844 * Bootstrap FieldLabel class
32845 * @cfg {String} html contents of the element
32846 * @cfg {String} tag tag of the element default label
32847 * @cfg {String} cls class of the element
32848 * @cfg {String} target label target
32849 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32850 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32851 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32852 * @cfg {String} iconTooltip default "This field is required"
32853 * @cfg {String} indicatorpos (left|right) default left
32856 * Create a new FieldLabel
32857 * @param {Object} config The config object
32860 Roo.bootstrap.FieldLabel = function(config){
32861 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32866 * Fires after the field has been marked as invalid.
32867 * @param {Roo.form.FieldLabel} this
32868 * @param {String} msg The validation message
32873 * Fires after the field has been validated with no errors.
32874 * @param {Roo.form.FieldLabel} this
32880 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32887 invalidClass : 'has-warning',
32888 validClass : 'has-success',
32889 iconTooltip : 'This field is required',
32890 indicatorpos : 'left',
32892 getAutoCreate : function(){
32895 if (!this.allowBlank) {
32901 cls : 'roo-bootstrap-field-label ' + this.cls,
32906 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32907 tooltip : this.iconTooltip
32916 if(this.indicatorpos == 'right'){
32919 cls : 'roo-bootstrap-field-label ' + this.cls,
32928 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32929 tooltip : this.iconTooltip
32938 initEvents: function()
32940 Roo.bootstrap.Element.superclass.initEvents.call(this);
32942 this.indicator = this.indicatorEl();
32944 if(this.indicator){
32945 this.indicator.removeClass('visible');
32946 this.indicator.addClass('invisible');
32949 Roo.bootstrap.FieldLabel.register(this);
32952 indicatorEl : function()
32954 var indicator = this.el.select('i.roo-required-indicator',true).first();
32965 * Mark this field as valid
32967 markValid : function()
32969 if(this.indicator){
32970 this.indicator.removeClass('visible');
32971 this.indicator.addClass('invisible');
32973 if (Roo.bootstrap.version == 3) {
32974 this.el.removeClass(this.invalidClass);
32975 this.el.addClass(this.validClass);
32977 this.el.removeClass('is-invalid');
32978 this.el.addClass('is-valid');
32982 this.fireEvent('valid', this);
32986 * Mark this field as invalid
32987 * @param {String} msg The validation message
32989 markInvalid : function(msg)
32991 if(this.indicator){
32992 this.indicator.removeClass('invisible');
32993 this.indicator.addClass('visible');
32995 if (Roo.bootstrap.version == 3) {
32996 this.el.removeClass(this.validClass);
32997 this.el.addClass(this.invalidClass);
32999 this.el.removeClass('is-valid');
33000 this.el.addClass('is-invalid');
33004 this.fireEvent('invalid', this, msg);
33010 Roo.apply(Roo.bootstrap.FieldLabel, {
33015 * register a FieldLabel Group
33016 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33018 register : function(label)
33020 if(this.groups.hasOwnProperty(label.target)){
33024 this.groups[label.target] = label;
33028 * fetch a FieldLabel Group based on the target
33029 * @param {string} target
33030 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33032 get: function(target) {
33033 if (typeof(this.groups[target]) == 'undefined') {
33037 return this.groups[target] ;
33046 * page DateSplitField.
33052 * @class Roo.bootstrap.DateSplitField
33053 * @extends Roo.bootstrap.Component
33054 * Bootstrap DateSplitField class
33055 * @cfg {string} fieldLabel - the label associated
33056 * @cfg {Number} labelWidth set the width of label (0-12)
33057 * @cfg {String} labelAlign (top|left)
33058 * @cfg {Boolean} dayAllowBlank (true|false) default false
33059 * @cfg {Boolean} monthAllowBlank (true|false) default false
33060 * @cfg {Boolean} yearAllowBlank (true|false) default false
33061 * @cfg {string} dayPlaceholder
33062 * @cfg {string} monthPlaceholder
33063 * @cfg {string} yearPlaceholder
33064 * @cfg {string} dayFormat default 'd'
33065 * @cfg {string} monthFormat default 'm'
33066 * @cfg {string} yearFormat default 'Y'
33067 * @cfg {Number} labellg set the width of label (1-12)
33068 * @cfg {Number} labelmd set the width of label (1-12)
33069 * @cfg {Number} labelsm set the width of label (1-12)
33070 * @cfg {Number} labelxs set the width of label (1-12)
33074 * Create a new DateSplitField
33075 * @param {Object} config The config object
33078 Roo.bootstrap.DateSplitField = function(config){
33079 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33085 * getting the data of years
33086 * @param {Roo.bootstrap.DateSplitField} this
33087 * @param {Object} years
33092 * getting the data of days
33093 * @param {Roo.bootstrap.DateSplitField} this
33094 * @param {Object} days
33099 * Fires after the field has been marked as invalid.
33100 * @param {Roo.form.Field} this
33101 * @param {String} msg The validation message
33106 * Fires after the field has been validated with no errors.
33107 * @param {Roo.form.Field} this
33113 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33116 labelAlign : 'top',
33118 dayAllowBlank : false,
33119 monthAllowBlank : false,
33120 yearAllowBlank : false,
33121 dayPlaceholder : '',
33122 monthPlaceholder : '',
33123 yearPlaceholder : '',
33127 isFormField : true,
33133 getAutoCreate : function()
33137 cls : 'row roo-date-split-field-group',
33142 cls : 'form-hidden-field roo-date-split-field-group-value',
33148 var labelCls = 'col-md-12';
33149 var contentCls = 'col-md-4';
33151 if(this.fieldLabel){
33155 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33159 html : this.fieldLabel
33164 if(this.labelAlign == 'left'){
33166 if(this.labelWidth > 12){
33167 label.style = "width: " + this.labelWidth + 'px';
33170 if(this.labelWidth < 13 && this.labelmd == 0){
33171 this.labelmd = this.labelWidth;
33174 if(this.labellg > 0){
33175 labelCls = ' col-lg-' + this.labellg;
33176 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33179 if(this.labelmd > 0){
33180 labelCls = ' col-md-' + this.labelmd;
33181 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33184 if(this.labelsm > 0){
33185 labelCls = ' col-sm-' + this.labelsm;
33186 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33189 if(this.labelxs > 0){
33190 labelCls = ' col-xs-' + this.labelxs;
33191 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33195 label.cls += ' ' + labelCls;
33197 cfg.cn.push(label);
33200 Roo.each(['day', 'month', 'year'], function(t){
33203 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33210 inputEl: function ()
33212 return this.el.select('.roo-date-split-field-group-value', true).first();
33215 onRender : function(ct, position)
33219 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33221 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33223 this.dayField = new Roo.bootstrap.ComboBox({
33224 allowBlank : this.dayAllowBlank,
33225 alwaysQuery : true,
33226 displayField : 'value',
33229 forceSelection : true,
33231 placeholder : this.dayPlaceholder,
33232 selectOnFocus : true,
33233 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33234 triggerAction : 'all',
33236 valueField : 'value',
33237 store : new Roo.data.SimpleStore({
33238 data : (function() {
33240 _this.fireEvent('days', _this, days);
33243 fields : [ 'value' ]
33246 select : function (_self, record, index)
33248 _this.setValue(_this.getValue());
33253 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33255 this.monthField = new Roo.bootstrap.MonthField({
33256 after : '<i class=\"fa fa-calendar\"></i>',
33257 allowBlank : this.monthAllowBlank,
33258 placeholder : this.monthPlaceholder,
33261 render : function (_self)
33263 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33264 e.preventDefault();
33268 select : function (_self, oldvalue, newvalue)
33270 _this.setValue(_this.getValue());
33275 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33277 this.yearField = new Roo.bootstrap.ComboBox({
33278 allowBlank : this.yearAllowBlank,
33279 alwaysQuery : true,
33280 displayField : 'value',
33283 forceSelection : true,
33285 placeholder : this.yearPlaceholder,
33286 selectOnFocus : true,
33287 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33288 triggerAction : 'all',
33290 valueField : 'value',
33291 store : new Roo.data.SimpleStore({
33292 data : (function() {
33294 _this.fireEvent('years', _this, years);
33297 fields : [ 'value' ]
33300 select : function (_self, record, index)
33302 _this.setValue(_this.getValue());
33307 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33310 setValue : function(v, format)
33312 this.inputEl.dom.value = v;
33314 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33316 var d = Date.parseDate(v, f);
33323 this.setDay(d.format(this.dayFormat));
33324 this.setMonth(d.format(this.monthFormat));
33325 this.setYear(d.format(this.yearFormat));
33332 setDay : function(v)
33334 this.dayField.setValue(v);
33335 this.inputEl.dom.value = this.getValue();
33340 setMonth : function(v)
33342 this.monthField.setValue(v, true);
33343 this.inputEl.dom.value = this.getValue();
33348 setYear : function(v)
33350 this.yearField.setValue(v);
33351 this.inputEl.dom.value = this.getValue();
33356 getDay : function()
33358 return this.dayField.getValue();
33361 getMonth : function()
33363 return this.monthField.getValue();
33366 getYear : function()
33368 return this.yearField.getValue();
33371 getValue : function()
33373 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33375 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33385 this.inputEl.dom.value = '';
33390 validate : function()
33392 var d = this.dayField.validate();
33393 var m = this.monthField.validate();
33394 var y = this.yearField.validate();
33399 (!this.dayAllowBlank && !d) ||
33400 (!this.monthAllowBlank && !m) ||
33401 (!this.yearAllowBlank && !y)
33406 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33415 this.markInvalid();
33420 markValid : function()
33423 var label = this.el.select('label', true).first();
33424 var icon = this.el.select('i.fa-star', true).first();
33430 this.fireEvent('valid', this);
33434 * Mark this field as invalid
33435 * @param {String} msg The validation message
33437 markInvalid : function(msg)
33440 var label = this.el.select('label', true).first();
33441 var icon = this.el.select('i.fa-star', true).first();
33443 if(label && !icon){
33444 this.el.select('.roo-date-split-field-label', true).createChild({
33446 cls : 'text-danger fa fa-lg fa-star',
33447 tooltip : 'This field is required',
33448 style : 'margin-right:5px;'
33452 this.fireEvent('invalid', this, msg);
33455 clearInvalid : function()
33457 var label = this.el.select('label', true).first();
33458 var icon = this.el.select('i.fa-star', true).first();
33464 this.fireEvent('valid', this);
33467 getName: function()
33477 * http://masonry.desandro.com
33479 * The idea is to render all the bricks based on vertical width...
33481 * The original code extends 'outlayer' - we might need to use that....
33487 * @class Roo.bootstrap.LayoutMasonry
33488 * @extends Roo.bootstrap.Component
33489 * Bootstrap Layout Masonry class
33492 * Create a new Element
33493 * @param {Object} config The config object
33496 Roo.bootstrap.LayoutMasonry = function(config){
33498 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33502 Roo.bootstrap.LayoutMasonry.register(this);
33508 * Fire after layout the items
33509 * @param {Roo.bootstrap.LayoutMasonry} this
33510 * @param {Roo.EventObject} e
33517 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33520 * @cfg {Boolean} isLayoutInstant = no animation?
33522 isLayoutInstant : false, // needed?
33525 * @cfg {Number} boxWidth width of the columns
33530 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33535 * @cfg {Number} padWidth padding below box..
33540 * @cfg {Number} gutter gutter width..
33545 * @cfg {Number} maxCols maximum number of columns
33551 * @cfg {Boolean} isAutoInitial defalut true
33553 isAutoInitial : true,
33558 * @cfg {Boolean} isHorizontal defalut false
33560 isHorizontal : false,
33562 currentSize : null,
33568 bricks: null, //CompositeElement
33572 _isLayoutInited : false,
33574 // isAlternative : false, // only use for vertical layout...
33577 * @cfg {Number} alternativePadWidth padding below box..
33579 alternativePadWidth : 50,
33581 selectedBrick : [],
33583 getAutoCreate : function(){
33585 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33589 cls: 'blog-masonary-wrapper ' + this.cls,
33591 cls : 'mas-boxes masonary'
33598 getChildContainer: function( )
33600 if (this.boxesEl) {
33601 return this.boxesEl;
33604 this.boxesEl = this.el.select('.mas-boxes').first();
33606 return this.boxesEl;
33610 initEvents : function()
33614 if(this.isAutoInitial){
33615 Roo.log('hook children rendered');
33616 this.on('childrenrendered', function() {
33617 Roo.log('children rendered');
33623 initial : function()
33625 this.selectedBrick = [];
33627 this.currentSize = this.el.getBox(true);
33629 Roo.EventManager.onWindowResize(this.resize, this);
33631 if(!this.isAutoInitial){
33639 //this.layout.defer(500,this);
33643 resize : function()
33645 var cs = this.el.getBox(true);
33648 this.currentSize.width == cs.width &&
33649 this.currentSize.x == cs.x &&
33650 this.currentSize.height == cs.height &&
33651 this.currentSize.y == cs.y
33653 Roo.log("no change in with or X or Y");
33657 this.currentSize = cs;
33663 layout : function()
33665 this._resetLayout();
33667 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33669 this.layoutItems( isInstant );
33671 this._isLayoutInited = true;
33673 this.fireEvent('layout', this);
33677 _resetLayout : function()
33679 if(this.isHorizontal){
33680 this.horizontalMeasureColumns();
33684 this.verticalMeasureColumns();
33688 verticalMeasureColumns : function()
33690 this.getContainerWidth();
33692 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33693 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33697 var boxWidth = this.boxWidth + this.padWidth;
33699 if(this.containerWidth < this.boxWidth){
33700 boxWidth = this.containerWidth
33703 var containerWidth = this.containerWidth;
33705 var cols = Math.floor(containerWidth / boxWidth);
33707 this.cols = Math.max( cols, 1 );
33709 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33711 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33713 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33715 this.colWidth = boxWidth + avail - this.padWidth;
33717 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33718 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33721 horizontalMeasureColumns : function()
33723 this.getContainerWidth();
33725 var boxWidth = this.boxWidth;
33727 if(this.containerWidth < boxWidth){
33728 boxWidth = this.containerWidth;
33731 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33733 this.el.setHeight(boxWidth);
33737 getContainerWidth : function()
33739 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33742 layoutItems : function( isInstant )
33744 Roo.log(this.bricks);
33746 var items = Roo.apply([], this.bricks);
33748 if(this.isHorizontal){
33749 this._horizontalLayoutItems( items , isInstant );
33753 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33754 // this._verticalAlternativeLayoutItems( items , isInstant );
33758 this._verticalLayoutItems( items , isInstant );
33762 _verticalLayoutItems : function ( items , isInstant)
33764 if ( !items || !items.length ) {
33769 ['xs', 'xs', 'xs', 'tall'],
33770 ['xs', 'xs', 'tall'],
33771 ['xs', 'xs', 'sm'],
33772 ['xs', 'xs', 'xs'],
33778 ['sm', 'xs', 'xs'],
33782 ['tall', 'xs', 'xs', 'xs'],
33783 ['tall', 'xs', 'xs'],
33795 Roo.each(items, function(item, k){
33797 switch (item.size) {
33798 // these layouts take up a full box,
33809 boxes.push([item]);
33832 var filterPattern = function(box, length)
33840 var pattern = box.slice(0, length);
33844 Roo.each(pattern, function(i){
33845 format.push(i.size);
33848 Roo.each(standard, function(s){
33850 if(String(s) != String(format)){
33859 if(!match && length == 1){
33864 filterPattern(box, length - 1);
33868 queue.push(pattern);
33870 box = box.slice(length, box.length);
33872 filterPattern(box, 4);
33878 Roo.each(boxes, function(box, k){
33884 if(box.length == 1){
33889 filterPattern(box, 4);
33893 this._processVerticalLayoutQueue( queue, isInstant );
33897 // _verticalAlternativeLayoutItems : function( items , isInstant )
33899 // if ( !items || !items.length ) {
33903 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33907 _horizontalLayoutItems : function ( items , isInstant)
33909 if ( !items || !items.length || items.length < 3) {
33915 var eItems = items.slice(0, 3);
33917 items = items.slice(3, items.length);
33920 ['xs', 'xs', 'xs', 'wide'],
33921 ['xs', 'xs', 'wide'],
33922 ['xs', 'xs', 'sm'],
33923 ['xs', 'xs', 'xs'],
33929 ['sm', 'xs', 'xs'],
33933 ['wide', 'xs', 'xs', 'xs'],
33934 ['wide', 'xs', 'xs'],
33947 Roo.each(items, function(item, k){
33949 switch (item.size) {
33960 boxes.push([item]);
33984 var filterPattern = function(box, length)
33992 var pattern = box.slice(0, length);
33996 Roo.each(pattern, function(i){
33997 format.push(i.size);
34000 Roo.each(standard, function(s){
34002 if(String(s) != String(format)){
34011 if(!match && length == 1){
34016 filterPattern(box, length - 1);
34020 queue.push(pattern);
34022 box = box.slice(length, box.length);
34024 filterPattern(box, 4);
34030 Roo.each(boxes, function(box, k){
34036 if(box.length == 1){
34041 filterPattern(box, 4);
34048 var pos = this.el.getBox(true);
34052 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34054 var hit_end = false;
34056 Roo.each(queue, function(box){
34060 Roo.each(box, function(b){
34062 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34072 Roo.each(box, function(b){
34074 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34077 mx = Math.max(mx, b.x);
34081 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34085 Roo.each(box, function(b){
34087 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34101 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34104 /** Sets position of item in DOM
34105 * @param {Element} item
34106 * @param {Number} x - horizontal position
34107 * @param {Number} y - vertical position
34108 * @param {Boolean} isInstant - disables transitions
34110 _processVerticalLayoutQueue : function( queue, isInstant )
34112 var pos = this.el.getBox(true);
34117 for (var i = 0; i < this.cols; i++){
34121 Roo.each(queue, function(box, k){
34123 var col = k % this.cols;
34125 Roo.each(box, function(b,kk){
34127 b.el.position('absolute');
34129 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34130 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34132 if(b.size == 'md-left' || b.size == 'md-right'){
34133 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34134 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34137 b.el.setWidth(width);
34138 b.el.setHeight(height);
34140 b.el.select('iframe',true).setSize(width,height);
34144 for (var i = 0; i < this.cols; i++){
34146 if(maxY[i] < maxY[col]){
34151 col = Math.min(col, i);
34155 x = pos.x + col * (this.colWidth + this.padWidth);
34159 var positions = [];
34161 switch (box.length){
34163 positions = this.getVerticalOneBoxColPositions(x, y, box);
34166 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34169 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34172 positions = this.getVerticalFourBoxColPositions(x, y, box);
34178 Roo.each(box, function(b,kk){
34180 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34182 var sz = b.el.getSize();
34184 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34192 for (var i = 0; i < this.cols; i++){
34193 mY = Math.max(mY, maxY[i]);
34196 this.el.setHeight(mY - pos.y);
34200 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34202 // var pos = this.el.getBox(true);
34205 // var maxX = pos.right;
34207 // var maxHeight = 0;
34209 // Roo.each(items, function(item, k){
34213 // item.el.position('absolute');
34215 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34217 // item.el.setWidth(width);
34219 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34221 // item.el.setHeight(height);
34224 // item.el.setXY([x, y], isInstant ? false : true);
34226 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34229 // y = y + height + this.alternativePadWidth;
34231 // maxHeight = maxHeight + height + this.alternativePadWidth;
34235 // this.el.setHeight(maxHeight);
34239 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34241 var pos = this.el.getBox(true);
34246 var maxX = pos.right;
34248 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34250 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34252 Roo.each(queue, function(box, k){
34254 Roo.each(box, function(b, kk){
34256 b.el.position('absolute');
34258 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34259 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34261 if(b.size == 'md-left' || b.size == 'md-right'){
34262 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34263 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34266 b.el.setWidth(width);
34267 b.el.setHeight(height);
34275 var positions = [];
34277 switch (box.length){
34279 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34282 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34285 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34288 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34294 Roo.each(box, function(b,kk){
34296 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34298 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34306 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34308 Roo.each(eItems, function(b,k){
34310 b.size = (k == 0) ? 'sm' : 'xs';
34311 b.x = (k == 0) ? 2 : 1;
34312 b.y = (k == 0) ? 2 : 1;
34314 b.el.position('absolute');
34316 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34318 b.el.setWidth(width);
34320 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34322 b.el.setHeight(height);
34326 var positions = [];
34329 x : maxX - this.unitWidth * 2 - this.gutter,
34334 x : maxX - this.unitWidth,
34335 y : minY + (this.unitWidth + this.gutter) * 2
34339 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34343 Roo.each(eItems, function(b,k){
34345 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34351 getVerticalOneBoxColPositions : function(x, y, box)
34355 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34357 if(box[0].size == 'md-left'){
34361 if(box[0].size == 'md-right'){
34366 x : x + (this.unitWidth + this.gutter) * rand,
34373 getVerticalTwoBoxColPositions : function(x, y, box)
34377 if(box[0].size == 'xs'){
34381 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34385 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34399 x : x + (this.unitWidth + this.gutter) * 2,
34400 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34407 getVerticalThreeBoxColPositions : function(x, y, box)
34411 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34419 x : x + (this.unitWidth + this.gutter) * 1,
34424 x : x + (this.unitWidth + this.gutter) * 2,
34432 if(box[0].size == 'xs' && box[1].size == 'xs'){
34441 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34445 x : x + (this.unitWidth + this.gutter) * 1,
34459 x : x + (this.unitWidth + this.gutter) * 2,
34464 x : x + (this.unitWidth + this.gutter) * 2,
34465 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34472 getVerticalFourBoxColPositions : function(x, y, box)
34476 if(box[0].size == 'xs'){
34485 y : y + (this.unitHeight + this.gutter) * 1
34490 y : y + (this.unitHeight + this.gutter) * 2
34494 x : x + (this.unitWidth + this.gutter) * 1,
34508 x : x + (this.unitWidth + this.gutter) * 2,
34513 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34514 y : y + (this.unitHeight + this.gutter) * 1
34518 x : x + (this.unitWidth + this.gutter) * 2,
34519 y : y + (this.unitWidth + this.gutter) * 2
34526 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34530 if(box[0].size == 'md-left'){
34532 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34539 if(box[0].size == 'md-right'){
34541 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34542 y : minY + (this.unitWidth + this.gutter) * 1
34548 var rand = Math.floor(Math.random() * (4 - box[0].y));
34551 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34552 y : minY + (this.unitWidth + this.gutter) * rand
34559 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34563 if(box[0].size == 'xs'){
34566 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34571 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34572 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34580 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34585 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34586 y : minY + (this.unitWidth + this.gutter) * 2
34593 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34597 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34600 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34605 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34606 y : minY + (this.unitWidth + this.gutter) * 1
34610 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34611 y : minY + (this.unitWidth + this.gutter) * 2
34618 if(box[0].size == 'xs' && box[1].size == 'xs'){
34621 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34626 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34631 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34632 y : minY + (this.unitWidth + this.gutter) * 1
34640 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34645 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34646 y : minY + (this.unitWidth + this.gutter) * 2
34650 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34651 y : minY + (this.unitWidth + this.gutter) * 2
34658 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34662 if(box[0].size == 'xs'){
34665 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34670 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34675 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),
34680 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34681 y : minY + (this.unitWidth + this.gutter) * 1
34689 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34694 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34695 y : minY + (this.unitWidth + this.gutter) * 2
34699 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34700 y : minY + (this.unitWidth + this.gutter) * 2
34704 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),
34705 y : minY + (this.unitWidth + this.gutter) * 2
34713 * remove a Masonry Brick
34714 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34716 removeBrick : function(brick_id)
34722 for (var i = 0; i<this.bricks.length; i++) {
34723 if (this.bricks[i].id == brick_id) {
34724 this.bricks.splice(i,1);
34725 this.el.dom.removeChild(Roo.get(brick_id).dom);
34732 * adds a Masonry Brick
34733 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34735 addBrick : function(cfg)
34737 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34738 //this.register(cn);
34739 cn.parentId = this.id;
34740 cn.render(this.el);
34745 * register a Masonry Brick
34746 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34749 register : function(brick)
34751 this.bricks.push(brick);
34752 brick.masonryId = this.id;
34756 * clear all the Masonry Brick
34758 clearAll : function()
34761 //this.getChildContainer().dom.innerHTML = "";
34762 this.el.dom.innerHTML = '';
34765 getSelected : function()
34767 if (!this.selectedBrick) {
34771 return this.selectedBrick;
34775 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34779 * register a Masonry Layout
34780 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34783 register : function(layout)
34785 this.groups[layout.id] = layout;
34788 * fetch a Masonry Layout based on the masonry layout ID
34789 * @param {string} the masonry layout to add
34790 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34793 get: function(layout_id) {
34794 if (typeof(this.groups[layout_id]) == 'undefined') {
34797 return this.groups[layout_id] ;
34809 * http://masonry.desandro.com
34811 * The idea is to render all the bricks based on vertical width...
34813 * The original code extends 'outlayer' - we might need to use that....
34819 * @class Roo.bootstrap.LayoutMasonryAuto
34820 * @extends Roo.bootstrap.Component
34821 * Bootstrap Layout Masonry class
34824 * Create a new Element
34825 * @param {Object} config The config object
34828 Roo.bootstrap.LayoutMasonryAuto = function(config){
34829 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34832 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34835 * @cfg {Boolean} isFitWidth - resize the width..
34837 isFitWidth : false, // options..
34839 * @cfg {Boolean} isOriginLeft = left align?
34841 isOriginLeft : true,
34843 * @cfg {Boolean} isOriginTop = top align?
34845 isOriginTop : false,
34847 * @cfg {Boolean} isLayoutInstant = no animation?
34849 isLayoutInstant : false, // needed?
34851 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34853 isResizingContainer : true,
34855 * @cfg {Number} columnWidth width of the columns
34861 * @cfg {Number} maxCols maximum number of columns
34866 * @cfg {Number} padHeight padding below box..
34872 * @cfg {Boolean} isAutoInitial defalut true
34875 isAutoInitial : true,
34881 initialColumnWidth : 0,
34882 currentSize : null,
34884 colYs : null, // array.
34891 bricks: null, //CompositeElement
34892 cols : 0, // array?
34893 // element : null, // wrapped now this.el
34894 _isLayoutInited : null,
34897 getAutoCreate : function(){
34901 cls: 'blog-masonary-wrapper ' + this.cls,
34903 cls : 'mas-boxes masonary'
34910 getChildContainer: function( )
34912 if (this.boxesEl) {
34913 return this.boxesEl;
34916 this.boxesEl = this.el.select('.mas-boxes').first();
34918 return this.boxesEl;
34922 initEvents : function()
34926 if(this.isAutoInitial){
34927 Roo.log('hook children rendered');
34928 this.on('childrenrendered', function() {
34929 Roo.log('children rendered');
34936 initial : function()
34938 this.reloadItems();
34940 this.currentSize = this.el.getBox(true);
34942 /// was window resize... - let's see if this works..
34943 Roo.EventManager.onWindowResize(this.resize, this);
34945 if(!this.isAutoInitial){
34950 this.layout.defer(500,this);
34953 reloadItems: function()
34955 this.bricks = this.el.select('.masonry-brick', true);
34957 this.bricks.each(function(b) {
34958 //Roo.log(b.getSize());
34959 if (!b.attr('originalwidth')) {
34960 b.attr('originalwidth', b.getSize().width);
34965 Roo.log(this.bricks.elements.length);
34968 resize : function()
34971 var cs = this.el.getBox(true);
34973 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34974 Roo.log("no change in with or X");
34977 this.currentSize = cs;
34981 layout : function()
34984 this._resetLayout();
34985 //this._manageStamps();
34987 // don't animate first layout
34988 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34989 this.layoutItems( isInstant );
34991 // flag for initalized
34992 this._isLayoutInited = true;
34995 layoutItems : function( isInstant )
34997 //var items = this._getItemsForLayout( this.items );
34998 // original code supports filtering layout items.. we just ignore it..
35000 this._layoutItems( this.bricks , isInstant );
35002 this._postLayout();
35004 _layoutItems : function ( items , isInstant)
35006 //this.fireEvent( 'layout', this, items );
35009 if ( !items || !items.elements.length ) {
35010 // no items, emit event with empty array
35015 items.each(function(item) {
35016 Roo.log("layout item");
35018 // get x/y object from method
35019 var position = this._getItemLayoutPosition( item );
35021 position.item = item;
35022 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35023 queue.push( position );
35026 this._processLayoutQueue( queue );
35028 /** Sets position of item in DOM
35029 * @param {Element} item
35030 * @param {Number} x - horizontal position
35031 * @param {Number} y - vertical position
35032 * @param {Boolean} isInstant - disables transitions
35034 _processLayoutQueue : function( queue )
35036 for ( var i=0, len = queue.length; i < len; i++ ) {
35037 var obj = queue[i];
35038 obj.item.position('absolute');
35039 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35045 * Any logic you want to do after each layout,
35046 * i.e. size the container
35048 _postLayout : function()
35050 this.resizeContainer();
35053 resizeContainer : function()
35055 if ( !this.isResizingContainer ) {
35058 var size = this._getContainerSize();
35060 this.el.setSize(size.width,size.height);
35061 this.boxesEl.setSize(size.width,size.height);
35067 _resetLayout : function()
35069 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35070 this.colWidth = this.el.getWidth();
35071 //this.gutter = this.el.getWidth();
35073 this.measureColumns();
35079 this.colYs.push( 0 );
35085 measureColumns : function()
35087 this.getContainerWidth();
35088 // if columnWidth is 0, default to outerWidth of first item
35089 if ( !this.columnWidth ) {
35090 var firstItem = this.bricks.first();
35091 Roo.log(firstItem);
35092 this.columnWidth = this.containerWidth;
35093 if (firstItem && firstItem.attr('originalwidth') ) {
35094 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35096 // columnWidth fall back to item of first element
35097 Roo.log("set column width?");
35098 this.initialColumnWidth = this.columnWidth ;
35100 // if first elem has no width, default to size of container
35105 if (this.initialColumnWidth) {
35106 this.columnWidth = this.initialColumnWidth;
35111 // column width is fixed at the top - however if container width get's smaller we should
35114 // this bit calcs how man columns..
35116 var columnWidth = this.columnWidth += this.gutter;
35118 // calculate columns
35119 var containerWidth = this.containerWidth + this.gutter;
35121 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35122 // fix rounding errors, typically with gutters
35123 var excess = columnWidth - containerWidth % columnWidth;
35126 // if overshoot is less than a pixel, round up, otherwise floor it
35127 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35128 cols = Math[ mathMethod ]( cols );
35129 this.cols = Math.max( cols, 1 );
35130 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35132 // padding positioning..
35133 var totalColWidth = this.cols * this.columnWidth;
35134 var padavail = this.containerWidth - totalColWidth;
35135 // so for 2 columns - we need 3 'pads'
35137 var padNeeded = (1+this.cols) * this.padWidth;
35139 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35141 this.columnWidth += padExtra
35142 //this.padWidth = Math.floor(padavail / ( this.cols));
35144 // adjust colum width so that padding is fixed??
35146 // we have 3 columns ... total = width * 3
35147 // we have X left over... that should be used by
35149 //if (this.expandC) {
35157 getContainerWidth : function()
35159 /* // container is parent if fit width
35160 var container = this.isFitWidth ? this.element.parentNode : this.element;
35161 // check that this.size and size are there
35162 // IE8 triggers resize on body size change, so they might not be
35164 var size = getSize( container ); //FIXME
35165 this.containerWidth = size && size.innerWidth; //FIXME
35168 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35172 _getItemLayoutPosition : function( item ) // what is item?
35174 // we resize the item to our columnWidth..
35176 item.setWidth(this.columnWidth);
35177 item.autoBoxAdjust = false;
35179 var sz = item.getSize();
35181 // how many columns does this brick span
35182 var remainder = this.containerWidth % this.columnWidth;
35184 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35185 // round if off by 1 pixel, otherwise use ceil
35186 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35187 colSpan = Math.min( colSpan, this.cols );
35189 // normally this should be '1' as we dont' currently allow multi width columns..
35191 var colGroup = this._getColGroup( colSpan );
35192 // get the minimum Y value from the columns
35193 var minimumY = Math.min.apply( Math, colGroup );
35194 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35196 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35198 // position the brick
35200 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35201 y: this.currentSize.y + minimumY + this.padHeight
35205 // apply setHeight to necessary columns
35206 var setHeight = minimumY + sz.height + this.padHeight;
35207 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35209 var setSpan = this.cols + 1 - colGroup.length;
35210 for ( var i = 0; i < setSpan; i++ ) {
35211 this.colYs[ shortColIndex + i ] = setHeight ;
35218 * @param {Number} colSpan - number of columns the element spans
35219 * @returns {Array} colGroup
35221 _getColGroup : function( colSpan )
35223 if ( colSpan < 2 ) {
35224 // if brick spans only one column, use all the column Ys
35229 // how many different places could this brick fit horizontally
35230 var groupCount = this.cols + 1 - colSpan;
35231 // for each group potential horizontal position
35232 for ( var i = 0; i < groupCount; i++ ) {
35233 // make an array of colY values for that one group
35234 var groupColYs = this.colYs.slice( i, i + colSpan );
35235 // and get the max value of the array
35236 colGroup[i] = Math.max.apply( Math, groupColYs );
35241 _manageStamp : function( stamp )
35243 var stampSize = stamp.getSize();
35244 var offset = stamp.getBox();
35245 // get the columns that this stamp affects
35246 var firstX = this.isOriginLeft ? offset.x : offset.right;
35247 var lastX = firstX + stampSize.width;
35248 var firstCol = Math.floor( firstX / this.columnWidth );
35249 firstCol = Math.max( 0, firstCol );
35251 var lastCol = Math.floor( lastX / this.columnWidth );
35252 // lastCol should not go over if multiple of columnWidth #425
35253 lastCol -= lastX % this.columnWidth ? 0 : 1;
35254 lastCol = Math.min( this.cols - 1, lastCol );
35256 // set colYs to bottom of the stamp
35257 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35260 for ( var i = firstCol; i <= lastCol; i++ ) {
35261 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35266 _getContainerSize : function()
35268 this.maxY = Math.max.apply( Math, this.colYs );
35273 if ( this.isFitWidth ) {
35274 size.width = this._getContainerFitWidth();
35280 _getContainerFitWidth : function()
35282 var unusedCols = 0;
35283 // count unused columns
35286 if ( this.colYs[i] !== 0 ) {
35291 // fit container to columns that have been used
35292 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35295 needsResizeLayout : function()
35297 var previousWidth = this.containerWidth;
35298 this.getContainerWidth();
35299 return previousWidth !== this.containerWidth;
35314 * @class Roo.bootstrap.MasonryBrick
35315 * @extends Roo.bootstrap.Component
35316 * Bootstrap MasonryBrick class
35319 * Create a new MasonryBrick
35320 * @param {Object} config The config object
35323 Roo.bootstrap.MasonryBrick = function(config){
35325 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35327 Roo.bootstrap.MasonryBrick.register(this);
35333 * When a MasonryBrick is clcik
35334 * @param {Roo.bootstrap.MasonryBrick} this
35335 * @param {Roo.EventObject} e
35341 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35344 * @cfg {String} title
35348 * @cfg {String} html
35352 * @cfg {String} bgimage
35356 * @cfg {String} videourl
35360 * @cfg {String} cls
35364 * @cfg {String} href
35368 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35373 * @cfg {String} placetitle (center|bottom)
35378 * @cfg {Boolean} isFitContainer defalut true
35380 isFitContainer : true,
35383 * @cfg {Boolean} preventDefault defalut false
35385 preventDefault : false,
35388 * @cfg {Boolean} inverse defalut false
35390 maskInverse : false,
35392 getAutoCreate : function()
35394 if(!this.isFitContainer){
35395 return this.getSplitAutoCreate();
35398 var cls = 'masonry-brick masonry-brick-full';
35400 if(this.href.length){
35401 cls += ' masonry-brick-link';
35404 if(this.bgimage.length){
35405 cls += ' masonry-brick-image';
35408 if(this.maskInverse){
35409 cls += ' mask-inverse';
35412 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35413 cls += ' enable-mask';
35417 cls += ' masonry-' + this.size + '-brick';
35420 if(this.placetitle.length){
35422 switch (this.placetitle) {
35424 cls += ' masonry-center-title';
35427 cls += ' masonry-bottom-title';
35434 if(!this.html.length && !this.bgimage.length){
35435 cls += ' masonry-center-title';
35438 if(!this.html.length && this.bgimage.length){
35439 cls += ' masonry-bottom-title';
35444 cls += ' ' + this.cls;
35448 tag: (this.href.length) ? 'a' : 'div',
35453 cls: 'masonry-brick-mask'
35457 cls: 'masonry-brick-paragraph',
35463 if(this.href.length){
35464 cfg.href = this.href;
35467 var cn = cfg.cn[1].cn;
35469 if(this.title.length){
35472 cls: 'masonry-brick-title',
35477 if(this.html.length){
35480 cls: 'masonry-brick-text',
35485 if (!this.title.length && !this.html.length) {
35486 cfg.cn[1].cls += ' hide';
35489 if(this.bgimage.length){
35492 cls: 'masonry-brick-image-view',
35497 if(this.videourl.length){
35498 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35499 // youtube support only?
35502 cls: 'masonry-brick-image-view',
35505 allowfullscreen : true
35513 getSplitAutoCreate : function()
35515 var cls = 'masonry-brick masonry-brick-split';
35517 if(this.href.length){
35518 cls += ' masonry-brick-link';
35521 if(this.bgimage.length){
35522 cls += ' masonry-brick-image';
35526 cls += ' masonry-' + this.size + '-brick';
35529 switch (this.placetitle) {
35531 cls += ' masonry-center-title';
35534 cls += ' masonry-bottom-title';
35537 if(!this.bgimage.length){
35538 cls += ' masonry-center-title';
35541 if(this.bgimage.length){
35542 cls += ' masonry-bottom-title';
35548 cls += ' ' + this.cls;
35552 tag: (this.href.length) ? 'a' : 'div',
35557 cls: 'masonry-brick-split-head',
35561 cls: 'masonry-brick-paragraph',
35568 cls: 'masonry-brick-split-body',
35574 if(this.href.length){
35575 cfg.href = this.href;
35578 if(this.title.length){
35579 cfg.cn[0].cn[0].cn.push({
35581 cls: 'masonry-brick-title',
35586 if(this.html.length){
35587 cfg.cn[1].cn.push({
35589 cls: 'masonry-brick-text',
35594 if(this.bgimage.length){
35595 cfg.cn[0].cn.push({
35597 cls: 'masonry-brick-image-view',
35602 if(this.videourl.length){
35603 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35604 // youtube support only?
35605 cfg.cn[0].cn.cn.push({
35607 cls: 'masonry-brick-image-view',
35610 allowfullscreen : true
35617 initEvents: function()
35619 switch (this.size) {
35652 this.el.on('touchstart', this.onTouchStart, this);
35653 this.el.on('touchmove', this.onTouchMove, this);
35654 this.el.on('touchend', this.onTouchEnd, this);
35655 this.el.on('contextmenu', this.onContextMenu, this);
35657 this.el.on('mouseenter' ,this.enter, this);
35658 this.el.on('mouseleave', this.leave, this);
35659 this.el.on('click', this.onClick, this);
35662 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35663 this.parent().bricks.push(this);
35668 onClick: function(e, el)
35670 var time = this.endTimer - this.startTimer;
35671 // Roo.log(e.preventDefault());
35674 e.preventDefault();
35679 if(!this.preventDefault){
35683 e.preventDefault();
35685 if (this.activeClass != '') {
35686 this.selectBrick();
35689 this.fireEvent('click', this, e);
35692 enter: function(e, el)
35694 e.preventDefault();
35696 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35700 if(this.bgimage.length && this.html.length){
35701 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35705 leave: function(e, el)
35707 e.preventDefault();
35709 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35713 if(this.bgimage.length && this.html.length){
35714 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35718 onTouchStart: function(e, el)
35720 // e.preventDefault();
35722 this.touchmoved = false;
35724 if(!this.isFitContainer){
35728 if(!this.bgimage.length || !this.html.length){
35732 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35734 this.timer = new Date().getTime();
35738 onTouchMove: function(e, el)
35740 this.touchmoved = true;
35743 onContextMenu : function(e,el)
35745 e.preventDefault();
35746 e.stopPropagation();
35750 onTouchEnd: function(e, el)
35752 // e.preventDefault();
35754 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35761 if(!this.bgimage.length || !this.html.length){
35763 if(this.href.length){
35764 window.location.href = this.href;
35770 if(!this.isFitContainer){
35774 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35776 window.location.href = this.href;
35779 //selection on single brick only
35780 selectBrick : function() {
35782 if (!this.parentId) {
35786 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35787 var index = m.selectedBrick.indexOf(this.id);
35790 m.selectedBrick.splice(index,1);
35791 this.el.removeClass(this.activeClass);
35795 for(var i = 0; i < m.selectedBrick.length; i++) {
35796 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35797 b.el.removeClass(b.activeClass);
35800 m.selectedBrick = [];
35802 m.selectedBrick.push(this.id);
35803 this.el.addClass(this.activeClass);
35807 isSelected : function(){
35808 return this.el.hasClass(this.activeClass);
35813 Roo.apply(Roo.bootstrap.MasonryBrick, {
35816 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35818 * register a Masonry Brick
35819 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35822 register : function(brick)
35824 //this.groups[brick.id] = brick;
35825 this.groups.add(brick.id, brick);
35828 * fetch a masonry brick based on the masonry brick ID
35829 * @param {string} the masonry brick to add
35830 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35833 get: function(brick_id)
35835 // if (typeof(this.groups[brick_id]) == 'undefined') {
35838 // return this.groups[brick_id] ;
35840 if(this.groups.key(brick_id)) {
35841 return this.groups.key(brick_id);
35859 * @class Roo.bootstrap.Brick
35860 * @extends Roo.bootstrap.Component
35861 * Bootstrap Brick class
35864 * Create a new Brick
35865 * @param {Object} config The config object
35868 Roo.bootstrap.Brick = function(config){
35869 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35875 * When a Brick is click
35876 * @param {Roo.bootstrap.Brick} this
35877 * @param {Roo.EventObject} e
35883 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35886 * @cfg {String} title
35890 * @cfg {String} html
35894 * @cfg {String} bgimage
35898 * @cfg {String} cls
35902 * @cfg {String} href
35906 * @cfg {String} video
35910 * @cfg {Boolean} square
35914 getAutoCreate : function()
35916 var cls = 'roo-brick';
35918 if(this.href.length){
35919 cls += ' roo-brick-link';
35922 if(this.bgimage.length){
35923 cls += ' roo-brick-image';
35926 if(!this.html.length && !this.bgimage.length){
35927 cls += ' roo-brick-center-title';
35930 if(!this.html.length && this.bgimage.length){
35931 cls += ' roo-brick-bottom-title';
35935 cls += ' ' + this.cls;
35939 tag: (this.href.length) ? 'a' : 'div',
35944 cls: 'roo-brick-paragraph',
35950 if(this.href.length){
35951 cfg.href = this.href;
35954 var cn = cfg.cn[0].cn;
35956 if(this.title.length){
35959 cls: 'roo-brick-title',
35964 if(this.html.length){
35967 cls: 'roo-brick-text',
35974 if(this.bgimage.length){
35977 cls: 'roo-brick-image-view',
35985 initEvents: function()
35987 if(this.title.length || this.html.length){
35988 this.el.on('mouseenter' ,this.enter, this);
35989 this.el.on('mouseleave', this.leave, this);
35992 Roo.EventManager.onWindowResize(this.resize, this);
35994 if(this.bgimage.length){
35995 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35996 this.imageEl.on('load', this.onImageLoad, this);
36003 onImageLoad : function()
36008 resize : function()
36010 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36012 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36014 if(this.bgimage.length){
36015 var image = this.el.select('.roo-brick-image-view', true).first();
36017 image.setWidth(paragraph.getWidth());
36020 image.setHeight(paragraph.getWidth());
36023 this.el.setHeight(image.getHeight());
36024 paragraph.setHeight(image.getHeight());
36030 enter: function(e, el)
36032 e.preventDefault();
36034 if(this.bgimage.length){
36035 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36036 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36040 leave: function(e, el)
36042 e.preventDefault();
36044 if(this.bgimage.length){
36045 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36046 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36061 * @class Roo.bootstrap.NumberField
36062 * @extends Roo.bootstrap.Input
36063 * Bootstrap NumberField class
36069 * Create a new NumberField
36070 * @param {Object} config The config object
36073 Roo.bootstrap.NumberField = function(config){
36074 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36077 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36080 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36082 allowDecimals : true,
36084 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36086 decimalSeparator : ".",
36088 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36090 decimalPrecision : 2,
36092 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36094 allowNegative : true,
36097 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36101 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36103 minValue : Number.NEGATIVE_INFINITY,
36105 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36107 maxValue : Number.MAX_VALUE,
36109 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36111 minText : "The minimum value for this field is {0}",
36113 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36115 maxText : "The maximum value for this field is {0}",
36117 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36118 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36120 nanText : "{0} is not a valid number",
36122 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36124 thousandsDelimiter : false,
36126 * @cfg {String} valueAlign alignment of value
36128 valueAlign : "left",
36130 getAutoCreate : function()
36132 var hiddenInput = {
36136 cls: 'hidden-number-input'
36140 hiddenInput.name = this.name;
36145 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36147 this.name = hiddenInput.name;
36149 if(cfg.cn.length > 0) {
36150 cfg.cn.push(hiddenInput);
36157 initEvents : function()
36159 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36161 var allowed = "0123456789";
36163 if(this.allowDecimals){
36164 allowed += this.decimalSeparator;
36167 if(this.allowNegative){
36171 if(this.thousandsDelimiter) {
36175 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36177 var keyPress = function(e){
36179 var k = e.getKey();
36181 var c = e.getCharCode();
36184 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36185 allowed.indexOf(String.fromCharCode(c)) === -1
36191 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36195 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36200 this.el.on("keypress", keyPress, this);
36203 validateValue : function(value)
36206 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36210 var num = this.parseValue(value);
36213 this.markInvalid(String.format(this.nanText, value));
36217 if(num < this.minValue){
36218 this.markInvalid(String.format(this.minText, this.minValue));
36222 if(num > this.maxValue){
36223 this.markInvalid(String.format(this.maxText, this.maxValue));
36230 getValue : function()
36232 var v = this.hiddenEl().getValue();
36234 return this.fixPrecision(this.parseValue(v));
36237 parseValue : function(value)
36239 if(this.thousandsDelimiter) {
36241 r = new RegExp(",", "g");
36242 value = value.replace(r, "");
36245 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36246 return isNaN(value) ? '' : value;
36249 fixPrecision : function(value)
36251 if(this.thousandsDelimiter) {
36253 r = new RegExp(",", "g");
36254 value = value.replace(r, "");
36257 var nan = isNaN(value);
36259 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36260 return nan ? '' : value;
36262 return parseFloat(value).toFixed(this.decimalPrecision);
36265 setValue : function(v)
36267 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36273 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36275 this.inputEl().dom.value = (v == '') ? '' :
36276 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36278 if(!this.allowZero && v === '0') {
36279 this.hiddenEl().dom.value = '';
36280 this.inputEl().dom.value = '';
36287 decimalPrecisionFcn : function(v)
36289 return Math.floor(v);
36292 beforeBlur : function()
36294 var v = this.parseValue(this.getRawValue());
36296 if(v || v === 0 || v === ''){
36301 hiddenEl : function()
36303 return this.el.select('input.hidden-number-input',true).first();
36315 * @class Roo.bootstrap.DocumentSlider
36316 * @extends Roo.bootstrap.Component
36317 * Bootstrap DocumentSlider class
36320 * Create a new DocumentViewer
36321 * @param {Object} config The config object
36324 Roo.bootstrap.DocumentSlider = function(config){
36325 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36332 * Fire after initEvent
36333 * @param {Roo.bootstrap.DocumentSlider} this
36338 * Fire after update
36339 * @param {Roo.bootstrap.DocumentSlider} this
36345 * @param {Roo.bootstrap.DocumentSlider} this
36351 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36357 getAutoCreate : function()
36361 cls : 'roo-document-slider',
36365 cls : 'roo-document-slider-header',
36369 cls : 'roo-document-slider-header-title'
36375 cls : 'roo-document-slider-body',
36379 cls : 'roo-document-slider-prev',
36383 cls : 'fa fa-chevron-left'
36389 cls : 'roo-document-slider-thumb',
36393 cls : 'roo-document-slider-image'
36399 cls : 'roo-document-slider-next',
36403 cls : 'fa fa-chevron-right'
36415 initEvents : function()
36417 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36418 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36420 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36421 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36423 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36424 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36426 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36427 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36429 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36430 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36432 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36433 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36435 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36436 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36438 this.thumbEl.on('click', this.onClick, this);
36440 this.prevIndicator.on('click', this.prev, this);
36442 this.nextIndicator.on('click', this.next, this);
36446 initial : function()
36448 if(this.files.length){
36449 this.indicator = 1;
36453 this.fireEvent('initial', this);
36456 update : function()
36458 this.imageEl.attr('src', this.files[this.indicator - 1]);
36460 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36462 this.prevIndicator.show();
36464 if(this.indicator == 1){
36465 this.prevIndicator.hide();
36468 this.nextIndicator.show();
36470 if(this.indicator == this.files.length){
36471 this.nextIndicator.hide();
36474 this.thumbEl.scrollTo('top');
36476 this.fireEvent('update', this);
36479 onClick : function(e)
36481 e.preventDefault();
36483 this.fireEvent('click', this);
36488 e.preventDefault();
36490 this.indicator = Math.max(1, this.indicator - 1);
36497 e.preventDefault();
36499 this.indicator = Math.min(this.files.length, this.indicator + 1);
36513 * @class Roo.bootstrap.RadioSet
36514 * @extends Roo.bootstrap.Input
36515 * Bootstrap RadioSet class
36516 * @cfg {String} indicatorpos (left|right) default left
36517 * @cfg {Boolean} inline (true|false) inline the element (default true)
36518 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36520 * Create a new RadioSet
36521 * @param {Object} config The config object
36524 Roo.bootstrap.RadioSet = function(config){
36526 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36530 Roo.bootstrap.RadioSet.register(this);
36535 * Fires when the element is checked or unchecked.
36536 * @param {Roo.bootstrap.RadioSet} this This radio
36537 * @param {Roo.bootstrap.Radio} item The checked item
36542 * Fires when the element is click.
36543 * @param {Roo.bootstrap.RadioSet} this This radio set
36544 * @param {Roo.bootstrap.Radio} item The checked item
36545 * @param {Roo.EventObject} e The event object
36552 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36560 indicatorpos : 'left',
36562 getAutoCreate : function()
36566 cls : 'roo-radio-set-label',
36570 html : this.fieldLabel
36574 if (Roo.bootstrap.version == 3) {
36577 if(this.indicatorpos == 'left'){
36580 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36581 tooltip : 'This field is required'
36586 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36587 tooltip : 'This field is required'
36593 cls : 'roo-radio-set-items'
36596 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36598 if (align === 'left' && this.fieldLabel.length) {
36601 cls : "roo-radio-set-right",
36607 if(this.labelWidth > 12){
36608 label.style = "width: " + this.labelWidth + 'px';
36611 if(this.labelWidth < 13 && this.labelmd == 0){
36612 this.labelmd = this.labelWidth;
36615 if(this.labellg > 0){
36616 label.cls += ' col-lg-' + this.labellg;
36617 items.cls += ' col-lg-' + (12 - this.labellg);
36620 if(this.labelmd > 0){
36621 label.cls += ' col-md-' + this.labelmd;
36622 items.cls += ' col-md-' + (12 - this.labelmd);
36625 if(this.labelsm > 0){
36626 label.cls += ' col-sm-' + this.labelsm;
36627 items.cls += ' col-sm-' + (12 - this.labelsm);
36630 if(this.labelxs > 0){
36631 label.cls += ' col-xs-' + this.labelxs;
36632 items.cls += ' col-xs-' + (12 - this.labelxs);
36638 cls : 'roo-radio-set',
36642 cls : 'roo-radio-set-input',
36645 value : this.value ? this.value : ''
36652 if(this.weight.length){
36653 cfg.cls += ' roo-radio-' + this.weight;
36657 cfg.cls += ' roo-radio-set-inline';
36661 ['xs','sm','md','lg'].map(function(size){
36662 if (settings[size]) {
36663 cfg.cls += ' col-' + size + '-' + settings[size];
36671 initEvents : function()
36673 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36674 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36676 if(!this.fieldLabel.length){
36677 this.labelEl.hide();
36680 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36681 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36683 this.indicator = this.indicatorEl();
36685 if(this.indicator){
36686 this.indicator.addClass('invisible');
36689 this.originalValue = this.getValue();
36693 inputEl: function ()
36695 return this.el.select('.roo-radio-set-input', true).first();
36698 getChildContainer : function()
36700 return this.itemsEl;
36703 register : function(item)
36705 this.radioes.push(item);
36709 validate : function()
36711 if(this.getVisibilityEl().hasClass('hidden')){
36717 Roo.each(this.radioes, function(i){
36726 if(this.allowBlank) {
36730 if(this.disabled || valid){
36735 this.markInvalid();
36740 markValid : function()
36742 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36743 this.indicatorEl().removeClass('visible');
36744 this.indicatorEl().addClass('invisible');
36748 if (Roo.bootstrap.version == 3) {
36749 this.el.removeClass([this.invalidClass, this.validClass]);
36750 this.el.addClass(this.validClass);
36752 this.el.removeClass(['is-invalid','is-valid']);
36753 this.el.addClass(['is-valid']);
36755 this.fireEvent('valid', this);
36758 markInvalid : function(msg)
36760 if(this.allowBlank || this.disabled){
36764 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36765 this.indicatorEl().removeClass('invisible');
36766 this.indicatorEl().addClass('visible');
36768 if (Roo.bootstrap.version == 3) {
36769 this.el.removeClass([this.invalidClass, this.validClass]);
36770 this.el.addClass(this.invalidClass);
36772 this.el.removeClass(['is-invalid','is-valid']);
36773 this.el.addClass(['is-invalid']);
36776 this.fireEvent('invalid', this, msg);
36780 setValue : function(v, suppressEvent)
36782 if(this.value === v){
36789 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36792 Roo.each(this.radioes, function(i){
36794 i.el.removeClass('checked');
36797 Roo.each(this.radioes, function(i){
36799 if(i.value === v || i.value.toString() === v.toString()){
36801 i.el.addClass('checked');
36803 if(suppressEvent !== true){
36804 this.fireEvent('check', this, i);
36815 clearInvalid : function(){
36817 if(!this.el || this.preventMark){
36821 this.el.removeClass([this.invalidClass]);
36823 this.fireEvent('valid', this);
36828 Roo.apply(Roo.bootstrap.RadioSet, {
36832 register : function(set)
36834 this.groups[set.name] = set;
36837 get: function(name)
36839 if (typeof(this.groups[name]) == 'undefined') {
36843 return this.groups[name] ;
36849 * Ext JS Library 1.1.1
36850 * Copyright(c) 2006-2007, Ext JS, LLC.
36852 * Originally Released Under LGPL - original licence link has changed is not relivant.
36855 * <script type="text/javascript">
36860 * @class Roo.bootstrap.SplitBar
36861 * @extends Roo.util.Observable
36862 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36866 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36867 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36868 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36869 split.minSize = 100;
36870 split.maxSize = 600;
36871 split.animate = true;
36872 split.on('moved', splitterMoved);
36875 * Create a new SplitBar
36876 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36877 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36878 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36879 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36880 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36881 position of the SplitBar).
36883 Roo.bootstrap.SplitBar = function(cfg){
36888 // dragElement : elm
36889 // resizingElement: el,
36891 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36892 // placement : Roo.bootstrap.SplitBar.LEFT ,
36893 // existingProxy ???
36896 this.el = Roo.get(cfg.dragElement, true);
36897 this.el.dom.unselectable = "on";
36899 this.resizingEl = Roo.get(cfg.resizingElement, true);
36903 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36904 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36907 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36910 * The minimum size of the resizing element. (Defaults to 0)
36916 * The maximum size of the resizing element. (Defaults to 2000)
36919 this.maxSize = 2000;
36922 * Whether to animate the transition to the new size
36925 this.animate = false;
36928 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36931 this.useShim = false;
36936 if(!cfg.existingProxy){
36938 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36940 this.proxy = Roo.get(cfg.existingProxy).dom;
36943 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36946 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36949 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36952 this.dragSpecs = {};
36955 * @private The adapter to use to positon and resize elements
36957 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36958 this.adapter.init(this);
36960 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36962 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36963 this.el.addClass("roo-splitbar-h");
36966 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36967 this.el.addClass("roo-splitbar-v");
36973 * Fires when the splitter is moved (alias for {@link #event-moved})
36974 * @param {Roo.bootstrap.SplitBar} this
36975 * @param {Number} newSize the new width or height
36980 * Fires when the splitter is moved
36981 * @param {Roo.bootstrap.SplitBar} this
36982 * @param {Number} newSize the new width or height
36986 * @event beforeresize
36987 * Fires before the splitter is dragged
36988 * @param {Roo.bootstrap.SplitBar} this
36990 "beforeresize" : true,
36992 "beforeapply" : true
36995 Roo.util.Observable.call(this);
36998 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36999 onStartProxyDrag : function(x, y){
37000 this.fireEvent("beforeresize", this);
37002 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37004 o.enableDisplayMode("block");
37005 // all splitbars share the same overlay
37006 Roo.bootstrap.SplitBar.prototype.overlay = o;
37008 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37009 this.overlay.show();
37010 Roo.get(this.proxy).setDisplayed("block");
37011 var size = this.adapter.getElementSize(this);
37012 this.activeMinSize = this.getMinimumSize();;
37013 this.activeMaxSize = this.getMaximumSize();;
37014 var c1 = size - this.activeMinSize;
37015 var c2 = Math.max(this.activeMaxSize - size, 0);
37016 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37017 this.dd.resetConstraints();
37018 this.dd.setXConstraint(
37019 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37020 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37022 this.dd.setYConstraint(0, 0);
37024 this.dd.resetConstraints();
37025 this.dd.setXConstraint(0, 0);
37026 this.dd.setYConstraint(
37027 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37028 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37031 this.dragSpecs.startSize = size;
37032 this.dragSpecs.startPoint = [x, y];
37033 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37037 * @private Called after the drag operation by the DDProxy
37039 onEndProxyDrag : function(e){
37040 Roo.get(this.proxy).setDisplayed(false);
37041 var endPoint = Roo.lib.Event.getXY(e);
37043 this.overlay.hide();
37046 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37047 newSize = this.dragSpecs.startSize +
37048 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37049 endPoint[0] - this.dragSpecs.startPoint[0] :
37050 this.dragSpecs.startPoint[0] - endPoint[0]
37053 newSize = this.dragSpecs.startSize +
37054 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37055 endPoint[1] - this.dragSpecs.startPoint[1] :
37056 this.dragSpecs.startPoint[1] - endPoint[1]
37059 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37060 if(newSize != this.dragSpecs.startSize){
37061 if(this.fireEvent('beforeapply', this, newSize) !== false){
37062 this.adapter.setElementSize(this, newSize);
37063 this.fireEvent("moved", this, newSize);
37064 this.fireEvent("resize", this, newSize);
37070 * Get the adapter this SplitBar uses
37071 * @return The adapter object
37073 getAdapter : function(){
37074 return this.adapter;
37078 * Set the adapter this SplitBar uses
37079 * @param {Object} adapter A SplitBar adapter object
37081 setAdapter : function(adapter){
37082 this.adapter = adapter;
37083 this.adapter.init(this);
37087 * Gets the minimum size for the resizing element
37088 * @return {Number} The minimum size
37090 getMinimumSize : function(){
37091 return this.minSize;
37095 * Sets the minimum size for the resizing element
37096 * @param {Number} minSize The minimum size
37098 setMinimumSize : function(minSize){
37099 this.minSize = minSize;
37103 * Gets the maximum size for the resizing element
37104 * @return {Number} The maximum size
37106 getMaximumSize : function(){
37107 return this.maxSize;
37111 * Sets the maximum size for the resizing element
37112 * @param {Number} maxSize The maximum size
37114 setMaximumSize : function(maxSize){
37115 this.maxSize = maxSize;
37119 * Sets the initialize size for the resizing element
37120 * @param {Number} size The initial size
37122 setCurrentSize : function(size){
37123 var oldAnimate = this.animate;
37124 this.animate = false;
37125 this.adapter.setElementSize(this, size);
37126 this.animate = oldAnimate;
37130 * Destroy this splitbar.
37131 * @param {Boolean} removeEl True to remove the element
37133 destroy : function(removeEl){
37135 this.shim.remove();
37138 this.proxy.parentNode.removeChild(this.proxy);
37146 * @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.
37148 Roo.bootstrap.SplitBar.createProxy = function(dir){
37149 var proxy = new Roo.Element(document.createElement("div"));
37150 proxy.unselectable();
37151 var cls = 'roo-splitbar-proxy';
37152 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37153 document.body.appendChild(proxy.dom);
37158 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37159 * Default Adapter. It assumes the splitter and resizing element are not positioned
37160 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37162 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37165 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37166 // do nothing for now
37167 init : function(s){
37171 * Called before drag operations to get the current size of the resizing element.
37172 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37174 getElementSize : function(s){
37175 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37176 return s.resizingEl.getWidth();
37178 return s.resizingEl.getHeight();
37183 * Called after drag operations to set the size of the resizing element.
37184 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37185 * @param {Number} newSize The new size to set
37186 * @param {Function} onComplete A function to be invoked when resizing is complete
37188 setElementSize : function(s, newSize, onComplete){
37189 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37191 s.resizingEl.setWidth(newSize);
37193 onComplete(s, newSize);
37196 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37201 s.resizingEl.setHeight(newSize);
37203 onComplete(s, newSize);
37206 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37213 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37214 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37215 * Adapter that moves the splitter element to align with the resized sizing element.
37216 * Used with an absolute positioned SplitBar.
37217 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37218 * document.body, make sure you assign an id to the body element.
37220 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37221 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37222 this.container = Roo.get(container);
37225 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37226 init : function(s){
37227 this.basic.init(s);
37230 getElementSize : function(s){
37231 return this.basic.getElementSize(s);
37234 setElementSize : function(s, newSize, onComplete){
37235 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37238 moveSplitter : function(s){
37239 var yes = Roo.bootstrap.SplitBar;
37240 switch(s.placement){
37242 s.el.setX(s.resizingEl.getRight());
37245 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37248 s.el.setY(s.resizingEl.getBottom());
37251 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37258 * Orientation constant - Create a vertical SplitBar
37262 Roo.bootstrap.SplitBar.VERTICAL = 1;
37265 * Orientation constant - Create a horizontal SplitBar
37269 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37272 * Placement constant - The resizing element is to the left of the splitter element
37276 Roo.bootstrap.SplitBar.LEFT = 1;
37279 * Placement constant - The resizing element is to the right of the splitter element
37283 Roo.bootstrap.SplitBar.RIGHT = 2;
37286 * Placement constant - The resizing element is positioned above the splitter element
37290 Roo.bootstrap.SplitBar.TOP = 3;
37293 * Placement constant - The resizing element is positioned under splitter element
37297 Roo.bootstrap.SplitBar.BOTTOM = 4;
37298 Roo.namespace("Roo.bootstrap.layout");/*
37300 * Ext JS Library 1.1.1
37301 * Copyright(c) 2006-2007, Ext JS, LLC.
37303 * Originally Released Under LGPL - original licence link has changed is not relivant.
37306 * <script type="text/javascript">
37310 * @class Roo.bootstrap.layout.Manager
37311 * @extends Roo.bootstrap.Component
37312 * Base class for layout managers.
37314 Roo.bootstrap.layout.Manager = function(config)
37316 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37322 /** false to disable window resize monitoring @type Boolean */
37323 this.monitorWindowResize = true;
37328 * Fires when a layout is performed.
37329 * @param {Roo.LayoutManager} this
37333 * @event regionresized
37334 * Fires when the user resizes a region.
37335 * @param {Roo.LayoutRegion} region The resized region
37336 * @param {Number} newSize The new size (width for east/west, height for north/south)
37338 "regionresized" : true,
37340 * @event regioncollapsed
37341 * Fires when a region is collapsed.
37342 * @param {Roo.LayoutRegion} region The collapsed region
37344 "regioncollapsed" : true,
37346 * @event regionexpanded
37347 * Fires when a region is expanded.
37348 * @param {Roo.LayoutRegion} region The expanded region
37350 "regionexpanded" : true
37352 this.updating = false;
37355 this.el = Roo.get(config.el);
37361 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37366 monitorWindowResize : true,
37372 onRender : function(ct, position)
37375 this.el = Roo.get(ct);
37378 //this.fireEvent('render',this);
37382 initEvents: function()
37386 // ie scrollbar fix
37387 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37388 document.body.scroll = "no";
37389 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37390 this.el.position('relative');
37392 this.id = this.el.id;
37393 this.el.addClass("roo-layout-container");
37394 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37395 if(this.el.dom != document.body ) {
37396 this.el.on('resize', this.layout,this);
37397 this.el.on('show', this.layout,this);
37403 * Returns true if this layout is currently being updated
37404 * @return {Boolean}
37406 isUpdating : function(){
37407 return this.updating;
37411 * Suspend the LayoutManager from doing auto-layouts while
37412 * making multiple add or remove calls
37414 beginUpdate : function(){
37415 this.updating = true;
37419 * Restore auto-layouts and optionally disable the manager from performing a layout
37420 * @param {Boolean} noLayout true to disable a layout update
37422 endUpdate : function(noLayout){
37423 this.updating = false;
37429 layout: function(){
37433 onRegionResized : function(region, newSize){
37434 this.fireEvent("regionresized", region, newSize);
37438 onRegionCollapsed : function(region){
37439 this.fireEvent("regioncollapsed", region);
37442 onRegionExpanded : function(region){
37443 this.fireEvent("regionexpanded", region);
37447 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37448 * performs box-model adjustments.
37449 * @return {Object} The size as an object {width: (the width), height: (the height)}
37451 getViewSize : function()
37454 if(this.el.dom != document.body){
37455 size = this.el.getSize();
37457 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37459 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37460 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37465 * Returns the Element this layout is bound to.
37466 * @return {Roo.Element}
37468 getEl : function(){
37473 * Returns the specified region.
37474 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37475 * @return {Roo.LayoutRegion}
37477 getRegion : function(target){
37478 return this.regions[target.toLowerCase()];
37481 onWindowResize : function(){
37482 if(this.monitorWindowResize){
37489 * Ext JS Library 1.1.1
37490 * Copyright(c) 2006-2007, Ext JS, LLC.
37492 * Originally Released Under LGPL - original licence link has changed is not relivant.
37495 * <script type="text/javascript">
37498 * @class Roo.bootstrap.layout.Border
37499 * @extends Roo.bootstrap.layout.Manager
37500 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37501 * please see: examples/bootstrap/nested.html<br><br>
37503 <b>The container the layout is rendered into can be either the body element or any other element.
37504 If it is not the body element, the container needs to either be an absolute positioned element,
37505 or you will need to add "position:relative" to the css of the container. You will also need to specify
37506 the container size if it is not the body element.</b>
37509 * Create a new Border
37510 * @param {Object} config Configuration options
37512 Roo.bootstrap.layout.Border = function(config){
37513 config = config || {};
37514 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37518 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37519 if(config[region]){
37520 config[region].region = region;
37521 this.addRegion(config[region]);
37527 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37529 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37531 parent : false, // this might point to a 'nest' or a ???
37534 * Creates and adds a new region if it doesn't already exist.
37535 * @param {String} target The target region key (north, south, east, west or center).
37536 * @param {Object} config The regions config object
37537 * @return {BorderLayoutRegion} The new region
37539 addRegion : function(config)
37541 if(!this.regions[config.region]){
37542 var r = this.factory(config);
37543 this.bindRegion(r);
37545 return this.regions[config.region];
37549 bindRegion : function(r){
37550 this.regions[r.config.region] = r;
37552 r.on("visibilitychange", this.layout, this);
37553 r.on("paneladded", this.layout, this);
37554 r.on("panelremoved", this.layout, this);
37555 r.on("invalidated", this.layout, this);
37556 r.on("resized", this.onRegionResized, this);
37557 r.on("collapsed", this.onRegionCollapsed, this);
37558 r.on("expanded", this.onRegionExpanded, this);
37562 * Performs a layout update.
37564 layout : function()
37566 if(this.updating) {
37570 // render all the rebions if they have not been done alreayd?
37571 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37572 if(this.regions[region] && !this.regions[region].bodyEl){
37573 this.regions[region].onRender(this.el)
37577 var size = this.getViewSize();
37578 var w = size.width;
37579 var h = size.height;
37584 //var x = 0, y = 0;
37586 var rs = this.regions;
37587 var north = rs["north"];
37588 var south = rs["south"];
37589 var west = rs["west"];
37590 var east = rs["east"];
37591 var center = rs["center"];
37592 //if(this.hideOnLayout){ // not supported anymore
37593 //c.el.setStyle("display", "none");
37595 if(north && north.isVisible()){
37596 var b = north.getBox();
37597 var m = north.getMargins();
37598 b.width = w - (m.left+m.right);
37601 centerY = b.height + b.y + m.bottom;
37602 centerH -= centerY;
37603 north.updateBox(this.safeBox(b));
37605 if(south && south.isVisible()){
37606 var b = south.getBox();
37607 var m = south.getMargins();
37608 b.width = w - (m.left+m.right);
37610 var totalHeight = (b.height + m.top + m.bottom);
37611 b.y = h - totalHeight + m.top;
37612 centerH -= totalHeight;
37613 south.updateBox(this.safeBox(b));
37615 if(west && west.isVisible()){
37616 var b = west.getBox();
37617 var m = west.getMargins();
37618 b.height = centerH - (m.top+m.bottom);
37620 b.y = centerY + m.top;
37621 var totalWidth = (b.width + m.left + m.right);
37622 centerX += totalWidth;
37623 centerW -= totalWidth;
37624 west.updateBox(this.safeBox(b));
37626 if(east && east.isVisible()){
37627 var b = east.getBox();
37628 var m = east.getMargins();
37629 b.height = centerH - (m.top+m.bottom);
37630 var totalWidth = (b.width + m.left + m.right);
37631 b.x = w - totalWidth + m.left;
37632 b.y = centerY + m.top;
37633 centerW -= totalWidth;
37634 east.updateBox(this.safeBox(b));
37637 var m = center.getMargins();
37639 x: centerX + m.left,
37640 y: centerY + m.top,
37641 width: centerW - (m.left+m.right),
37642 height: centerH - (m.top+m.bottom)
37644 //if(this.hideOnLayout){
37645 //center.el.setStyle("display", "block");
37647 center.updateBox(this.safeBox(centerBox));
37650 this.fireEvent("layout", this);
37654 safeBox : function(box){
37655 box.width = Math.max(0, box.width);
37656 box.height = Math.max(0, box.height);
37661 * Adds a ContentPanel (or subclass) to this layout.
37662 * @param {String} target The target region key (north, south, east, west or center).
37663 * @param {Roo.ContentPanel} panel The panel to add
37664 * @return {Roo.ContentPanel} The added panel
37666 add : function(target, panel){
37668 target = target.toLowerCase();
37669 return this.regions[target].add(panel);
37673 * Remove a ContentPanel (or subclass) to this layout.
37674 * @param {String} target The target region key (north, south, east, west or center).
37675 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37676 * @return {Roo.ContentPanel} The removed panel
37678 remove : function(target, panel){
37679 target = target.toLowerCase();
37680 return this.regions[target].remove(panel);
37684 * Searches all regions for a panel with the specified id
37685 * @param {String} panelId
37686 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37688 findPanel : function(panelId){
37689 var rs = this.regions;
37690 for(var target in rs){
37691 if(typeof rs[target] != "function"){
37692 var p = rs[target].getPanel(panelId);
37702 * Searches all regions for a panel with the specified id and activates (shows) it.
37703 * @param {String/ContentPanel} panelId The panels id or the panel itself
37704 * @return {Roo.ContentPanel} The shown panel or null
37706 showPanel : function(panelId) {
37707 var rs = this.regions;
37708 for(var target in rs){
37709 var r = rs[target];
37710 if(typeof r != "function"){
37711 if(r.hasPanel(panelId)){
37712 return r.showPanel(panelId);
37720 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37721 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37724 restoreState : function(provider){
37726 provider = Roo.state.Manager;
37728 var sm = new Roo.LayoutStateManager();
37729 sm.init(this, provider);
37735 * Adds a xtype elements to the layout.
37739 xtype : 'ContentPanel',
37746 xtype : 'NestedLayoutPanel',
37752 items : [ ... list of content panels or nested layout panels.. ]
37756 * @param {Object} cfg Xtype definition of item to add.
37758 addxtype : function(cfg)
37760 // basically accepts a pannel...
37761 // can accept a layout region..!?!?
37762 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37765 // theory? children can only be panels??
37767 //if (!cfg.xtype.match(/Panel$/)) {
37772 if (typeof(cfg.region) == 'undefined') {
37773 Roo.log("Failed to add Panel, region was not set");
37777 var region = cfg.region;
37783 xitems = cfg.items;
37788 if ( region == 'center') {
37789 Roo.log("Center: " + cfg.title);
37795 case 'Content': // ContentPanel (el, cfg)
37796 case 'Scroll': // ContentPanel (el, cfg)
37798 cfg.autoCreate = cfg.autoCreate || true;
37799 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37801 // var el = this.el.createChild();
37802 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37805 this.add(region, ret);
37809 case 'TreePanel': // our new panel!
37810 cfg.el = this.el.createChild();
37811 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37812 this.add(region, ret);
37817 // create a new Layout (which is a Border Layout...
37819 var clayout = cfg.layout;
37820 clayout.el = this.el.createChild();
37821 clayout.items = clayout.items || [];
37825 // replace this exitems with the clayout ones..
37826 xitems = clayout.items;
37828 // force background off if it's in center...
37829 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37830 cfg.background = false;
37832 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37835 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37836 //console.log('adding nested layout panel ' + cfg.toSource());
37837 this.add(region, ret);
37838 nb = {}; /// find first...
37843 // needs grid and region
37845 //var el = this.getRegion(region).el.createChild();
37847 *var el = this.el.createChild();
37848 // create the grid first...
37849 cfg.grid.container = el;
37850 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37853 if (region == 'center' && this.active ) {
37854 cfg.background = false;
37857 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37859 this.add(region, ret);
37861 if (cfg.background) {
37862 // render grid on panel activation (if panel background)
37863 ret.on('activate', function(gp) {
37864 if (!gp.grid.rendered) {
37865 // gp.grid.render(el);
37869 // cfg.grid.render(el);
37875 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37876 // it was the old xcomponent building that caused this before.
37877 // espeically if border is the top element in the tree.
37887 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37889 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37890 this.add(region, ret);
37894 throw "Can not add '" + cfg.xtype + "' to Border";
37900 this.beginUpdate();
37904 Roo.each(xitems, function(i) {
37905 region = nb && i.region ? i.region : false;
37907 var add = ret.addxtype(i);
37910 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37911 if (!i.background) {
37912 abn[region] = nb[region] ;
37919 // make the last non-background panel active..
37920 //if (nb) { Roo.log(abn); }
37923 for(var r in abn) {
37924 region = this.getRegion(r);
37926 // tried using nb[r], but it does not work..
37928 region.showPanel(abn[r]);
37939 factory : function(cfg)
37942 var validRegions = Roo.bootstrap.layout.Border.regions;
37944 var target = cfg.region;
37947 var r = Roo.bootstrap.layout;
37951 return new r.North(cfg);
37953 return new r.South(cfg);
37955 return new r.East(cfg);
37957 return new r.West(cfg);
37959 return new r.Center(cfg);
37961 throw 'Layout region "'+target+'" not supported.';
37968 * Ext JS Library 1.1.1
37969 * Copyright(c) 2006-2007, Ext JS, LLC.
37971 * Originally Released Under LGPL - original licence link has changed is not relivant.
37974 * <script type="text/javascript">
37978 * @class Roo.bootstrap.layout.Basic
37979 * @extends Roo.util.Observable
37980 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37981 * and does not have a titlebar, tabs or any other features. All it does is size and position
37982 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37983 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37984 * @cfg {string} region the region that it inhabits..
37985 * @cfg {bool} skipConfig skip config?
37989 Roo.bootstrap.layout.Basic = function(config){
37991 this.mgr = config.mgr;
37993 this.position = config.region;
37995 var skipConfig = config.skipConfig;
37999 * @scope Roo.BasicLayoutRegion
38003 * @event beforeremove
38004 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38005 * @param {Roo.LayoutRegion} this
38006 * @param {Roo.ContentPanel} panel The panel
38007 * @param {Object} e The cancel event object
38009 "beforeremove" : true,
38011 * @event invalidated
38012 * Fires when the layout for this region is changed.
38013 * @param {Roo.LayoutRegion} this
38015 "invalidated" : true,
38017 * @event visibilitychange
38018 * Fires when this region is shown or hidden
38019 * @param {Roo.LayoutRegion} this
38020 * @param {Boolean} visibility true or false
38022 "visibilitychange" : true,
38024 * @event paneladded
38025 * Fires when a panel is added.
38026 * @param {Roo.LayoutRegion} this
38027 * @param {Roo.ContentPanel} panel The panel
38029 "paneladded" : true,
38031 * @event panelremoved
38032 * Fires when a panel is removed.
38033 * @param {Roo.LayoutRegion} this
38034 * @param {Roo.ContentPanel} panel The panel
38036 "panelremoved" : true,
38038 * @event beforecollapse
38039 * Fires when this region before collapse.
38040 * @param {Roo.LayoutRegion} this
38042 "beforecollapse" : true,
38045 * Fires when this region is collapsed.
38046 * @param {Roo.LayoutRegion} this
38048 "collapsed" : true,
38051 * Fires when this region is expanded.
38052 * @param {Roo.LayoutRegion} this
38057 * Fires when this region is slid into view.
38058 * @param {Roo.LayoutRegion} this
38060 "slideshow" : true,
38063 * Fires when this region slides out of view.
38064 * @param {Roo.LayoutRegion} this
38066 "slidehide" : true,
38068 * @event panelactivated
38069 * Fires when a panel is activated.
38070 * @param {Roo.LayoutRegion} this
38071 * @param {Roo.ContentPanel} panel The activated panel
38073 "panelactivated" : true,
38076 * Fires when the user resizes this region.
38077 * @param {Roo.LayoutRegion} this
38078 * @param {Number} newSize The new size (width for east/west, height for north/south)
38082 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38083 this.panels = new Roo.util.MixedCollection();
38084 this.panels.getKey = this.getPanelId.createDelegate(this);
38086 this.activePanel = null;
38087 // ensure listeners are added...
38089 if (config.listeners || config.events) {
38090 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38091 listeners : config.listeners || {},
38092 events : config.events || {}
38096 if(skipConfig !== true){
38097 this.applyConfig(config);
38101 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38103 getPanelId : function(p){
38107 applyConfig : function(config){
38108 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38109 this.config = config;
38114 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38115 * the width, for horizontal (north, south) the height.
38116 * @param {Number} newSize The new width or height
38118 resizeTo : function(newSize){
38119 var el = this.el ? this.el :
38120 (this.activePanel ? this.activePanel.getEl() : null);
38122 switch(this.position){
38125 el.setWidth(newSize);
38126 this.fireEvent("resized", this, newSize);
38130 el.setHeight(newSize);
38131 this.fireEvent("resized", this, newSize);
38137 getBox : function(){
38138 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38141 getMargins : function(){
38142 return this.margins;
38145 updateBox : function(box){
38147 var el = this.activePanel.getEl();
38148 el.dom.style.left = box.x + "px";
38149 el.dom.style.top = box.y + "px";
38150 this.activePanel.setSize(box.width, box.height);
38154 * Returns the container element for this region.
38155 * @return {Roo.Element}
38157 getEl : function(){
38158 return this.activePanel;
38162 * Returns true if this region is currently visible.
38163 * @return {Boolean}
38165 isVisible : function(){
38166 return this.activePanel ? true : false;
38169 setActivePanel : function(panel){
38170 panel = this.getPanel(panel);
38171 if(this.activePanel && this.activePanel != panel){
38172 this.activePanel.setActiveState(false);
38173 this.activePanel.getEl().setLeftTop(-10000,-10000);
38175 this.activePanel = panel;
38176 panel.setActiveState(true);
38178 panel.setSize(this.box.width, this.box.height);
38180 this.fireEvent("panelactivated", this, panel);
38181 this.fireEvent("invalidated");
38185 * Show the specified panel.
38186 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38187 * @return {Roo.ContentPanel} The shown panel or null
38189 showPanel : function(panel){
38190 panel = this.getPanel(panel);
38192 this.setActivePanel(panel);
38198 * Get the active panel for this region.
38199 * @return {Roo.ContentPanel} The active panel or null
38201 getActivePanel : function(){
38202 return this.activePanel;
38206 * Add the passed ContentPanel(s)
38207 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38208 * @return {Roo.ContentPanel} The panel added (if only one was added)
38210 add : function(panel){
38211 if(arguments.length > 1){
38212 for(var i = 0, len = arguments.length; i < len; i++) {
38213 this.add(arguments[i]);
38217 if(this.hasPanel(panel)){
38218 this.showPanel(panel);
38221 var el = panel.getEl();
38222 if(el.dom.parentNode != this.mgr.el.dom){
38223 this.mgr.el.dom.appendChild(el.dom);
38225 if(panel.setRegion){
38226 panel.setRegion(this);
38228 this.panels.add(panel);
38229 el.setStyle("position", "absolute");
38230 if(!panel.background){
38231 this.setActivePanel(panel);
38232 if(this.config.initialSize && this.panels.getCount()==1){
38233 this.resizeTo(this.config.initialSize);
38236 this.fireEvent("paneladded", this, panel);
38241 * Returns true if the panel is in this region.
38242 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38243 * @return {Boolean}
38245 hasPanel : function(panel){
38246 if(typeof panel == "object"){ // must be panel obj
38247 panel = panel.getId();
38249 return this.getPanel(panel) ? true : false;
38253 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38254 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38255 * @param {Boolean} preservePanel Overrides the config preservePanel option
38256 * @return {Roo.ContentPanel} The panel that was removed
38258 remove : function(panel, preservePanel){
38259 panel = this.getPanel(panel);
38264 this.fireEvent("beforeremove", this, panel, e);
38265 if(e.cancel === true){
38268 var panelId = panel.getId();
38269 this.panels.removeKey(panelId);
38274 * Returns the panel specified or null if it's not in this region.
38275 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38276 * @return {Roo.ContentPanel}
38278 getPanel : function(id){
38279 if(typeof id == "object"){ // must be panel obj
38282 return this.panels.get(id);
38286 * Returns this regions position (north/south/east/west/center).
38289 getPosition: function(){
38290 return this.position;
38294 * Ext JS Library 1.1.1
38295 * Copyright(c) 2006-2007, Ext JS, LLC.
38297 * Originally Released Under LGPL - original licence link has changed is not relivant.
38300 * <script type="text/javascript">
38304 * @class Roo.bootstrap.layout.Region
38305 * @extends Roo.bootstrap.layout.Basic
38306 * This class represents a region in a layout manager.
38308 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38309 * @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})
38310 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38311 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38312 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38313 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38314 * @cfg {String} title The title for the region (overrides panel titles)
38315 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38316 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38317 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38318 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38319 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38320 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38321 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38322 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38323 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38324 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38326 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38327 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38328 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38329 * @cfg {Number} width For East/West panels
38330 * @cfg {Number} height For North/South panels
38331 * @cfg {Boolean} split To show the splitter
38332 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38334 * @cfg {string} cls Extra CSS classes to add to region
38336 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38337 * @cfg {string} region the region that it inhabits..
38340 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38341 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38343 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38344 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38345 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38347 Roo.bootstrap.layout.Region = function(config)
38349 this.applyConfig(config);
38351 var mgr = config.mgr;
38352 var pos = config.region;
38353 config.skipConfig = true;
38354 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38357 this.onRender(mgr.el);
38360 this.visible = true;
38361 this.collapsed = false;
38362 this.unrendered_panels = [];
38365 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38367 position: '', // set by wrapper (eg. north/south etc..)
38368 unrendered_panels : null, // unrendered panels.
38370 tabPosition : false,
38372 mgr: false, // points to 'Border'
38375 createBody : function(){
38376 /** This region's body element
38377 * @type Roo.Element */
38378 this.bodyEl = this.el.createChild({
38380 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38384 onRender: function(ctr, pos)
38386 var dh = Roo.DomHelper;
38387 /** This region's container element
38388 * @type Roo.Element */
38389 this.el = dh.append(ctr.dom, {
38391 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38393 /** This region's title element
38394 * @type Roo.Element */
38396 this.titleEl = dh.append(this.el.dom, {
38398 unselectable: "on",
38399 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38401 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38402 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38406 this.titleEl.enableDisplayMode();
38407 /** This region's title text element
38408 * @type HTMLElement */
38409 this.titleTextEl = this.titleEl.dom.firstChild;
38410 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38412 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38413 this.closeBtn.enableDisplayMode();
38414 this.closeBtn.on("click", this.closeClicked, this);
38415 this.closeBtn.hide();
38417 this.createBody(this.config);
38418 if(this.config.hideWhenEmpty){
38420 this.on("paneladded", this.validateVisibility, this);
38421 this.on("panelremoved", this.validateVisibility, this);
38423 if(this.autoScroll){
38424 this.bodyEl.setStyle("overflow", "auto");
38426 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38428 //if(c.titlebar !== false){
38429 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38430 this.titleEl.hide();
38432 this.titleEl.show();
38433 if(this.config.title){
38434 this.titleTextEl.innerHTML = this.config.title;
38438 if(this.config.collapsed){
38439 this.collapse(true);
38441 if(this.config.hidden){
38445 if (this.unrendered_panels && this.unrendered_panels.length) {
38446 for (var i =0;i< this.unrendered_panels.length; i++) {
38447 this.add(this.unrendered_panels[i]);
38449 this.unrendered_panels = null;
38455 applyConfig : function(c)
38458 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38459 var dh = Roo.DomHelper;
38460 if(c.titlebar !== false){
38461 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38462 this.collapseBtn.on("click", this.collapse, this);
38463 this.collapseBtn.enableDisplayMode();
38465 if(c.showPin === true || this.showPin){
38466 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38467 this.stickBtn.enableDisplayMode();
38468 this.stickBtn.on("click", this.expand, this);
38469 this.stickBtn.hide();
38474 /** This region's collapsed element
38475 * @type Roo.Element */
38478 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38479 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38482 if(c.floatable !== false){
38483 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38484 this.collapsedEl.on("click", this.collapseClick, this);
38487 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38488 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38489 id: "message", unselectable: "on", style:{"float":"left"}});
38490 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38492 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38493 this.expandBtn.on("click", this.expand, this);
38497 if(this.collapseBtn){
38498 this.collapseBtn.setVisible(c.collapsible == true);
38501 this.cmargins = c.cmargins || this.cmargins ||
38502 (this.position == "west" || this.position == "east" ?
38503 {top: 0, left: 2, right:2, bottom: 0} :
38504 {top: 2, left: 0, right:0, bottom: 2});
38506 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38509 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38511 this.autoScroll = c.autoScroll || false;
38516 this.duration = c.duration || .30;
38517 this.slideDuration = c.slideDuration || .45;
38522 * Returns true if this region is currently visible.
38523 * @return {Boolean}
38525 isVisible : function(){
38526 return this.visible;
38530 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38531 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38533 //setCollapsedTitle : function(title){
38534 // title = title || " ";
38535 // if(this.collapsedTitleTextEl){
38536 // this.collapsedTitleTextEl.innerHTML = title;
38540 getBox : function(){
38542 // if(!this.collapsed){
38543 b = this.el.getBox(false, true);
38545 // b = this.collapsedEl.getBox(false, true);
38550 getMargins : function(){
38551 return this.margins;
38552 //return this.collapsed ? this.cmargins : this.margins;
38555 highlight : function(){
38556 this.el.addClass("x-layout-panel-dragover");
38559 unhighlight : function(){
38560 this.el.removeClass("x-layout-panel-dragover");
38563 updateBox : function(box)
38565 if (!this.bodyEl) {
38566 return; // not rendered yet..
38570 if(!this.collapsed){
38571 this.el.dom.style.left = box.x + "px";
38572 this.el.dom.style.top = box.y + "px";
38573 this.updateBody(box.width, box.height);
38575 this.collapsedEl.dom.style.left = box.x + "px";
38576 this.collapsedEl.dom.style.top = box.y + "px";
38577 this.collapsedEl.setSize(box.width, box.height);
38580 this.tabs.autoSizeTabs();
38584 updateBody : function(w, h)
38587 this.el.setWidth(w);
38588 w -= this.el.getBorderWidth("rl");
38589 if(this.config.adjustments){
38590 w += this.config.adjustments[0];
38593 if(h !== null && h > 0){
38594 this.el.setHeight(h);
38595 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38596 h -= this.el.getBorderWidth("tb");
38597 if(this.config.adjustments){
38598 h += this.config.adjustments[1];
38600 this.bodyEl.setHeight(h);
38602 h = this.tabs.syncHeight(h);
38605 if(this.panelSize){
38606 w = w !== null ? w : this.panelSize.width;
38607 h = h !== null ? h : this.panelSize.height;
38609 if(this.activePanel){
38610 var el = this.activePanel.getEl();
38611 w = w !== null ? w : el.getWidth();
38612 h = h !== null ? h : el.getHeight();
38613 this.panelSize = {width: w, height: h};
38614 this.activePanel.setSize(w, h);
38616 if(Roo.isIE && this.tabs){
38617 this.tabs.el.repaint();
38622 * Returns the container element for this region.
38623 * @return {Roo.Element}
38625 getEl : function(){
38630 * Hides this region.
38633 //if(!this.collapsed){
38634 this.el.dom.style.left = "-2000px";
38637 // this.collapsedEl.dom.style.left = "-2000px";
38638 // this.collapsedEl.hide();
38640 this.visible = false;
38641 this.fireEvent("visibilitychange", this, false);
38645 * Shows this region if it was previously hidden.
38648 //if(!this.collapsed){
38651 // this.collapsedEl.show();
38653 this.visible = true;
38654 this.fireEvent("visibilitychange", this, true);
38657 closeClicked : function(){
38658 if(this.activePanel){
38659 this.remove(this.activePanel);
38663 collapseClick : function(e){
38665 e.stopPropagation();
38668 e.stopPropagation();
38674 * Collapses this region.
38675 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38678 collapse : function(skipAnim, skipCheck = false){
38679 if(this.collapsed) {
38683 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38685 this.collapsed = true;
38687 this.split.el.hide();
38689 if(this.config.animate && skipAnim !== true){
38690 this.fireEvent("invalidated", this);
38691 this.animateCollapse();
38693 this.el.setLocation(-20000,-20000);
38695 this.collapsedEl.show();
38696 this.fireEvent("collapsed", this);
38697 this.fireEvent("invalidated", this);
38703 animateCollapse : function(){
38708 * Expands this region if it was previously collapsed.
38709 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38710 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38713 expand : function(e, skipAnim){
38715 e.stopPropagation();
38717 if(!this.collapsed || this.el.hasActiveFx()) {
38721 this.afterSlideIn();
38724 this.collapsed = false;
38725 if(this.config.animate && skipAnim !== true){
38726 this.animateExpand();
38730 this.split.el.show();
38732 this.collapsedEl.setLocation(-2000,-2000);
38733 this.collapsedEl.hide();
38734 this.fireEvent("invalidated", this);
38735 this.fireEvent("expanded", this);
38739 animateExpand : function(){
38743 initTabs : function()
38745 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38747 var ts = new Roo.bootstrap.panel.Tabs({
38748 el: this.bodyEl.dom,
38750 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38751 disableTooltips: this.config.disableTabTips,
38752 toolbar : this.config.toolbar
38755 if(this.config.hideTabs){
38756 ts.stripWrap.setDisplayed(false);
38759 ts.resizeTabs = this.config.resizeTabs === true;
38760 ts.minTabWidth = this.config.minTabWidth || 40;
38761 ts.maxTabWidth = this.config.maxTabWidth || 250;
38762 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38763 ts.monitorResize = false;
38764 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38765 ts.bodyEl.addClass('roo-layout-tabs-body');
38766 this.panels.each(this.initPanelAsTab, this);
38769 initPanelAsTab : function(panel){
38770 var ti = this.tabs.addTab(
38774 this.config.closeOnTab && panel.isClosable(),
38777 if(panel.tabTip !== undefined){
38778 ti.setTooltip(panel.tabTip);
38780 ti.on("activate", function(){
38781 this.setActivePanel(panel);
38784 if(this.config.closeOnTab){
38785 ti.on("beforeclose", function(t, e){
38787 this.remove(panel);
38791 panel.tabItem = ti;
38796 updatePanelTitle : function(panel, title)
38798 if(this.activePanel == panel){
38799 this.updateTitle(title);
38802 var ti = this.tabs.getTab(panel.getEl().id);
38804 if(panel.tabTip !== undefined){
38805 ti.setTooltip(panel.tabTip);
38810 updateTitle : function(title){
38811 if(this.titleTextEl && !this.config.title){
38812 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38816 setActivePanel : function(panel)
38818 panel = this.getPanel(panel);
38819 if(this.activePanel && this.activePanel != panel){
38820 if(this.activePanel.setActiveState(false) === false){
38824 this.activePanel = panel;
38825 panel.setActiveState(true);
38826 if(this.panelSize){
38827 panel.setSize(this.panelSize.width, this.panelSize.height);
38830 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38832 this.updateTitle(panel.getTitle());
38834 this.fireEvent("invalidated", this);
38836 this.fireEvent("panelactivated", this, panel);
38840 * Shows the specified panel.
38841 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38842 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38844 showPanel : function(panel)
38846 panel = this.getPanel(panel);
38849 var tab = this.tabs.getTab(panel.getEl().id);
38850 if(tab.isHidden()){
38851 this.tabs.unhideTab(tab.id);
38855 this.setActivePanel(panel);
38862 * Get the active panel for this region.
38863 * @return {Roo.ContentPanel} The active panel or null
38865 getActivePanel : function(){
38866 return this.activePanel;
38869 validateVisibility : function(){
38870 if(this.panels.getCount() < 1){
38871 this.updateTitle(" ");
38872 this.closeBtn.hide();
38875 if(!this.isVisible()){
38882 * Adds the passed ContentPanel(s) to this region.
38883 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38884 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38886 add : function(panel)
38888 if(arguments.length > 1){
38889 for(var i = 0, len = arguments.length; i < len; i++) {
38890 this.add(arguments[i]);
38895 // if we have not been rendered yet, then we can not really do much of this..
38896 if (!this.bodyEl) {
38897 this.unrendered_panels.push(panel);
38904 if(this.hasPanel(panel)){
38905 this.showPanel(panel);
38908 panel.setRegion(this);
38909 this.panels.add(panel);
38910 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38911 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38912 // and hide them... ???
38913 this.bodyEl.dom.appendChild(panel.getEl().dom);
38914 if(panel.background !== true){
38915 this.setActivePanel(panel);
38917 this.fireEvent("paneladded", this, panel);
38924 this.initPanelAsTab(panel);
38928 if(panel.background !== true){
38929 this.tabs.activate(panel.getEl().id);
38931 this.fireEvent("paneladded", this, panel);
38936 * Hides the tab for the specified panel.
38937 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38939 hidePanel : function(panel){
38940 if(this.tabs && (panel = this.getPanel(panel))){
38941 this.tabs.hideTab(panel.getEl().id);
38946 * Unhides the tab for a previously hidden panel.
38947 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38949 unhidePanel : function(panel){
38950 if(this.tabs && (panel = this.getPanel(panel))){
38951 this.tabs.unhideTab(panel.getEl().id);
38955 clearPanels : function(){
38956 while(this.panels.getCount() > 0){
38957 this.remove(this.panels.first());
38962 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38963 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38964 * @param {Boolean} preservePanel Overrides the config preservePanel option
38965 * @return {Roo.ContentPanel} The panel that was removed
38967 remove : function(panel, preservePanel)
38969 panel = this.getPanel(panel);
38974 this.fireEvent("beforeremove", this, panel, e);
38975 if(e.cancel === true){
38978 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38979 var panelId = panel.getId();
38980 this.panels.removeKey(panelId);
38982 document.body.appendChild(panel.getEl().dom);
38985 this.tabs.removeTab(panel.getEl().id);
38986 }else if (!preservePanel){
38987 this.bodyEl.dom.removeChild(panel.getEl().dom);
38989 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38990 var p = this.panels.first();
38991 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38992 tempEl.appendChild(p.getEl().dom);
38993 this.bodyEl.update("");
38994 this.bodyEl.dom.appendChild(p.getEl().dom);
38996 this.updateTitle(p.getTitle());
38998 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38999 this.setActivePanel(p);
39001 panel.setRegion(null);
39002 if(this.activePanel == panel){
39003 this.activePanel = null;
39005 if(this.config.autoDestroy !== false && preservePanel !== true){
39006 try{panel.destroy();}catch(e){}
39008 this.fireEvent("panelremoved", this, panel);
39013 * Returns the TabPanel component used by this region
39014 * @return {Roo.TabPanel}
39016 getTabs : function(){
39020 createTool : function(parentEl, className){
39021 var btn = Roo.DomHelper.append(parentEl, {
39023 cls: "x-layout-tools-button",
39026 cls: "roo-layout-tools-button-inner " + className,
39030 btn.addClassOnOver("roo-layout-tools-button-over");
39035 * Ext JS Library 1.1.1
39036 * Copyright(c) 2006-2007, Ext JS, LLC.
39038 * Originally Released Under LGPL - original licence link has changed is not relivant.
39041 * <script type="text/javascript">
39047 * @class Roo.SplitLayoutRegion
39048 * @extends Roo.LayoutRegion
39049 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39051 Roo.bootstrap.layout.Split = function(config){
39052 this.cursor = config.cursor;
39053 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39056 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39058 splitTip : "Drag to resize.",
39059 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39060 useSplitTips : false,
39062 applyConfig : function(config){
39063 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39066 onRender : function(ctr,pos) {
39068 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39069 if(!this.config.split){
39074 var splitEl = Roo.DomHelper.append(ctr.dom, {
39076 id: this.el.id + "-split",
39077 cls: "roo-layout-split roo-layout-split-"+this.position,
39080 /** The SplitBar for this region
39081 * @type Roo.SplitBar */
39082 // does not exist yet...
39083 Roo.log([this.position, this.orientation]);
39085 this.split = new Roo.bootstrap.SplitBar({
39086 dragElement : splitEl,
39087 resizingElement: this.el,
39088 orientation : this.orientation
39091 this.split.on("moved", this.onSplitMove, this);
39092 this.split.useShim = this.config.useShim === true;
39093 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39094 if(this.useSplitTips){
39095 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39097 //if(config.collapsible){
39098 // this.split.el.on("dblclick", this.collapse, this);
39101 if(typeof this.config.minSize != "undefined"){
39102 this.split.minSize = this.config.minSize;
39104 if(typeof this.config.maxSize != "undefined"){
39105 this.split.maxSize = this.config.maxSize;
39107 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39108 this.hideSplitter();
39113 getHMaxSize : function(){
39114 var cmax = this.config.maxSize || 10000;
39115 var center = this.mgr.getRegion("center");
39116 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39119 getVMaxSize : function(){
39120 var cmax = this.config.maxSize || 10000;
39121 var center = this.mgr.getRegion("center");
39122 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39125 onSplitMove : function(split, newSize){
39126 this.fireEvent("resized", this, newSize);
39130 * Returns the {@link Roo.SplitBar} for this region.
39131 * @return {Roo.SplitBar}
39133 getSplitBar : function(){
39138 this.hideSplitter();
39139 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39142 hideSplitter : function(){
39144 this.split.el.setLocation(-2000,-2000);
39145 this.split.el.hide();
39151 this.split.el.show();
39153 Roo.bootstrap.layout.Split.superclass.show.call(this);
39156 beforeSlide: function(){
39157 if(Roo.isGecko){// firefox overflow auto bug workaround
39158 this.bodyEl.clip();
39160 this.tabs.bodyEl.clip();
39162 if(this.activePanel){
39163 this.activePanel.getEl().clip();
39165 if(this.activePanel.beforeSlide){
39166 this.activePanel.beforeSlide();
39172 afterSlide : function(){
39173 if(Roo.isGecko){// firefox overflow auto bug workaround
39174 this.bodyEl.unclip();
39176 this.tabs.bodyEl.unclip();
39178 if(this.activePanel){
39179 this.activePanel.getEl().unclip();
39180 if(this.activePanel.afterSlide){
39181 this.activePanel.afterSlide();
39187 initAutoHide : function(){
39188 if(this.autoHide !== false){
39189 if(!this.autoHideHd){
39190 var st = new Roo.util.DelayedTask(this.slideIn, this);
39191 this.autoHideHd = {
39192 "mouseout": function(e){
39193 if(!e.within(this.el, true)){
39197 "mouseover" : function(e){
39203 this.el.on(this.autoHideHd);
39207 clearAutoHide : function(){
39208 if(this.autoHide !== false){
39209 this.el.un("mouseout", this.autoHideHd.mouseout);
39210 this.el.un("mouseover", this.autoHideHd.mouseover);
39214 clearMonitor : function(){
39215 Roo.get(document).un("click", this.slideInIf, this);
39218 // these names are backwards but not changed for compat
39219 slideOut : function(){
39220 if(this.isSlid || this.el.hasActiveFx()){
39223 this.isSlid = true;
39224 if(this.collapseBtn){
39225 this.collapseBtn.hide();
39227 this.closeBtnState = this.closeBtn.getStyle('display');
39228 this.closeBtn.hide();
39230 this.stickBtn.show();
39233 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39234 this.beforeSlide();
39235 this.el.setStyle("z-index", 10001);
39236 this.el.slideIn(this.getSlideAnchor(), {
39237 callback: function(){
39239 this.initAutoHide();
39240 Roo.get(document).on("click", this.slideInIf, this);
39241 this.fireEvent("slideshow", this);
39248 afterSlideIn : function(){
39249 this.clearAutoHide();
39250 this.isSlid = false;
39251 this.clearMonitor();
39252 this.el.setStyle("z-index", "");
39253 if(this.collapseBtn){
39254 this.collapseBtn.show();
39256 this.closeBtn.setStyle('display', this.closeBtnState);
39258 this.stickBtn.hide();
39260 this.fireEvent("slidehide", this);
39263 slideIn : function(cb){
39264 if(!this.isSlid || this.el.hasActiveFx()){
39268 this.isSlid = false;
39269 this.beforeSlide();
39270 this.el.slideOut(this.getSlideAnchor(), {
39271 callback: function(){
39272 this.el.setLeftTop(-10000, -10000);
39274 this.afterSlideIn();
39282 slideInIf : function(e){
39283 if(!e.within(this.el)){
39288 animateCollapse : function(){
39289 this.beforeSlide();
39290 this.el.setStyle("z-index", 20000);
39291 var anchor = this.getSlideAnchor();
39292 this.el.slideOut(anchor, {
39293 callback : function(){
39294 this.el.setStyle("z-index", "");
39295 this.collapsedEl.slideIn(anchor, {duration:.3});
39297 this.el.setLocation(-10000,-10000);
39299 this.fireEvent("collapsed", this);
39306 animateExpand : function(){
39307 this.beforeSlide();
39308 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39309 this.el.setStyle("z-index", 20000);
39310 this.collapsedEl.hide({
39313 this.el.slideIn(this.getSlideAnchor(), {
39314 callback : function(){
39315 this.el.setStyle("z-index", "");
39318 this.split.el.show();
39320 this.fireEvent("invalidated", this);
39321 this.fireEvent("expanded", this);
39349 getAnchor : function(){
39350 return this.anchors[this.position];
39353 getCollapseAnchor : function(){
39354 return this.canchors[this.position];
39357 getSlideAnchor : function(){
39358 return this.sanchors[this.position];
39361 getAlignAdj : function(){
39362 var cm = this.cmargins;
39363 switch(this.position){
39379 getExpandAdj : function(){
39380 var c = this.collapsedEl, cm = this.cmargins;
39381 switch(this.position){
39383 return [-(cm.right+c.getWidth()+cm.left), 0];
39386 return [cm.right+c.getWidth()+cm.left, 0];
39389 return [0, -(cm.top+cm.bottom+c.getHeight())];
39392 return [0, cm.top+cm.bottom+c.getHeight()];
39398 * Ext JS Library 1.1.1
39399 * Copyright(c) 2006-2007, Ext JS, LLC.
39401 * Originally Released Under LGPL - original licence link has changed is not relivant.
39404 * <script type="text/javascript">
39407 * These classes are private internal classes
39409 Roo.bootstrap.layout.Center = function(config){
39410 config.region = "center";
39411 Roo.bootstrap.layout.Region.call(this, config);
39412 this.visible = true;
39413 this.minWidth = config.minWidth || 20;
39414 this.minHeight = config.minHeight || 20;
39417 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39419 // center panel can't be hidden
39423 // center panel can't be hidden
39426 getMinWidth: function(){
39427 return this.minWidth;
39430 getMinHeight: function(){
39431 return this.minHeight;
39445 Roo.bootstrap.layout.North = function(config)
39447 config.region = 'north';
39448 config.cursor = 'n-resize';
39450 Roo.bootstrap.layout.Split.call(this, config);
39454 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39455 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39456 this.split.el.addClass("roo-layout-split-v");
39458 //var size = config.initialSize || config.height;
39459 //if(this.el && typeof size != "undefined"){
39460 // this.el.setHeight(size);
39463 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39465 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39468 onRender : function(ctr, pos)
39470 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39471 var size = this.config.initialSize || this.config.height;
39472 if(this.el && typeof size != "undefined"){
39473 this.el.setHeight(size);
39478 getBox : function(){
39479 if(this.collapsed){
39480 return this.collapsedEl.getBox();
39482 var box = this.el.getBox();
39484 box.height += this.split.el.getHeight();
39489 updateBox : function(box){
39490 if(this.split && !this.collapsed){
39491 box.height -= this.split.el.getHeight();
39492 this.split.el.setLeft(box.x);
39493 this.split.el.setTop(box.y+box.height);
39494 this.split.el.setWidth(box.width);
39496 if(this.collapsed){
39497 this.updateBody(box.width, null);
39499 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39507 Roo.bootstrap.layout.South = function(config){
39508 config.region = 'south';
39509 config.cursor = 's-resize';
39510 Roo.bootstrap.layout.Split.call(this, config);
39512 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39513 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39514 this.split.el.addClass("roo-layout-split-v");
39519 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39520 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39522 onRender : function(ctr, pos)
39524 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39525 var size = this.config.initialSize || this.config.height;
39526 if(this.el && typeof size != "undefined"){
39527 this.el.setHeight(size);
39532 getBox : function(){
39533 if(this.collapsed){
39534 return this.collapsedEl.getBox();
39536 var box = this.el.getBox();
39538 var sh = this.split.el.getHeight();
39545 updateBox : function(box){
39546 if(this.split && !this.collapsed){
39547 var sh = this.split.el.getHeight();
39550 this.split.el.setLeft(box.x);
39551 this.split.el.setTop(box.y-sh);
39552 this.split.el.setWidth(box.width);
39554 if(this.collapsed){
39555 this.updateBody(box.width, null);
39557 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39561 Roo.bootstrap.layout.East = function(config){
39562 config.region = "east";
39563 config.cursor = "e-resize";
39564 Roo.bootstrap.layout.Split.call(this, config);
39566 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39567 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39568 this.split.el.addClass("roo-layout-split-h");
39572 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39573 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39575 onRender : function(ctr, pos)
39577 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39578 var size = this.config.initialSize || this.config.width;
39579 if(this.el && typeof size != "undefined"){
39580 this.el.setWidth(size);
39585 getBox : function(){
39586 if(this.collapsed){
39587 return this.collapsedEl.getBox();
39589 var box = this.el.getBox();
39591 var sw = this.split.el.getWidth();
39598 updateBox : function(box){
39599 if(this.split && !this.collapsed){
39600 var sw = this.split.el.getWidth();
39602 this.split.el.setLeft(box.x);
39603 this.split.el.setTop(box.y);
39604 this.split.el.setHeight(box.height);
39607 if(this.collapsed){
39608 this.updateBody(null, box.height);
39610 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39614 Roo.bootstrap.layout.West = function(config){
39615 config.region = "west";
39616 config.cursor = "w-resize";
39618 Roo.bootstrap.layout.Split.call(this, config);
39620 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39621 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39622 this.split.el.addClass("roo-layout-split-h");
39626 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39627 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39629 onRender: function(ctr, pos)
39631 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39632 var size = this.config.initialSize || this.config.width;
39633 if(typeof size != "undefined"){
39634 this.el.setWidth(size);
39638 getBox : function(){
39639 if(this.collapsed){
39640 return this.collapsedEl.getBox();
39642 var box = this.el.getBox();
39643 if (box.width == 0) {
39644 box.width = this.config.width; // kludge?
39647 box.width += this.split.el.getWidth();
39652 updateBox : function(box){
39653 if(this.split && !this.collapsed){
39654 var sw = this.split.el.getWidth();
39656 this.split.el.setLeft(box.x+box.width);
39657 this.split.el.setTop(box.y);
39658 this.split.el.setHeight(box.height);
39660 if(this.collapsed){
39661 this.updateBody(null, box.height);
39663 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39665 });Roo.namespace("Roo.bootstrap.panel");/*
39667 * Ext JS Library 1.1.1
39668 * Copyright(c) 2006-2007, Ext JS, LLC.
39670 * Originally Released Under LGPL - original licence link has changed is not relivant.
39673 * <script type="text/javascript">
39676 * @class Roo.ContentPanel
39677 * @extends Roo.util.Observable
39678 * A basic ContentPanel element.
39679 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39680 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39681 * @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
39682 * @cfg {Boolean} closable True if the panel can be closed/removed
39683 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39684 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39685 * @cfg {Toolbar} toolbar A toolbar for this panel
39686 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39687 * @cfg {String} title The title for this panel
39688 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39689 * @cfg {String} url Calls {@link #setUrl} with this value
39690 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39691 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39692 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39693 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39694 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39695 * @cfg {Boolean} badges render the badges
39696 * @cfg {String} cls extra classes to use
39697 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39700 * Create a new ContentPanel.
39701 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39702 * @param {String/Object} config A string to set only the title or a config object
39703 * @param {String} content (optional) Set the HTML content for this panel
39704 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39706 Roo.bootstrap.panel.Content = function( config){
39708 this.tpl = config.tpl || false;
39710 var el = config.el;
39711 var content = config.content;
39713 if(config.autoCreate){ // xtype is available if this is called from factory
39716 this.el = Roo.get(el);
39717 if(!this.el && config && config.autoCreate){
39718 if(typeof config.autoCreate == "object"){
39719 if(!config.autoCreate.id){
39720 config.autoCreate.id = config.id||el;
39722 this.el = Roo.DomHelper.append(document.body,
39723 config.autoCreate, true);
39727 cls: (config.cls || '') +
39728 (config.background ? ' bg-' + config.background : '') +
39729 " roo-layout-inactive-content",
39732 if (config.iframe) {
39736 style : 'border: 0px',
39737 src : 'about:blank'
39743 elcfg.html = config.html;
39747 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39748 if (config.iframe) {
39749 this.iframeEl = this.el.select('iframe',true).first();
39754 this.closable = false;
39755 this.loaded = false;
39756 this.active = false;
39759 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39761 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39763 this.wrapEl = this.el; //this.el.wrap();
39765 if (config.toolbar.items) {
39766 ti = config.toolbar.items ;
39767 delete config.toolbar.items ;
39771 this.toolbar.render(this.wrapEl, 'before');
39772 for(var i =0;i < ti.length;i++) {
39773 // Roo.log(['add child', items[i]]);
39774 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39776 this.toolbar.items = nitems;
39777 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39778 delete config.toolbar;
39782 // xtype created footer. - not sure if will work as we normally have to render first..
39783 if (this.footer && !this.footer.el && this.footer.xtype) {
39784 if (!this.wrapEl) {
39785 this.wrapEl = this.el.wrap();
39788 this.footer.container = this.wrapEl.createChild();
39790 this.footer = Roo.factory(this.footer, Roo);
39795 if(typeof config == "string"){
39796 this.title = config;
39798 Roo.apply(this, config);
39802 this.resizeEl = Roo.get(this.resizeEl, true);
39804 this.resizeEl = this.el;
39806 // handle view.xtype
39814 * Fires when this panel is activated.
39815 * @param {Roo.ContentPanel} this
39819 * @event deactivate
39820 * Fires when this panel is activated.
39821 * @param {Roo.ContentPanel} this
39823 "deactivate" : true,
39827 * Fires when this panel is resized if fitToFrame is true.
39828 * @param {Roo.ContentPanel} this
39829 * @param {Number} width The width after any component adjustments
39830 * @param {Number} height The height after any component adjustments
39836 * Fires when this tab is created
39837 * @param {Roo.ContentPanel} this
39848 if(this.autoScroll && !this.iframe){
39849 this.resizeEl.setStyle("overflow", "auto");
39851 // fix randome scrolling
39852 //this.el.on('scroll', function() {
39853 // Roo.log('fix random scolling');
39854 // this.scrollTo('top',0);
39857 content = content || this.content;
39859 this.setContent(content);
39861 if(config && config.url){
39862 this.setUrl(this.url, this.params, this.loadOnce);
39867 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39869 if (this.view && typeof(this.view.xtype) != 'undefined') {
39870 this.view.el = this.el.appendChild(document.createElement("div"));
39871 this.view = Roo.factory(this.view);
39872 this.view.render && this.view.render(false, '');
39876 this.fireEvent('render', this);
39879 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39889 setRegion : function(region){
39890 this.region = region;
39891 this.setActiveClass(region && !this.background);
39895 setActiveClass: function(state)
39898 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39899 this.el.setStyle('position','relative');
39901 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39902 this.el.setStyle('position', 'absolute');
39907 * Returns the toolbar for this Panel if one was configured.
39908 * @return {Roo.Toolbar}
39910 getToolbar : function(){
39911 return this.toolbar;
39914 setActiveState : function(active)
39916 this.active = active;
39917 this.setActiveClass(active);
39919 if(this.fireEvent("deactivate", this) === false){
39924 this.fireEvent("activate", this);
39928 * Updates this panel's element (not for iframe)
39929 * @param {String} content The new content
39930 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39932 setContent : function(content, loadScripts){
39937 this.el.update(content, loadScripts);
39940 ignoreResize : function(w, h){
39941 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39944 this.lastSize = {width: w, height: h};
39949 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39950 * @return {Roo.UpdateManager} The UpdateManager
39952 getUpdateManager : function(){
39956 return this.el.getUpdateManager();
39959 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39960 * Does not work with IFRAME contents
39961 * @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:
39964 url: "your-url.php",
39965 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39966 callback: yourFunction,
39967 scope: yourObject, //(optional scope)
39970 text: "Loading...",
39976 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39977 * 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.
39978 * @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}
39979 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39980 * @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.
39981 * @return {Roo.ContentPanel} this
39989 var um = this.el.getUpdateManager();
39990 um.update.apply(um, arguments);
39996 * 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.
39997 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39998 * @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)
39999 * @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)
40000 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40002 setUrl : function(url, params, loadOnce){
40004 this.iframeEl.dom.src = url;
40008 if(this.refreshDelegate){
40009 this.removeListener("activate", this.refreshDelegate);
40011 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40012 this.on("activate", this.refreshDelegate);
40013 return this.el.getUpdateManager();
40016 _handleRefresh : function(url, params, loadOnce){
40017 if(!loadOnce || !this.loaded){
40018 var updater = this.el.getUpdateManager();
40019 updater.update(url, params, this._setLoaded.createDelegate(this));
40023 _setLoaded : function(){
40024 this.loaded = true;
40028 * Returns this panel's id
40031 getId : function(){
40036 * Returns this panel's element - used by regiosn to add.
40037 * @return {Roo.Element}
40039 getEl : function(){
40040 return this.wrapEl || this.el;
40045 adjustForComponents : function(width, height)
40047 //Roo.log('adjustForComponents ');
40048 if(this.resizeEl != this.el){
40049 width -= this.el.getFrameWidth('lr');
40050 height -= this.el.getFrameWidth('tb');
40053 var te = this.toolbar.getEl();
40054 te.setWidth(width);
40055 height -= te.getHeight();
40058 var te = this.footer.getEl();
40059 te.setWidth(width);
40060 height -= te.getHeight();
40064 if(this.adjustments){
40065 width += this.adjustments[0];
40066 height += this.adjustments[1];
40068 return {"width": width, "height": height};
40071 setSize : function(width, height){
40072 if(this.fitToFrame && !this.ignoreResize(width, height)){
40073 if(this.fitContainer && this.resizeEl != this.el){
40074 this.el.setSize(width, height);
40076 var size = this.adjustForComponents(width, height);
40078 this.iframeEl.setSize(width,height);
40081 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40082 this.fireEvent('resize', this, size.width, size.height);
40089 * Returns this panel's title
40092 getTitle : function(){
40094 if (typeof(this.title) != 'object') {
40099 for (var k in this.title) {
40100 if (!this.title.hasOwnProperty(k)) {
40104 if (k.indexOf('-') >= 0) {
40105 var s = k.split('-');
40106 for (var i = 0; i<s.length; i++) {
40107 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40110 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40117 * Set this panel's title
40118 * @param {String} title
40120 setTitle : function(title){
40121 this.title = title;
40123 this.region.updatePanelTitle(this, title);
40128 * Returns true is this panel was configured to be closable
40129 * @return {Boolean}
40131 isClosable : function(){
40132 return this.closable;
40135 beforeSlide : function(){
40137 this.resizeEl.clip();
40140 afterSlide : function(){
40142 this.resizeEl.unclip();
40146 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40147 * Will fail silently if the {@link #setUrl} method has not been called.
40148 * This does not activate the panel, just updates its content.
40150 refresh : function(){
40151 if(this.refreshDelegate){
40152 this.loaded = false;
40153 this.refreshDelegate();
40158 * Destroys this panel
40160 destroy : function(){
40161 this.el.removeAllListeners();
40162 var tempEl = document.createElement("span");
40163 tempEl.appendChild(this.el.dom);
40164 tempEl.innerHTML = "";
40170 * form - if the content panel contains a form - this is a reference to it.
40171 * @type {Roo.form.Form}
40175 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40176 * This contains a reference to it.
40182 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40192 * @param {Object} cfg Xtype definition of item to add.
40196 getChildContainer: function () {
40197 return this.getEl();
40202 var ret = new Roo.factory(cfg);
40207 if (cfg.xtype.match(/^Form$/)) {
40210 //if (this.footer) {
40211 // el = this.footer.container.insertSibling(false, 'before');
40213 el = this.el.createChild();
40216 this.form = new Roo.form.Form(cfg);
40219 if ( this.form.allItems.length) {
40220 this.form.render(el.dom);
40224 // should only have one of theses..
40225 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40226 // views.. should not be just added - used named prop 'view''
40228 cfg.el = this.el.appendChild(document.createElement("div"));
40231 var ret = new Roo.factory(cfg);
40233 ret.render && ret.render(false, ''); // render blank..
40243 * @class Roo.bootstrap.panel.Grid
40244 * @extends Roo.bootstrap.panel.Content
40246 * Create a new GridPanel.
40247 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40248 * @param {Object} config A the config object
40254 Roo.bootstrap.panel.Grid = function(config)
40258 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40259 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40261 config.el = this.wrapper;
40262 //this.el = this.wrapper;
40264 if (config.container) {
40265 // ctor'ed from a Border/panel.grid
40268 this.wrapper.setStyle("overflow", "hidden");
40269 this.wrapper.addClass('roo-grid-container');
40274 if(config.toolbar){
40275 var tool_el = this.wrapper.createChild();
40276 this.toolbar = Roo.factory(config.toolbar);
40278 if (config.toolbar.items) {
40279 ti = config.toolbar.items ;
40280 delete config.toolbar.items ;
40284 this.toolbar.render(tool_el);
40285 for(var i =0;i < ti.length;i++) {
40286 // Roo.log(['add child', items[i]]);
40287 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40289 this.toolbar.items = nitems;
40291 delete config.toolbar;
40294 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40295 config.grid.scrollBody = true;;
40296 config.grid.monitorWindowResize = false; // turn off autosizing
40297 config.grid.autoHeight = false;
40298 config.grid.autoWidth = false;
40300 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40302 if (config.background) {
40303 // render grid on panel activation (if panel background)
40304 this.on('activate', function(gp) {
40305 if (!gp.grid.rendered) {
40306 gp.grid.render(this.wrapper);
40307 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40312 this.grid.render(this.wrapper);
40313 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40316 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40317 // ??? needed ??? config.el = this.wrapper;
40322 // xtype created footer. - not sure if will work as we normally have to render first..
40323 if (this.footer && !this.footer.el && this.footer.xtype) {
40325 var ctr = this.grid.getView().getFooterPanel(true);
40326 this.footer.dataSource = this.grid.dataSource;
40327 this.footer = Roo.factory(this.footer, Roo);
40328 this.footer.render(ctr);
40338 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40339 getId : function(){
40340 return this.grid.id;
40344 * Returns the grid for this panel
40345 * @return {Roo.bootstrap.Table}
40347 getGrid : function(){
40351 setSize : function(width, height){
40352 if(!this.ignoreResize(width, height)){
40353 var grid = this.grid;
40354 var size = this.adjustForComponents(width, height);
40355 // tfoot is not a footer?
40358 var gridel = grid.getGridEl();
40359 gridel.setSize(size.width, size.height);
40361 var tbd = grid.getGridEl().select('tbody', true).first();
40362 var thd = grid.getGridEl().select('thead',true).first();
40363 var tbf= grid.getGridEl().select('tfoot', true).first();
40366 size.height -= tbf.getHeight();
40369 size.height -= thd.getHeight();
40372 tbd.setSize(size.width, size.height );
40373 // this is for the account management tab -seems to work there.
40374 var thd = grid.getGridEl().select('thead',true).first();
40376 // tbd.setSize(size.width, size.height - thd.getHeight());
40385 beforeSlide : function(){
40386 this.grid.getView().scroller.clip();
40389 afterSlide : function(){
40390 this.grid.getView().scroller.unclip();
40393 destroy : function(){
40394 this.grid.destroy();
40396 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40401 * @class Roo.bootstrap.panel.Nest
40402 * @extends Roo.bootstrap.panel.Content
40404 * Create a new Panel, that can contain a layout.Border.
40407 * @param {Roo.BorderLayout} layout The layout for this panel
40408 * @param {String/Object} config A string to set only the title or a config object
40410 Roo.bootstrap.panel.Nest = function(config)
40412 // construct with only one argument..
40413 /* FIXME - implement nicer consturctors
40414 if (layout.layout) {
40416 layout = config.layout;
40417 delete config.layout;
40419 if (layout.xtype && !layout.getEl) {
40420 // then layout needs constructing..
40421 layout = Roo.factory(layout, Roo);
40425 config.el = config.layout.getEl();
40427 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40429 config.layout.monitorWindowResize = false; // turn off autosizing
40430 this.layout = config.layout;
40431 this.layout.getEl().addClass("roo-layout-nested-layout");
40432 this.layout.parent = this;
40439 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40441 setSize : function(width, height){
40442 if(!this.ignoreResize(width, height)){
40443 var size = this.adjustForComponents(width, height);
40444 var el = this.layout.getEl();
40445 if (size.height < 1) {
40446 el.setWidth(size.width);
40448 el.setSize(size.width, size.height);
40450 var touch = el.dom.offsetWidth;
40451 this.layout.layout();
40452 // ie requires a double layout on the first pass
40453 if(Roo.isIE && !this.initialized){
40454 this.initialized = true;
40455 this.layout.layout();
40460 // activate all subpanels if not currently active..
40462 setActiveState : function(active){
40463 this.active = active;
40464 this.setActiveClass(active);
40467 this.fireEvent("deactivate", this);
40471 this.fireEvent("activate", this);
40472 // not sure if this should happen before or after..
40473 if (!this.layout) {
40474 return; // should not happen..
40477 for (var r in this.layout.regions) {
40478 reg = this.layout.getRegion(r);
40479 if (reg.getActivePanel()) {
40480 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40481 reg.setActivePanel(reg.getActivePanel());
40484 if (!reg.panels.length) {
40487 reg.showPanel(reg.getPanel(0));
40496 * Returns the nested BorderLayout for this panel
40497 * @return {Roo.BorderLayout}
40499 getLayout : function(){
40500 return this.layout;
40504 * Adds a xtype elements to the layout of the nested panel
40508 xtype : 'ContentPanel',
40515 xtype : 'NestedLayoutPanel',
40521 items : [ ... list of content panels or nested layout panels.. ]
40525 * @param {Object} cfg Xtype definition of item to add.
40527 addxtype : function(cfg) {
40528 return this.layout.addxtype(cfg);
40533 * Ext JS Library 1.1.1
40534 * Copyright(c) 2006-2007, Ext JS, LLC.
40536 * Originally Released Under LGPL - original licence link has changed is not relivant.
40539 * <script type="text/javascript">
40542 * @class Roo.TabPanel
40543 * @extends Roo.util.Observable
40544 * A lightweight tab container.
40548 // basic tabs 1, built from existing content
40549 var tabs = new Roo.TabPanel("tabs1");
40550 tabs.addTab("script", "View Script");
40551 tabs.addTab("markup", "View Markup");
40552 tabs.activate("script");
40554 // more advanced tabs, built from javascript
40555 var jtabs = new Roo.TabPanel("jtabs");
40556 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40558 // set up the UpdateManager
40559 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40560 var updater = tab2.getUpdateManager();
40561 updater.setDefaultUrl("ajax1.htm");
40562 tab2.on('activate', updater.refresh, updater, true);
40564 // Use setUrl for Ajax loading
40565 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40566 tab3.setUrl("ajax2.htm", null, true);
40569 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40572 jtabs.activate("jtabs-1");
40575 * Create a new TabPanel.
40576 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40577 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40579 Roo.bootstrap.panel.Tabs = function(config){
40581 * The container element for this TabPanel.
40582 * @type Roo.Element
40584 this.el = Roo.get(config.el);
40587 if(typeof config == "boolean"){
40588 this.tabPosition = config ? "bottom" : "top";
40590 Roo.apply(this, config);
40594 if(this.tabPosition == "bottom"){
40595 // if tabs are at the bottom = create the body first.
40596 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40597 this.el.addClass("roo-tabs-bottom");
40599 // next create the tabs holders
40601 if (this.tabPosition == "west"){
40603 var reg = this.region; // fake it..
40605 if (!reg.mgr.parent) {
40608 reg = reg.mgr.parent.region;
40610 Roo.log("got nest?");
40612 if (reg.mgr.getRegion('west')) {
40613 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40614 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40615 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40616 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40617 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40625 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40626 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40627 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40628 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40633 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40636 // finally - if tabs are at the top, then create the body last..
40637 if(this.tabPosition != "bottom"){
40638 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40639 * @type Roo.Element
40641 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40642 this.el.addClass("roo-tabs-top");
40646 this.bodyEl.setStyle("position", "relative");
40648 this.active = null;
40649 this.activateDelegate = this.activate.createDelegate(this);
40654 * Fires when the active tab changes
40655 * @param {Roo.TabPanel} this
40656 * @param {Roo.TabPanelItem} activePanel The new active tab
40660 * @event beforetabchange
40661 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40662 * @param {Roo.TabPanel} this
40663 * @param {Object} e Set cancel to true on this object to cancel the tab change
40664 * @param {Roo.TabPanelItem} tab The tab being changed to
40666 "beforetabchange" : true
40669 Roo.EventManager.onWindowResize(this.onResize, this);
40670 this.cpad = this.el.getPadding("lr");
40671 this.hiddenCount = 0;
40674 // toolbar on the tabbar support...
40675 if (this.toolbar) {
40676 alert("no toolbar support yet");
40677 this.toolbar = false;
40679 var tcfg = this.toolbar;
40680 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40681 this.toolbar = new Roo.Toolbar(tcfg);
40682 if (Roo.isSafari) {
40683 var tbl = tcfg.container.child('table', true);
40684 tbl.setAttribute('width', '100%');
40692 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40695 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40697 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40699 tabPosition : "top",
40701 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40703 currentTabWidth : 0,
40705 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40709 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40713 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40715 preferredTabWidth : 175,
40717 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40719 resizeTabs : false,
40721 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40723 monitorResize : true,
40725 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40727 toolbar : false, // set by caller..
40729 region : false, /// set by caller
40731 disableTooltips : true, // not used yet...
40734 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40735 * @param {String} id The id of the div to use <b>or create</b>
40736 * @param {String} text The text for the tab
40737 * @param {String} content (optional) Content to put in the TabPanelItem body
40738 * @param {Boolean} closable (optional) True to create a close icon on the tab
40739 * @return {Roo.TabPanelItem} The created TabPanelItem
40741 addTab : function(id, text, content, closable, tpl)
40743 var item = new Roo.bootstrap.panel.TabItem({
40747 closable : closable,
40750 this.addTabItem(item);
40752 item.setContent(content);
40758 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40759 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40760 * @return {Roo.TabPanelItem}
40762 getTab : function(id){
40763 return this.items[id];
40767 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40768 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40770 hideTab : function(id){
40771 var t = this.items[id];
40774 this.hiddenCount++;
40775 this.autoSizeTabs();
40780 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40781 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40783 unhideTab : function(id){
40784 var t = this.items[id];
40786 t.setHidden(false);
40787 this.hiddenCount--;
40788 this.autoSizeTabs();
40793 * Adds an existing {@link Roo.TabPanelItem}.
40794 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40796 addTabItem : function(item)
40798 this.items[item.id] = item;
40799 this.items.push(item);
40800 this.autoSizeTabs();
40801 // if(this.resizeTabs){
40802 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40803 // this.autoSizeTabs();
40805 // item.autoSize();
40810 * Removes a {@link Roo.TabPanelItem}.
40811 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40813 removeTab : function(id){
40814 var items = this.items;
40815 var tab = items[id];
40816 if(!tab) { return; }
40817 var index = items.indexOf(tab);
40818 if(this.active == tab && items.length > 1){
40819 var newTab = this.getNextAvailable(index);
40824 this.stripEl.dom.removeChild(tab.pnode.dom);
40825 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40826 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40828 items.splice(index, 1);
40829 delete this.items[tab.id];
40830 tab.fireEvent("close", tab);
40831 tab.purgeListeners();
40832 this.autoSizeTabs();
40835 getNextAvailable : function(start){
40836 var items = this.items;
40838 // look for a next tab that will slide over to
40839 // replace the one being removed
40840 while(index < items.length){
40841 var item = items[++index];
40842 if(item && !item.isHidden()){
40846 // if one isn't found select the previous tab (on the left)
40849 var item = items[--index];
40850 if(item && !item.isHidden()){
40858 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40859 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40861 disableTab : function(id){
40862 var tab = this.items[id];
40863 if(tab && this.active != tab){
40869 * Enables a {@link Roo.TabPanelItem} that is disabled.
40870 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40872 enableTab : function(id){
40873 var tab = this.items[id];
40878 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40879 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40880 * @return {Roo.TabPanelItem} The TabPanelItem.
40882 activate : function(id)
40884 //Roo.log('activite:' + id);
40886 var tab = this.items[id];
40890 if(tab == this.active || tab.disabled){
40894 this.fireEvent("beforetabchange", this, e, tab);
40895 if(e.cancel !== true && !tab.disabled){
40897 this.active.hide();
40899 this.active = this.items[id];
40900 this.active.show();
40901 this.fireEvent("tabchange", this, this.active);
40907 * Gets the active {@link Roo.TabPanelItem}.
40908 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40910 getActiveTab : function(){
40911 return this.active;
40915 * Updates the tab body element to fit the height of the container element
40916 * for overflow scrolling
40917 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40919 syncHeight : function(targetHeight){
40920 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40921 var bm = this.bodyEl.getMargins();
40922 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40923 this.bodyEl.setHeight(newHeight);
40927 onResize : function(){
40928 if(this.monitorResize){
40929 this.autoSizeTabs();
40934 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40936 beginUpdate : function(){
40937 this.updating = true;
40941 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40943 endUpdate : function(){
40944 this.updating = false;
40945 this.autoSizeTabs();
40949 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40951 autoSizeTabs : function()
40953 var count = this.items.length;
40954 var vcount = count - this.hiddenCount;
40957 this.stripEl.hide();
40959 this.stripEl.show();
40962 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40967 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40968 var availWidth = Math.floor(w / vcount);
40969 var b = this.stripBody;
40970 if(b.getWidth() > w){
40971 var tabs = this.items;
40972 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40973 if(availWidth < this.minTabWidth){
40974 /*if(!this.sleft){ // incomplete scrolling code
40975 this.createScrollButtons();
40978 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40981 if(this.currentTabWidth < this.preferredTabWidth){
40982 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40988 * Returns the number of tabs in this TabPanel.
40991 getCount : function(){
40992 return this.items.length;
40996 * Resizes all the tabs to the passed width
40997 * @param {Number} The new width
40999 setTabWidth : function(width){
41000 this.currentTabWidth = width;
41001 for(var i = 0, len = this.items.length; i < len; i++) {
41002 if(!this.items[i].isHidden()) {
41003 this.items[i].setWidth(width);
41009 * Destroys this TabPanel
41010 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41012 destroy : function(removeEl){
41013 Roo.EventManager.removeResizeListener(this.onResize, this);
41014 for(var i = 0, len = this.items.length; i < len; i++){
41015 this.items[i].purgeListeners();
41017 if(removeEl === true){
41018 this.el.update("");
41023 createStrip : function(container)
41025 var strip = document.createElement("nav");
41026 strip.className = Roo.bootstrap.version == 4 ?
41027 "navbar-light bg-light" :
41028 "navbar navbar-default"; //"x-tabs-wrap";
41029 container.appendChild(strip);
41033 createStripList : function(strip)
41035 // div wrapper for retard IE
41036 // returns the "tr" element.
41037 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41038 //'<div class="x-tabs-strip-wrap">'+
41039 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41040 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41041 return strip.firstChild; //.firstChild.firstChild.firstChild;
41043 createBody : function(container)
41045 var body = document.createElement("div");
41046 Roo.id(body, "tab-body");
41047 //Roo.fly(body).addClass("x-tabs-body");
41048 Roo.fly(body).addClass("tab-content");
41049 container.appendChild(body);
41052 createItemBody :function(bodyEl, id){
41053 var body = Roo.getDom(id);
41055 body = document.createElement("div");
41058 //Roo.fly(body).addClass("x-tabs-item-body");
41059 Roo.fly(body).addClass("tab-pane");
41060 bodyEl.insertBefore(body, bodyEl.firstChild);
41064 createStripElements : function(stripEl, text, closable, tpl)
41066 var td = document.createElement("li"); // was td..
41067 td.className = 'nav-item';
41069 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41072 stripEl.appendChild(td);
41074 td.className = "x-tabs-closable";
41075 if(!this.closeTpl){
41076 this.closeTpl = new Roo.Template(
41077 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41078 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41079 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41082 var el = this.closeTpl.overwrite(td, {"text": text});
41083 var close = el.getElementsByTagName("div")[0];
41084 var inner = el.getElementsByTagName("em")[0];
41085 return {"el": el, "close": close, "inner": inner};
41088 // not sure what this is..
41089 // if(!this.tabTpl){
41090 //this.tabTpl = new Roo.Template(
41091 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41092 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41094 // this.tabTpl = new Roo.Template(
41095 // '<a href="#">' +
41096 // '<span unselectable="on"' +
41097 // (this.disableTooltips ? '' : ' title="{text}"') +
41098 // ' >{text}</span></a>'
41104 var template = tpl || this.tabTpl || false;
41107 template = new Roo.Template(
41108 Roo.bootstrap.version == 4 ?
41110 '<a class="nav-link" href="#" unselectable="on"' +
41111 (this.disableTooltips ? '' : ' title="{text}"') +
41114 '<a class="nav-link" href="#">' +
41115 '<span unselectable="on"' +
41116 (this.disableTooltips ? '' : ' title="{text}"') +
41117 ' >{text}</span></a>'
41122 switch (typeof(template)) {
41126 template = new Roo.Template(template);
41132 var el = template.overwrite(td, {"text": text});
41134 var inner = el.getElementsByTagName("span")[0];
41136 return {"el": el, "inner": inner};
41144 * @class Roo.TabPanelItem
41145 * @extends Roo.util.Observable
41146 * Represents an individual item (tab plus body) in a TabPanel.
41147 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41148 * @param {String} id The id of this TabPanelItem
41149 * @param {String} text The text for the tab of this TabPanelItem
41150 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41152 Roo.bootstrap.panel.TabItem = function(config){
41154 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41155 * @type Roo.TabPanel
41157 this.tabPanel = config.panel;
41159 * The id for this TabPanelItem
41162 this.id = config.id;
41164 this.disabled = false;
41166 this.text = config.text;
41168 this.loaded = false;
41169 this.closable = config.closable;
41172 * The body element for this TabPanelItem.
41173 * @type Roo.Element
41175 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41176 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41177 this.bodyEl.setStyle("display", "block");
41178 this.bodyEl.setStyle("zoom", "1");
41179 //this.hideAction();
41181 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41183 this.el = Roo.get(els.el);
41184 this.inner = Roo.get(els.inner, true);
41185 this.textEl = Roo.bootstrap.version == 4 ?
41186 this.el : Roo.get(this.el.dom.firstChild, true);
41188 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41189 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41192 // this.el.on("mousedown", this.onTabMouseDown, this);
41193 this.el.on("click", this.onTabClick, this);
41195 if(config.closable){
41196 var c = Roo.get(els.close, true);
41197 c.dom.title = this.closeText;
41198 c.addClassOnOver("close-over");
41199 c.on("click", this.closeClick, this);
41205 * Fires when this tab becomes the active tab.
41206 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41207 * @param {Roo.TabPanelItem} this
41211 * @event beforeclose
41212 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41213 * @param {Roo.TabPanelItem} this
41214 * @param {Object} e Set cancel to true on this object to cancel the close.
41216 "beforeclose": true,
41219 * Fires when this tab is closed.
41220 * @param {Roo.TabPanelItem} this
41224 * @event deactivate
41225 * Fires when this tab is no longer the active tab.
41226 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41227 * @param {Roo.TabPanelItem} this
41229 "deactivate" : true
41231 this.hidden = false;
41233 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41236 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41238 purgeListeners : function(){
41239 Roo.util.Observable.prototype.purgeListeners.call(this);
41240 this.el.removeAllListeners();
41243 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41246 this.status_node.addClass("active");
41249 this.tabPanel.stripWrap.repaint();
41251 this.fireEvent("activate", this.tabPanel, this);
41255 * Returns true if this tab is the active tab.
41256 * @return {Boolean}
41258 isActive : function(){
41259 return this.tabPanel.getActiveTab() == this;
41263 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41266 this.status_node.removeClass("active");
41268 this.fireEvent("deactivate", this.tabPanel, this);
41271 hideAction : function(){
41272 this.bodyEl.hide();
41273 this.bodyEl.setStyle("position", "absolute");
41274 this.bodyEl.setLeft("-20000px");
41275 this.bodyEl.setTop("-20000px");
41278 showAction : function(){
41279 this.bodyEl.setStyle("position", "relative");
41280 this.bodyEl.setTop("");
41281 this.bodyEl.setLeft("");
41282 this.bodyEl.show();
41286 * Set the tooltip for the tab.
41287 * @param {String} tooltip The tab's tooltip
41289 setTooltip : function(text){
41290 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41291 this.textEl.dom.qtip = text;
41292 this.textEl.dom.removeAttribute('title');
41294 this.textEl.dom.title = text;
41298 onTabClick : function(e){
41299 e.preventDefault();
41300 this.tabPanel.activate(this.id);
41303 onTabMouseDown : function(e){
41304 e.preventDefault();
41305 this.tabPanel.activate(this.id);
41308 getWidth : function(){
41309 return this.inner.getWidth();
41312 setWidth : function(width){
41313 var iwidth = width - this.linode.getPadding("lr");
41314 this.inner.setWidth(iwidth);
41315 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41316 this.linode.setWidth(width);
41320 * Show or hide the tab
41321 * @param {Boolean} hidden True to hide or false to show.
41323 setHidden : function(hidden){
41324 this.hidden = hidden;
41325 this.linode.setStyle("display", hidden ? "none" : "");
41329 * Returns true if this tab is "hidden"
41330 * @return {Boolean}
41332 isHidden : function(){
41333 return this.hidden;
41337 * Returns the text for this tab
41340 getText : function(){
41344 autoSize : function(){
41345 //this.el.beginMeasure();
41346 this.textEl.setWidth(1);
41348 * #2804 [new] Tabs in Roojs
41349 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41351 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41352 //this.el.endMeasure();
41356 * Sets the text for the tab (Note: this also sets the tooltip text)
41357 * @param {String} text The tab's text and tooltip
41359 setText : function(text){
41361 this.textEl.update(text);
41362 this.setTooltip(text);
41363 //if(!this.tabPanel.resizeTabs){
41364 // this.autoSize();
41368 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41370 activate : function(){
41371 this.tabPanel.activate(this.id);
41375 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41377 disable : function(){
41378 if(this.tabPanel.active != this){
41379 this.disabled = true;
41380 this.status_node.addClass("disabled");
41385 * Enables this TabPanelItem if it was previously disabled.
41387 enable : function(){
41388 this.disabled = false;
41389 this.status_node.removeClass("disabled");
41393 * Sets the content for this TabPanelItem.
41394 * @param {String} content The content
41395 * @param {Boolean} loadScripts true to look for and load scripts
41397 setContent : function(content, loadScripts){
41398 this.bodyEl.update(content, loadScripts);
41402 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41403 * @return {Roo.UpdateManager} The UpdateManager
41405 getUpdateManager : function(){
41406 return this.bodyEl.getUpdateManager();
41410 * Set a URL to be used to load the content for this TabPanelItem.
41411 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41412 * @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)
41413 * @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)
41414 * @return {Roo.UpdateManager} The UpdateManager
41416 setUrl : function(url, params, loadOnce){
41417 if(this.refreshDelegate){
41418 this.un('activate', this.refreshDelegate);
41420 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41421 this.on("activate", this.refreshDelegate);
41422 return this.bodyEl.getUpdateManager();
41426 _handleRefresh : function(url, params, loadOnce){
41427 if(!loadOnce || !this.loaded){
41428 var updater = this.bodyEl.getUpdateManager();
41429 updater.update(url, params, this._setLoaded.createDelegate(this));
41434 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41435 * Will fail silently if the setUrl method has not been called.
41436 * This does not activate the panel, just updates its content.
41438 refresh : function(){
41439 if(this.refreshDelegate){
41440 this.loaded = false;
41441 this.refreshDelegate();
41446 _setLoaded : function(){
41447 this.loaded = true;
41451 closeClick : function(e){
41454 this.fireEvent("beforeclose", this, o);
41455 if(o.cancel !== true){
41456 this.tabPanel.removeTab(this.id);
41460 * The text displayed in the tooltip for the close icon.
41463 closeText : "Close this tab"
41466 * This script refer to:
41467 * Title: International Telephone Input
41468 * Author: Jack O'Connor
41469 * Code version: v12.1.12
41470 * Availability: https://github.com/jackocnr/intl-tel-input.git
41473 Roo.bootstrap.PhoneInputData = function() {
41476 "Afghanistan (افغانستان)",
41481 "Albania (Shqipëri)",
41486 "Algeria (الجزائر)",
41511 "Antigua and Barbuda",
41521 "Armenia (Հայաստան)",
41537 "Austria (Österreich)",
41542 "Azerbaijan (Azərbaycan)",
41552 "Bahrain (البحرين)",
41557 "Bangladesh (বাংলাদেশ)",
41567 "Belarus (Беларусь)",
41572 "Belgium (België)",
41602 "Bosnia and Herzegovina (Босна и Херцеговина)",
41617 "British Indian Ocean Territory",
41622 "British Virgin Islands",
41632 "Bulgaria (България)",
41642 "Burundi (Uburundi)",
41647 "Cambodia (កម្ពុជា)",
41652 "Cameroon (Cameroun)",
41661 ["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"]
41664 "Cape Verde (Kabu Verdi)",
41669 "Caribbean Netherlands",
41680 "Central African Republic (République centrafricaine)",
41700 "Christmas Island",
41706 "Cocos (Keeling) Islands",
41717 "Comoros (جزر القمر)",
41722 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41727 "Congo (Republic) (Congo-Brazzaville)",
41747 "Croatia (Hrvatska)",
41768 "Czech Republic (Česká republika)",
41773 "Denmark (Danmark)",
41788 "Dominican Republic (República Dominicana)",
41792 ["809", "829", "849"]
41810 "Equatorial Guinea (Guinea Ecuatorial)",
41830 "Falkland Islands (Islas Malvinas)",
41835 "Faroe Islands (Føroyar)",
41856 "French Guiana (Guyane française)",
41861 "French Polynesia (Polynésie française)",
41876 "Georgia (საქართველო)",
41881 "Germany (Deutschland)",
41901 "Greenland (Kalaallit Nunaat)",
41938 "Guinea-Bissau (Guiné Bissau)",
41963 "Hungary (Magyarország)",
41968 "Iceland (Ísland)",
41988 "Iraq (العراق)",
42004 "Israel (ישראל)",
42031 "Jordan (الأردن)",
42036 "Kazakhstan (Казахстан)",
42057 "Kuwait (الكويت)",
42062 "Kyrgyzstan (Кыргызстан)",
42072 "Latvia (Latvija)",
42077 "Lebanon (لبنان)",
42092 "Libya (ليبيا)",
42102 "Lithuania (Lietuva)",
42117 "Macedonia (FYROM) (Македонија)",
42122 "Madagascar (Madagasikara)",
42152 "Marshall Islands",
42162 "Mauritania (موريتانيا)",
42167 "Mauritius (Moris)",
42188 "Moldova (Republica Moldova)",
42198 "Mongolia (Монгол)",
42203 "Montenegro (Crna Gora)",
42213 "Morocco (المغرب)",
42219 "Mozambique (Moçambique)",
42224 "Myanmar (Burma) (မြန်မာ)",
42229 "Namibia (Namibië)",
42244 "Netherlands (Nederland)",
42249 "New Caledonia (Nouvelle-Calédonie)",
42284 "North Korea (조선 민주주의 인민 공화국)",
42289 "Northern Mariana Islands",
42305 "Pakistan (پاکستان)",
42315 "Palestine (فلسطين)",
42325 "Papua New Guinea",
42367 "Réunion (La Réunion)",
42373 "Romania (România)",
42389 "Saint Barthélemy",
42400 "Saint Kitts and Nevis",
42410 "Saint Martin (Saint-Martin (partie française))",
42416 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42421 "Saint Vincent and the Grenadines",
42436 "São Tomé and Príncipe (São Tomé e Príncipe)",
42441 "Saudi Arabia (المملكة العربية السعودية)",
42446 "Senegal (Sénégal)",
42476 "Slovakia (Slovensko)",
42481 "Slovenia (Slovenija)",
42491 "Somalia (Soomaaliya)",
42501 "South Korea (대한민국)",
42506 "South Sudan (جنوب السودان)",
42516 "Sri Lanka (ශ්රී ලංකාව)",
42521 "Sudan (السودان)",
42531 "Svalbard and Jan Mayen",
42542 "Sweden (Sverige)",
42547 "Switzerland (Schweiz)",
42552 "Syria (سوريا)",
42597 "Trinidad and Tobago",
42602 "Tunisia (تونس)",
42607 "Turkey (Türkiye)",
42617 "Turks and Caicos Islands",
42627 "U.S. Virgin Islands",
42637 "Ukraine (Україна)",
42642 "United Arab Emirates (الإمارات العربية المتحدة)",
42664 "Uzbekistan (Oʻzbekiston)",
42674 "Vatican City (Città del Vaticano)",
42685 "Vietnam (Việt Nam)",
42690 "Wallis and Futuna (Wallis-et-Futuna)",
42695 "Western Sahara (الصحراء الغربية)",
42701 "Yemen (اليمن)",
42725 * This script refer to:
42726 * Title: International Telephone Input
42727 * Author: Jack O'Connor
42728 * Code version: v12.1.12
42729 * Availability: https://github.com/jackocnr/intl-tel-input.git
42733 * @class Roo.bootstrap.PhoneInput
42734 * @extends Roo.bootstrap.TriggerField
42735 * An input with International dial-code selection
42737 * @cfg {String} defaultDialCode default '+852'
42738 * @cfg {Array} preferedCountries default []
42741 * Create a new PhoneInput.
42742 * @param {Object} config Configuration options
42745 Roo.bootstrap.PhoneInput = function(config) {
42746 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42749 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42751 listWidth: undefined,
42753 selectedClass: 'active',
42755 invalidClass : "has-warning",
42757 validClass: 'has-success',
42759 allowed: '0123456789',
42764 * @cfg {String} defaultDialCode The default dial code when initializing the input
42766 defaultDialCode: '+852',
42769 * @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
42771 preferedCountries: false,
42773 getAutoCreate : function()
42775 var data = Roo.bootstrap.PhoneInputData();
42776 var align = this.labelAlign || this.parentLabelAlign();
42779 this.allCountries = [];
42780 this.dialCodeMapping = [];
42782 for (var i = 0; i < data.length; i++) {
42784 this.allCountries[i] = {
42788 priority: c[3] || 0,
42789 areaCodes: c[4] || null
42791 this.dialCodeMapping[c[2]] = {
42794 priority: c[3] || 0,
42795 areaCodes: c[4] || null
42807 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42808 maxlength: this.max_length,
42809 cls : 'form-control tel-input',
42810 autocomplete: 'new-password'
42813 var hiddenInput = {
42816 cls: 'hidden-tel-input'
42820 hiddenInput.name = this.name;
42823 if (this.disabled) {
42824 input.disabled = true;
42827 var flag_container = {
42844 cls: this.hasFeedback ? 'has-feedback' : '',
42850 cls: 'dial-code-holder',
42857 cls: 'roo-select2-container input-group',
42864 if (this.fieldLabel.length) {
42867 tooltip: 'This field is required'
42873 cls: 'control-label',
42879 html: this.fieldLabel
42882 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42888 if(this.indicatorpos == 'right') {
42889 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42896 if(align == 'left') {
42904 if(this.labelWidth > 12){
42905 label.style = "width: " + this.labelWidth + 'px';
42907 if(this.labelWidth < 13 && this.labelmd == 0){
42908 this.labelmd = this.labelWidth;
42910 if(this.labellg > 0){
42911 label.cls += ' col-lg-' + this.labellg;
42912 input.cls += ' col-lg-' + (12 - this.labellg);
42914 if(this.labelmd > 0){
42915 label.cls += ' col-md-' + this.labelmd;
42916 container.cls += ' col-md-' + (12 - this.labelmd);
42918 if(this.labelsm > 0){
42919 label.cls += ' col-sm-' + this.labelsm;
42920 container.cls += ' col-sm-' + (12 - this.labelsm);
42922 if(this.labelxs > 0){
42923 label.cls += ' col-xs-' + this.labelxs;
42924 container.cls += ' col-xs-' + (12 - this.labelxs);
42934 var settings = this;
42936 ['xs','sm','md','lg'].map(function(size){
42937 if (settings[size]) {
42938 cfg.cls += ' col-' + size + '-' + settings[size];
42942 this.store = new Roo.data.Store({
42943 proxy : new Roo.data.MemoryProxy({}),
42944 reader : new Roo.data.JsonReader({
42955 'name' : 'dialCode',
42959 'name' : 'priority',
42963 'name' : 'areaCodes',
42970 if(!this.preferedCountries) {
42971 this.preferedCountries = [
42978 var p = this.preferedCountries.reverse();
42981 for (var i = 0; i < p.length; i++) {
42982 for (var j = 0; j < this.allCountries.length; j++) {
42983 if(this.allCountries[j].iso2 == p[i]) {
42984 var t = this.allCountries[j];
42985 this.allCountries.splice(j,1);
42986 this.allCountries.unshift(t);
42992 this.store.proxy.data = {
42994 data: this.allCountries
43000 initEvents : function()
43003 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43005 this.indicator = this.indicatorEl();
43006 this.flag = this.flagEl();
43007 this.dialCodeHolder = this.dialCodeHolderEl();
43009 this.trigger = this.el.select('div.flag-box',true).first();
43010 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43015 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43016 _this.list.setWidth(lw);
43019 this.list.on('mouseover', this.onViewOver, this);
43020 this.list.on('mousemove', this.onViewMove, this);
43021 this.inputEl().on("keyup", this.onKeyUp, this);
43022 this.inputEl().on("keypress", this.onKeyPress, this);
43024 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43026 this.view = new Roo.View(this.list, this.tpl, {
43027 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43030 this.view.on('click', this.onViewClick, this);
43031 this.setValue(this.defaultDialCode);
43034 onTriggerClick : function(e)
43036 Roo.log('trigger click');
43041 if(this.isExpanded()){
43043 this.hasFocus = false;
43045 this.store.load({});
43046 this.hasFocus = true;
43051 isExpanded : function()
43053 return this.list.isVisible();
43056 collapse : function()
43058 if(!this.isExpanded()){
43062 Roo.get(document).un('mousedown', this.collapseIf, this);
43063 Roo.get(document).un('mousewheel', this.collapseIf, this);
43064 this.fireEvent('collapse', this);
43068 expand : function()
43072 if(this.isExpanded() || !this.hasFocus){
43076 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43077 this.list.setWidth(lw);
43080 this.restrictHeight();
43082 Roo.get(document).on('mousedown', this.collapseIf, this);
43083 Roo.get(document).on('mousewheel', this.collapseIf, this);
43085 this.fireEvent('expand', this);
43088 restrictHeight : function()
43090 this.list.alignTo(this.inputEl(), this.listAlign);
43091 this.list.alignTo(this.inputEl(), this.listAlign);
43094 onViewOver : function(e, t)
43096 if(this.inKeyMode){
43099 var item = this.view.findItemFromChild(t);
43102 var index = this.view.indexOf(item);
43103 this.select(index, false);
43108 onViewClick : function(view, doFocus, el, e)
43110 var index = this.view.getSelectedIndexes()[0];
43112 var r = this.store.getAt(index);
43115 this.onSelect(r, index);
43117 if(doFocus !== false && !this.blockFocus){
43118 this.inputEl().focus();
43122 onViewMove : function(e, t)
43124 this.inKeyMode = false;
43127 select : function(index, scrollIntoView)
43129 this.selectedIndex = index;
43130 this.view.select(index);
43131 if(scrollIntoView !== false){
43132 var el = this.view.getNode(index);
43134 this.list.scrollChildIntoView(el, false);
43139 createList : function()
43141 this.list = Roo.get(document.body).createChild({
43143 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43144 style: 'display:none'
43147 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43150 collapseIf : function(e)
43152 var in_combo = e.within(this.el);
43153 var in_list = e.within(this.list);
43154 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43156 if (in_combo || in_list || is_list) {
43162 onSelect : function(record, index)
43164 if(this.fireEvent('beforeselect', this, record, index) !== false){
43166 this.setFlagClass(record.data.iso2);
43167 this.setDialCode(record.data.dialCode);
43168 this.hasFocus = false;
43170 this.fireEvent('select', this, record, index);
43174 flagEl : function()
43176 var flag = this.el.select('div.flag',true).first();
43183 dialCodeHolderEl : function()
43185 var d = this.el.select('input.dial-code-holder',true).first();
43192 setDialCode : function(v)
43194 this.dialCodeHolder.dom.value = '+'+v;
43197 setFlagClass : function(n)
43199 this.flag.dom.className = 'flag '+n;
43202 getValue : function()
43204 var v = this.inputEl().getValue();
43205 if(this.dialCodeHolder) {
43206 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43211 setValue : function(v)
43213 var d = this.getDialCode(v);
43215 //invalid dial code
43216 if(v.length == 0 || !d || d.length == 0) {
43218 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43219 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43225 this.setFlagClass(this.dialCodeMapping[d].iso2);
43226 this.setDialCode(d);
43227 this.inputEl().dom.value = v.replace('+'+d,'');
43228 this.hiddenEl().dom.value = this.getValue();
43233 getDialCode : function(v)
43237 if (v.length == 0) {
43238 return this.dialCodeHolder.dom.value;
43242 if (v.charAt(0) != "+") {
43245 var numericChars = "";
43246 for (var i = 1; i < v.length; i++) {
43247 var c = v.charAt(i);
43250 if (this.dialCodeMapping[numericChars]) {
43251 dialCode = v.substr(1, i);
43253 if (numericChars.length == 4) {
43263 this.setValue(this.defaultDialCode);
43267 hiddenEl : function()
43269 return this.el.select('input.hidden-tel-input',true).first();
43272 // after setting val
43273 onKeyUp : function(e){
43274 this.setValue(this.getValue());
43277 onKeyPress : function(e){
43278 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43285 * @class Roo.bootstrap.MoneyField
43286 * @extends Roo.bootstrap.ComboBox
43287 * Bootstrap MoneyField class
43290 * Create a new MoneyField.
43291 * @param {Object} config Configuration options
43294 Roo.bootstrap.MoneyField = function(config) {
43296 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43300 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43303 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43305 allowDecimals : true,
43307 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43309 decimalSeparator : ".",
43311 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43313 decimalPrecision : 0,
43315 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43317 allowNegative : true,
43319 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43323 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43325 minValue : Number.NEGATIVE_INFINITY,
43327 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43329 maxValue : Number.MAX_VALUE,
43331 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43333 minText : "The minimum value for this field is {0}",
43335 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43337 maxText : "The maximum value for this field is {0}",
43339 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43340 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43342 nanText : "{0} is not a valid number",
43344 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43348 * @cfg {String} defaults currency of the MoneyField
43349 * value should be in lkey
43351 defaultCurrency : false,
43353 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43355 thousandsDelimiter : false,
43357 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43368 getAutoCreate : function()
43370 var align = this.labelAlign || this.parentLabelAlign();
43382 cls : 'form-control roo-money-amount-input',
43383 autocomplete: 'new-password'
43386 var hiddenInput = {
43390 cls: 'hidden-number-input'
43393 if(this.max_length) {
43394 input.maxlength = this.max_length;
43398 hiddenInput.name = this.name;
43401 if (this.disabled) {
43402 input.disabled = true;
43405 var clg = 12 - this.inputlg;
43406 var cmd = 12 - this.inputmd;
43407 var csm = 12 - this.inputsm;
43408 var cxs = 12 - this.inputxs;
43412 cls : 'row roo-money-field',
43416 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43420 cls: 'roo-select2-container input-group',
43424 cls : 'form-control roo-money-currency-input',
43425 autocomplete: 'new-password',
43427 name : this.currencyName
43431 cls : 'input-group-addon',
43445 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43449 cls: this.hasFeedback ? 'has-feedback' : '',
43460 if (this.fieldLabel.length) {
43463 tooltip: 'This field is required'
43469 cls: 'control-label',
43475 html: this.fieldLabel
43478 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43484 if(this.indicatorpos == 'right') {
43485 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43492 if(align == 'left') {
43500 if(this.labelWidth > 12){
43501 label.style = "width: " + this.labelWidth + 'px';
43503 if(this.labelWidth < 13 && this.labelmd == 0){
43504 this.labelmd = this.labelWidth;
43506 if(this.labellg > 0){
43507 label.cls += ' col-lg-' + this.labellg;
43508 input.cls += ' col-lg-' + (12 - this.labellg);
43510 if(this.labelmd > 0){
43511 label.cls += ' col-md-' + this.labelmd;
43512 container.cls += ' col-md-' + (12 - this.labelmd);
43514 if(this.labelsm > 0){
43515 label.cls += ' col-sm-' + this.labelsm;
43516 container.cls += ' col-sm-' + (12 - this.labelsm);
43518 if(this.labelxs > 0){
43519 label.cls += ' col-xs-' + this.labelxs;
43520 container.cls += ' col-xs-' + (12 - this.labelxs);
43531 var settings = this;
43533 ['xs','sm','md','lg'].map(function(size){
43534 if (settings[size]) {
43535 cfg.cls += ' col-' + size + '-' + settings[size];
43542 initEvents : function()
43544 this.indicator = this.indicatorEl();
43546 this.initCurrencyEvent();
43548 this.initNumberEvent();
43551 initCurrencyEvent : function()
43554 throw "can not find store for combo";
43557 this.store = Roo.factory(this.store, Roo.data);
43558 this.store.parent = this;
43562 this.triggerEl = this.el.select('.input-group-addon', true).first();
43564 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43569 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43570 _this.list.setWidth(lw);
43573 this.list.on('mouseover', this.onViewOver, this);
43574 this.list.on('mousemove', this.onViewMove, this);
43575 this.list.on('scroll', this.onViewScroll, this);
43578 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43581 this.view = new Roo.View(this.list, this.tpl, {
43582 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43585 this.view.on('click', this.onViewClick, this);
43587 this.store.on('beforeload', this.onBeforeLoad, this);
43588 this.store.on('load', this.onLoad, this);
43589 this.store.on('loadexception', this.onLoadException, this);
43591 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43592 "up" : function(e){
43593 this.inKeyMode = true;
43597 "down" : function(e){
43598 if(!this.isExpanded()){
43599 this.onTriggerClick();
43601 this.inKeyMode = true;
43606 "enter" : function(e){
43609 if(this.fireEvent("specialkey", this, e)){
43610 this.onViewClick(false);
43616 "esc" : function(e){
43620 "tab" : function(e){
43623 if(this.fireEvent("specialkey", this, e)){
43624 this.onViewClick(false);
43632 doRelay : function(foo, bar, hname){
43633 if(hname == 'down' || this.scope.isExpanded()){
43634 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43642 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43646 initNumberEvent : function(e)
43648 this.inputEl().on("keydown" , this.fireKey, this);
43649 this.inputEl().on("focus", this.onFocus, this);
43650 this.inputEl().on("blur", this.onBlur, this);
43652 this.inputEl().relayEvent('keyup', this);
43654 if(this.indicator){
43655 this.indicator.addClass('invisible');
43658 this.originalValue = this.getValue();
43660 if(this.validationEvent == 'keyup'){
43661 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43662 this.inputEl().on('keyup', this.filterValidation, this);
43664 else if(this.validationEvent !== false){
43665 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43668 if(this.selectOnFocus){
43669 this.on("focus", this.preFocus, this);
43672 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43673 this.inputEl().on("keypress", this.filterKeys, this);
43675 this.inputEl().relayEvent('keypress', this);
43678 var allowed = "0123456789";
43680 if(this.allowDecimals){
43681 allowed += this.decimalSeparator;
43684 if(this.allowNegative){
43688 if(this.thousandsDelimiter) {
43692 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43694 var keyPress = function(e){
43696 var k = e.getKey();
43698 var c = e.getCharCode();
43701 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43702 allowed.indexOf(String.fromCharCode(c)) === -1
43708 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43712 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43717 this.inputEl().on("keypress", keyPress, this);
43721 onTriggerClick : function(e)
43728 this.loadNext = false;
43730 if(this.isExpanded()){
43735 this.hasFocus = true;
43737 if(this.triggerAction == 'all') {
43738 this.doQuery(this.allQuery, true);
43742 this.doQuery(this.getRawValue());
43745 getCurrency : function()
43747 var v = this.currencyEl().getValue();
43752 restrictHeight : function()
43754 this.list.alignTo(this.currencyEl(), this.listAlign);
43755 this.list.alignTo(this.currencyEl(), this.listAlign);
43758 onViewClick : function(view, doFocus, el, e)
43760 var index = this.view.getSelectedIndexes()[0];
43762 var r = this.store.getAt(index);
43765 this.onSelect(r, index);
43769 onSelect : function(record, index){
43771 if(this.fireEvent('beforeselect', this, record, index) !== false){
43773 this.setFromCurrencyData(index > -1 ? record.data : false);
43777 this.fireEvent('select', this, record, index);
43781 setFromCurrencyData : function(o)
43785 this.lastCurrency = o;
43787 if (this.currencyField) {
43788 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43790 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43793 this.lastSelectionText = currency;
43795 //setting default currency
43796 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43797 this.setCurrency(this.defaultCurrency);
43801 this.setCurrency(currency);
43804 setFromData : function(o)
43808 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43810 this.setFromCurrencyData(c);
43815 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43817 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43820 this.setValue(value);
43824 setCurrency : function(v)
43826 this.currencyValue = v;
43829 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43834 setValue : function(v)
43836 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43842 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43844 this.inputEl().dom.value = (v == '') ? '' :
43845 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43847 if(!this.allowZero && v === '0') {
43848 this.hiddenEl().dom.value = '';
43849 this.inputEl().dom.value = '';
43856 getRawValue : function()
43858 var v = this.inputEl().getValue();
43863 getValue : function()
43865 return this.fixPrecision(this.parseValue(this.getRawValue()));
43868 parseValue : function(value)
43870 if(this.thousandsDelimiter) {
43872 r = new RegExp(",", "g");
43873 value = value.replace(r, "");
43876 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43877 return isNaN(value) ? '' : value;
43881 fixPrecision : function(value)
43883 if(this.thousandsDelimiter) {
43885 r = new RegExp(",", "g");
43886 value = value.replace(r, "");
43889 var nan = isNaN(value);
43891 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43892 return nan ? '' : value;
43894 return parseFloat(value).toFixed(this.decimalPrecision);
43897 decimalPrecisionFcn : function(v)
43899 return Math.floor(v);
43902 validateValue : function(value)
43904 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43908 var num = this.parseValue(value);
43911 this.markInvalid(String.format(this.nanText, value));
43915 if(num < this.minValue){
43916 this.markInvalid(String.format(this.minText, this.minValue));
43920 if(num > this.maxValue){
43921 this.markInvalid(String.format(this.maxText, this.maxValue));
43928 validate : function()
43930 if(this.disabled || this.allowBlank){
43935 var currency = this.getCurrency();
43937 if(this.validateValue(this.getRawValue()) && currency.length){
43942 this.markInvalid();
43946 getName: function()
43951 beforeBlur : function()
43957 var v = this.parseValue(this.getRawValue());
43964 onBlur : function()
43968 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43969 //this.el.removeClass(this.focusClass);
43972 this.hasFocus = false;
43974 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43978 var v = this.getValue();
43980 if(String(v) !== String(this.startValue)){
43981 this.fireEvent('change', this, v, this.startValue);
43984 this.fireEvent("blur", this);
43987 inputEl : function()
43989 return this.el.select('.roo-money-amount-input', true).first();
43992 currencyEl : function()
43994 return this.el.select('.roo-money-currency-input', true).first();
43997 hiddenEl : function()
43999 return this.el.select('input.hidden-number-input',true).first();
44003 * @class Roo.bootstrap.BezierSignature
44004 * @extends Roo.bootstrap.Component
44005 * Bootstrap BezierSignature class
44006 * This script refer to:
44007 * Title: Signature Pad
44009 * Availability: https://github.com/szimek/signature_pad
44012 * Create a new BezierSignature
44013 * @param {Object} config The config object
44016 Roo.bootstrap.BezierSignature = function(config){
44017 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44023 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44030 mouse_btn_down: true,
44033 * @cfg {int} canvas height
44035 canvas_height: '200px',
44038 * @cfg {float|function} Radius of a single dot.
44043 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44048 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44053 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44058 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44063 * @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.
44065 bg_color: 'rgba(0, 0, 0, 0)',
44068 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44070 dot_color: 'black',
44073 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44075 velocity_filter_weight: 0.7,
44078 * @cfg {function} Callback when stroke begin.
44083 * @cfg {function} Callback when stroke end.
44087 getAutoCreate : function()
44089 var cls = 'roo-signature column';
44092 cls += ' ' + this.cls;
44102 for(var i = 0; i < col_sizes.length; i++) {
44103 if(this[col_sizes[i]]) {
44104 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44114 cls: 'roo-signature-body',
44118 cls: 'roo-signature-body-canvas',
44119 height: this.canvas_height,
44120 width: this.canvas_width
44127 style: 'display: none'
44135 initEvents: function()
44137 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44139 var canvas = this.canvasEl();
44141 // mouse && touch event swapping...
44142 canvas.dom.style.touchAction = 'none';
44143 canvas.dom.style.msTouchAction = 'none';
44145 this.mouse_btn_down = false;
44146 canvas.on('mousedown', this._handleMouseDown, this);
44147 canvas.on('mousemove', this._handleMouseMove, this);
44148 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44150 if (window.PointerEvent) {
44151 canvas.on('pointerdown', this._handleMouseDown, this);
44152 canvas.on('pointermove', this._handleMouseMove, this);
44153 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44156 if ('ontouchstart' in window) {
44157 canvas.on('touchstart', this._handleTouchStart, this);
44158 canvas.on('touchmove', this._handleTouchMove, this);
44159 canvas.on('touchend', this._handleTouchEnd, this);
44162 Roo.EventManager.onWindowResize(this.resize, this, true);
44164 // file input event
44165 this.fileEl().on('change', this.uploadImage, this);
44172 resize: function(){
44174 var canvas = this.canvasEl().dom;
44175 var ctx = this.canvasElCtx();
44176 var img_data = false;
44178 if(canvas.width > 0) {
44179 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44181 // setting canvas width will clean img data
44184 var style = window.getComputedStyle ?
44185 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44187 var padding_left = parseInt(style.paddingLeft) || 0;
44188 var padding_right = parseInt(style.paddingRight) || 0;
44190 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44193 ctx.putImageData(img_data, 0, 0);
44197 _handleMouseDown: function(e)
44199 if (e.browserEvent.which === 1) {
44200 this.mouse_btn_down = true;
44201 this.strokeBegin(e);
44205 _handleMouseMove: function (e)
44207 if (this.mouse_btn_down) {
44208 this.strokeMoveUpdate(e);
44212 _handleMouseUp: function (e)
44214 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44215 this.mouse_btn_down = false;
44220 _handleTouchStart: function (e) {
44222 e.preventDefault();
44223 if (e.browserEvent.targetTouches.length === 1) {
44224 // var touch = e.browserEvent.changedTouches[0];
44225 // this.strokeBegin(touch);
44227 this.strokeBegin(e); // assume e catching the correct xy...
44231 _handleTouchMove: function (e) {
44232 e.preventDefault();
44233 // var touch = event.targetTouches[0];
44234 // _this._strokeMoveUpdate(touch);
44235 this.strokeMoveUpdate(e);
44238 _handleTouchEnd: function (e) {
44239 var wasCanvasTouched = e.target === this.canvasEl().dom;
44240 if (wasCanvasTouched) {
44241 e.preventDefault();
44242 // var touch = event.changedTouches[0];
44243 // _this._strokeEnd(touch);
44248 reset: function () {
44249 this._lastPoints = [];
44250 this._lastVelocity = 0;
44251 this._lastWidth = (this.min_width + this.max_width) / 2;
44252 this.canvasElCtx().fillStyle = this.dot_color;
44255 strokeMoveUpdate: function(e)
44257 this.strokeUpdate(e);
44259 if (this.throttle) {
44260 this.throttleStroke(this.strokeUpdate, this.throttle);
44263 this.strokeUpdate(e);
44267 strokeBegin: function(e)
44269 var newPointGroup = {
44270 color: this.dot_color,
44274 if (typeof this.onBegin === 'function') {
44278 this.curve_data.push(newPointGroup);
44280 this.strokeUpdate(e);
44283 strokeUpdate: function(e)
44285 var rect = this.canvasEl().dom.getBoundingClientRect();
44286 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44287 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44288 var lastPoints = lastPointGroup.points;
44289 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44290 var isLastPointTooClose = lastPoint
44291 ? point.distanceTo(lastPoint) <= this.min_distance
44293 var color = lastPointGroup.color;
44294 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44295 var curve = this.addPoint(point);
44297 this.drawDot({color: color, point: point});
44300 this.drawCurve({color: color, curve: curve});
44310 strokeEnd: function(e)
44312 this.strokeUpdate(e);
44313 if (typeof this.onEnd === 'function') {
44318 addPoint: function (point) {
44319 var _lastPoints = this._lastPoints;
44320 _lastPoints.push(point);
44321 if (_lastPoints.length > 2) {
44322 if (_lastPoints.length === 3) {
44323 _lastPoints.unshift(_lastPoints[0]);
44325 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44326 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44327 _lastPoints.shift();
44333 calculateCurveWidths: function (startPoint, endPoint) {
44334 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44335 (1 - this.velocity_filter_weight) * this._lastVelocity;
44337 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44340 start: this._lastWidth
44343 this._lastVelocity = velocity;
44344 this._lastWidth = newWidth;
44348 drawDot: function (_a) {
44349 var color = _a.color, point = _a.point;
44350 var ctx = this.canvasElCtx();
44351 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44353 this.drawCurveSegment(point.x, point.y, width);
44355 ctx.fillStyle = color;
44359 drawCurve: function (_a) {
44360 var color = _a.color, curve = _a.curve;
44361 var ctx = this.canvasElCtx();
44362 var widthDelta = curve.endWidth - curve.startWidth;
44363 var drawSteps = Math.floor(curve.length()) * 2;
44365 ctx.fillStyle = color;
44366 for (var i = 0; i < drawSteps; i += 1) {
44367 var t = i / drawSteps;
44373 var x = uuu * curve.startPoint.x;
44374 x += 3 * uu * t * curve.control1.x;
44375 x += 3 * u * tt * curve.control2.x;
44376 x += ttt * curve.endPoint.x;
44377 var y = uuu * curve.startPoint.y;
44378 y += 3 * uu * t * curve.control1.y;
44379 y += 3 * u * tt * curve.control2.y;
44380 y += ttt * curve.endPoint.y;
44381 var width = curve.startWidth + ttt * widthDelta;
44382 this.drawCurveSegment(x, y, width);
44388 drawCurveSegment: function (x, y, width) {
44389 var ctx = this.canvasElCtx();
44391 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44392 this.is_empty = false;
44397 var ctx = this.canvasElCtx();
44398 var canvas = this.canvasEl().dom;
44399 ctx.fillStyle = this.bg_color;
44400 ctx.clearRect(0, 0, canvas.width, canvas.height);
44401 ctx.fillRect(0, 0, canvas.width, canvas.height);
44402 this.curve_data = [];
44404 this.is_empty = true;
44409 return this.el.select('input',true).first();
44412 canvasEl: function()
44414 return this.el.select('canvas',true).first();
44417 canvasElCtx: function()
44419 return this.el.select('canvas',true).first().dom.getContext('2d');
44422 getImage: function(type)
44424 if(this.is_empty) {
44429 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44432 drawFromImage: function(img_src)
44434 var img = new Image();
44436 img.onload = function(){
44437 this.canvasElCtx().drawImage(img, 0, 0);
44442 this.is_empty = false;
44445 selectImage: function()
44447 this.fileEl().dom.click();
44450 uploadImage: function(e)
44452 var reader = new FileReader();
44454 reader.onload = function(e){
44455 var img = new Image();
44456 img.onload = function(){
44458 this.canvasElCtx().drawImage(img, 0, 0);
44460 img.src = e.target.result;
44463 reader.readAsDataURL(e.target.files[0]);
44466 // Bezier Point Constructor
44467 Point: (function () {
44468 function Point(x, y, time) {
44471 this.time = time || Date.now();
44473 Point.prototype.distanceTo = function (start) {
44474 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44476 Point.prototype.equals = function (other) {
44477 return this.x === other.x && this.y === other.y && this.time === other.time;
44479 Point.prototype.velocityFrom = function (start) {
44480 return this.time !== start.time
44481 ? this.distanceTo(start) / (this.time - start.time)
44488 // Bezier Constructor
44489 Bezier: (function () {
44490 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44491 this.startPoint = startPoint;
44492 this.control2 = control2;
44493 this.control1 = control1;
44494 this.endPoint = endPoint;
44495 this.startWidth = startWidth;
44496 this.endWidth = endWidth;
44498 Bezier.fromPoints = function (points, widths, scope) {
44499 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44500 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44501 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44503 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44504 var dx1 = s1.x - s2.x;
44505 var dy1 = s1.y - s2.y;
44506 var dx2 = s2.x - s3.x;
44507 var dy2 = s2.y - s3.y;
44508 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44509 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44510 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44511 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44512 var dxm = m1.x - m2.x;
44513 var dym = m1.y - m2.y;
44514 var k = l2 / (l1 + l2);
44515 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44516 var tx = s2.x - cm.x;
44517 var ty = s2.y - cm.y;
44519 c1: new scope.Point(m1.x + tx, m1.y + ty),
44520 c2: new scope.Point(m2.x + tx, m2.y + ty)
44523 Bezier.prototype.length = function () {
44528 for (var i = 0; i <= steps; i += 1) {
44530 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44531 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44533 var xdiff = cx - px;
44534 var ydiff = cy - py;
44535 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44542 Bezier.prototype.point = function (t, start, c1, c2, end) {
44543 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44544 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44545 + (3.0 * c2 * (1.0 - t) * t * t)
44546 + (end * t * t * t);
44551 throttleStroke: function(fn, wait) {
44552 if (wait === void 0) { wait = 250; }
44554 var timeout = null;
44558 var later = function () {
44559 previous = Date.now();
44561 result = fn.apply(storedContext, storedArgs);
44563 storedContext = null;
44567 return function wrapper() {
44569 for (var _i = 0; _i < arguments.length; _i++) {
44570 args[_i] = arguments[_i];
44572 var now = Date.now();
44573 var remaining = wait - (now - previous);
44574 storedContext = this;
44576 if (remaining <= 0 || remaining > wait) {
44578 clearTimeout(timeout);
44582 result = fn.apply(storedContext, storedArgs);
44584 storedContext = null;
44588 else if (!timeout) {
44589 timeout = window.setTimeout(later, remaining);