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
653 * @cfg {String} role default blank - set to button to force cursor pointer
657 * Create a new Element
658 * @param {Object} config The config object
661 Roo.bootstrap.Element = function(config){
662 Roo.bootstrap.Element.superclass.constructor.call(this, config);
668 * When a element is chick
669 * @param {Roo.bootstrap.Element} this
670 * @param {Roo.EventObject} e
678 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
683 preventDefault: false,
688 getAutoCreate : function(){
692 // cls: this.cls, double assign in parent class Component.js :: onRender
695 if (this.role !== false) {
696 cfg.role = this.role;
702 initEvents: function()
704 Roo.bootstrap.Element.superclass.initEvents.call(this);
707 this.el.on('click', this.onClick, this);
713 onClick : function(e)
715 if(this.preventDefault){
719 this.fireEvent('click', this, e); // why was this double click before?
727 getValue : function()
729 return this.el.dom.innerHTML;
732 setValue : function(value)
734 this.el.dom.innerHTML = value;
749 * @class Roo.bootstrap.DropTarget
750 * @extends Roo.bootstrap.Element
751 * Bootstrap DropTarget class
753 * @cfg {string} name dropable name
756 * Create a new Dropable Area
757 * @param {Object} config The config object
760 Roo.bootstrap.DropTarget = function(config){
761 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
767 * When a element is chick
768 * @param {Roo.bootstrap.Element} this
769 * @param {Roo.EventObject} e
775 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
778 getAutoCreate : function(){
783 initEvents: function()
785 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
786 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
789 drop : this.dragDrop.createDelegate(this),
790 enter : this.dragEnter.createDelegate(this),
791 out : this.dragOut.createDelegate(this),
792 over : this.dragOver.createDelegate(this)
796 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
799 dragDrop : function(source,e,data)
801 // user has to decide how to impliment this.
804 //this.fireEvent('drop', this, source, e ,data);
808 dragEnter : function(n, dd, e, data)
810 // probably want to resize the element to match the dropped element..
812 this.originalSize = this.el.getSize();
813 this.el.setSize( n.el.getSize());
814 this.dropZone.DDM.refreshCache(this.name);
815 Roo.log([n, dd, e, data]);
818 dragOut : function(value)
820 // resize back to normal
822 this.el.setSize(this.originalSize);
823 this.dropZone.resetConstraints();
826 dragOver : function()
843 * @class Roo.bootstrap.Body
844 * @extends Roo.bootstrap.Component
845 * Bootstrap Body class
849 * @param {Object} config The config object
852 Roo.bootstrap.Body = function(config){
854 config = config || {};
856 Roo.bootstrap.Body.superclass.constructor.call(this, config);
857 this.el = Roo.get(config.el ? config.el : document.body );
858 if (this.cls && this.cls.length) {
859 Roo.get(document.body).addClass(this.cls);
863 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
865 is_body : true,// just to make sure it's constructed?
870 onRender : function(ct, position)
872 /* Roo.log("Roo.bootstrap.Body - onRender");
873 if (this.cls && this.cls.length) {
874 Roo.get(document.body).addClass(this.cls);
893 * @class Roo.bootstrap.ButtonGroup
894 * @extends Roo.bootstrap.Component
895 * Bootstrap ButtonGroup class
896 * @cfg {String} size lg | sm | xs (default empty normal)
897 * @cfg {String} align vertical | justified (default none)
898 * @cfg {String} direction up | down (default down)
899 * @cfg {Boolean} toolbar false | true
900 * @cfg {Boolean} btn true | false
905 * @param {Object} config The config object
908 Roo.bootstrap.ButtonGroup = function(config){
909 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
912 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
920 getAutoCreate : function(){
926 cfg.html = this.html || cfg.html;
937 if (['vertical','justified'].indexOf(this.align)!==-1) {
938 cfg.cls = 'btn-group-' + this.align;
940 if (this.align == 'justified') {
941 console.log(this.items);
945 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
946 cfg.cls += ' btn-group-' + this.size;
949 if (this.direction == 'up') {
950 cfg.cls += ' dropup' ;
956 * Add a button to the group (similar to NavItem API.)
958 addItem : function(cfg)
960 var cn = new Roo.bootstrap.Button(cfg);
962 cn.parentId = this.id;
963 cn.onRender(this.el, null);
977 * @class Roo.bootstrap.Button
978 * @extends Roo.bootstrap.Component
979 * Bootstrap Button class
980 * @cfg {String} html The button content
981 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
982 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
983 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
984 * @cfg {String} size (lg|sm|xs)
985 * @cfg {String} tag (a|input|submit)
986 * @cfg {String} href empty or href
987 * @cfg {Boolean} disabled default false;
988 * @cfg {Boolean} isClose default false;
989 * @cfg {String} glyphicon depricated - use fa
990 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
991 * @cfg {String} badge text for badge
992 * @cfg {String} theme (default|glow)
993 * @cfg {Boolean} inverse dark themed version
994 * @cfg {Boolean} toggle is it a slidy toggle button
995 * @cfg {Boolean} pressed default null - if the button ahs active state
996 * @cfg {String} ontext text for on slidy toggle state
997 * @cfg {String} offtext text for off slidy toggle state
998 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
999 * @cfg {Boolean} removeClass remove the standard class..
1000 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1001 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1004 * Create a new button
1005 * @param {Object} config The config object
1009 Roo.bootstrap.Button = function(config){
1010 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1016 * When a button is pressed
1017 * @param {Roo.bootstrap.Button} btn
1018 * @param {Roo.EventObject} e
1023 * When a button is double clicked
1024 * @param {Roo.bootstrap.Button} btn
1025 * @param {Roo.EventObject} e
1030 * After the button has been toggles
1031 * @param {Roo.bootstrap.Button} btn
1032 * @param {Roo.EventObject} e
1033 * @param {boolean} pressed (also available as button.pressed)
1039 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1060 preventDefault: true,
1069 getAutoCreate : function(){
1077 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1078 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1079 this.tag = 'button';
1083 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1085 if (this.toggle == true) {
1088 cls: 'slider-frame roo-button',
1092 'data-on-text':'ON',
1093 'data-off-text':'OFF',
1094 cls: 'slider-button',
1099 // why are we validating the weights?
1100 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1101 cfg.cls += ' ' + this.weight;
1108 cfg.cls += ' close';
1110 cfg["aria-hidden"] = true;
1112 cfg.html = "×";
1118 if (this.theme==='default') {
1119 cfg.cls = 'btn roo-button';
1121 //if (this.parentType != 'Navbar') {
1122 this.weight = this.weight.length ? this.weight : 'default';
1124 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1126 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1127 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1128 cfg.cls += ' btn-' + outline + weight;
1129 if (this.weight == 'default') {
1131 cfg.cls += ' btn-' + this.weight;
1134 } else if (this.theme==='glow') {
1137 cfg.cls = 'btn-glow roo-button';
1139 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1141 cfg.cls += ' ' + this.weight;
1147 this.cls += ' inverse';
1151 if (this.active || this.pressed === true) {
1152 cfg.cls += ' active';
1155 if (this.disabled) {
1156 cfg.disabled = 'disabled';
1160 Roo.log('changing to ul' );
1162 this.glyphicon = 'caret';
1163 if (Roo.bootstrap.version == 4) {
1164 this.fa = 'caret-down';
1169 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1171 //gsRoo.log(this.parentType);
1172 if (this.parentType === 'Navbar' && !this.parent().bar) {
1173 Roo.log('changing to li?');
1182 href : this.href || '#'
1185 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1186 cfg.cls += ' dropdown';
1193 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1195 if (this.glyphicon) {
1196 cfg.html = ' ' + cfg.html;
1201 cls: 'glyphicon glyphicon-' + this.glyphicon
1206 cfg.html = ' ' + cfg.html;
1211 cls: 'fa fas fa-' + this.fa
1221 // cfg.cls='btn roo-button';
1225 var value = cfg.html;
1230 cls: 'glyphicon glyphicon-' + this.glyphicon,
1237 cls: 'fa fas fa-' + this.fa,
1242 var bw = this.badge_weight.length ? this.badge_weight :
1243 (this.weight.length ? this.weight : 'secondary');
1244 bw = bw == 'default' ? 'secondary' : bw;
1250 cls: 'badge badge-' + bw,
1259 cfg.cls += ' dropdown';
1260 cfg.html = typeof(cfg.html) != 'undefined' ?
1261 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1264 if (cfg.tag !== 'a' && this.href !== '') {
1265 throw "Tag must be a to set href.";
1266 } else if (this.href.length > 0) {
1267 cfg.href = this.href;
1270 if(this.removeClass){
1275 cfg.target = this.target;
1280 initEvents: function() {
1281 // Roo.log('init events?');
1282 // Roo.log(this.el.dom);
1285 if (typeof (this.menu) != 'undefined') {
1286 this.menu.parentType = this.xtype;
1287 this.menu.triggerEl = this.el;
1288 this.addxtype(Roo.apply({}, this.menu));
1292 if (this.el.hasClass('roo-button')) {
1293 this.el.on('click', this.onClick, this);
1294 this.el.on('dblclick', this.onDblClick, this);
1296 this.el.select('.roo-button').on('click', this.onClick, this);
1297 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1301 if(this.removeClass){
1302 this.el.on('click', this.onClick, this);
1305 if (this.group === true) {
1306 if (this.pressed === false || this.pressed === true) {
1309 this.pressed = false;
1310 this.setActive(this.pressed);
1315 this.el.enableDisplayMode();
1318 onClick : function(e)
1320 if (this.disabled) {
1324 Roo.log('button on click ');
1325 if(this.preventDefault){
1334 this.setActive(true);
1335 var pi = this.parent().items;
1336 for (var i = 0;i < pi.length;i++) {
1337 if (this == pi[i]) {
1340 if (pi[i].el.hasClass('roo-button')) {
1341 pi[i].setActive(false);
1344 this.fireEvent('click', this, e);
1348 if (this.pressed === true || this.pressed === false) {
1349 this.toggleActive(e);
1353 this.fireEvent('click', this, e);
1355 onDblClick: function(e)
1357 if (this.disabled) {
1360 if(this.preventDefault){
1363 this.fireEvent('dblclick', this, e);
1366 * Enables this button
1370 this.disabled = false;
1371 this.el.removeClass('disabled');
1372 this.el.dom.removeAttribute("disabled");
1376 * Disable this button
1378 disable : function()
1380 this.disabled = true;
1381 this.el.addClass('disabled');
1382 this.el.attr("disabled", "disabled")
1385 * sets the active state on/off,
1386 * @param {Boolean} state (optional) Force a particular state
1388 setActive : function(v) {
1390 this.el[v ? 'addClass' : 'removeClass']('active');
1394 * toggles the current active state
1396 toggleActive : function(e)
1398 this.setActive(!this.pressed); // this modifies pressed...
1399 this.fireEvent('toggle', this, e, this.pressed);
1402 * get the current active state
1403 * @return {boolean} true if it's active
1405 isActive : function()
1407 return this.el.hasClass('active');
1410 * set the text of the first selected button
1412 setText : function(str)
1414 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1417 * get the text of the first selected button
1419 getText : function()
1421 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1424 setWeight : function(str)
1426 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1427 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1429 var outline = this.outline ? 'outline-' : '';
1430 if (str == 'default') {
1431 this.el.addClass('btn-default btn-outline-secondary');
1434 this.el.addClass('btn-' + outline + str);
1439 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1441 Roo.bootstrap.Button.weights = [
1461 * @class Roo.bootstrap.Column
1462 * @extends Roo.bootstrap.Component
1463 * Bootstrap Column class
1464 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1465 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1466 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1467 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1468 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1469 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1470 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1471 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1474 * @cfg {Boolean} hidden (true|false) hide the element
1475 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1476 * @cfg {String} fa (ban|check|...) font awesome icon
1477 * @cfg {Number} fasize (1|2|....) font awsome size
1479 * @cfg {String} icon (info-sign|check|...) glyphicon name
1481 * @cfg {String} html content of column.
1484 * Create a new Column
1485 * @param {Object} config The config object
1488 Roo.bootstrap.Column = function(config){
1489 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1492 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1510 getAutoCreate : function(){
1511 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1519 var sizes = ['xs','sm','md','lg'];
1520 sizes.map(function(size ,ix){
1521 //Roo.log( size + ':' + settings[size]);
1523 if (settings[size+'off'] !== false) {
1524 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1527 if (settings[size] === false) {
1531 if (!settings[size]) { // 0 = hidden
1532 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1534 for (var i = ix; i > -1; i--) {
1535 cfg.cls += ' d-' + sizes[i] + '-none';
1541 cfg.cls += ' col-' + size + '-' + settings[size] + (
1542 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1548 cfg.cls += ' hidden';
1551 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1552 cfg.cls +=' alert alert-' + this.alert;
1556 if (this.html.length) {
1557 cfg.html = this.html;
1561 if (this.fasize > 1) {
1562 fasize = ' fa-' + this.fasize + 'x';
1564 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1569 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1588 * @class Roo.bootstrap.Container
1589 * @extends Roo.bootstrap.Component
1590 * Bootstrap Container class
1591 * @cfg {Boolean} jumbotron is it a jumbotron element
1592 * @cfg {String} html content of element
1593 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1594 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1595 * @cfg {String} header content of header (for panel)
1596 * @cfg {String} footer content of footer (for panel)
1597 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1598 * @cfg {String} tag (header|aside|section) type of HTML tag.
1599 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1600 * @cfg {String} fa font awesome icon
1601 * @cfg {String} icon (info-sign|check|...) glyphicon name
1602 * @cfg {Boolean} hidden (true|false) hide the element
1603 * @cfg {Boolean} expandable (true|false) default false
1604 * @cfg {Boolean} expanded (true|false) default true
1605 * @cfg {String} rheader contet on the right of header
1606 * @cfg {Boolean} clickable (true|false) default false
1610 * Create a new Container
1611 * @param {Object} config The config object
1614 Roo.bootstrap.Container = function(config){
1615 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1621 * After the panel has been expand
1623 * @param {Roo.bootstrap.Container} this
1628 * After the panel has been collapsed
1630 * @param {Roo.bootstrap.Container} this
1635 * When a element is chick
1636 * @param {Roo.bootstrap.Container} this
1637 * @param {Roo.EventObject} e
1643 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1661 getChildContainer : function() {
1667 if (this.panel.length) {
1668 return this.el.select('.panel-body',true).first();
1675 getAutoCreate : function(){
1678 tag : this.tag || 'div',
1682 if (this.jumbotron) {
1683 cfg.cls = 'jumbotron';
1688 // - this is applied by the parent..
1690 // cfg.cls = this.cls + '';
1693 if (this.sticky.length) {
1695 var bd = Roo.get(document.body);
1696 if (!bd.hasClass('bootstrap-sticky')) {
1697 bd.addClass('bootstrap-sticky');
1698 Roo.select('html',true).setStyle('height', '100%');
1701 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1705 if (this.well.length) {
1706 switch (this.well) {
1709 cfg.cls +=' well well-' +this.well;
1718 cfg.cls += ' hidden';
1722 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1723 cfg.cls +=' alert alert-' + this.alert;
1728 if (this.panel.length) {
1729 cfg.cls += ' panel panel-' + this.panel;
1731 if (this.header.length) {
1735 if(this.expandable){
1737 cfg.cls = cfg.cls + ' expandable';
1741 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1749 cls : 'panel-title',
1750 html : (this.expandable ? ' ' : '') + this.header
1754 cls: 'panel-header-right',
1760 cls : 'panel-heading',
1761 style : this.expandable ? 'cursor: pointer' : '',
1769 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1774 if (this.footer.length) {
1776 cls : 'panel-footer',
1785 body.html = this.html || cfg.html;
1786 // prefix with the icons..
1788 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1791 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1796 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1797 cfg.cls = 'container';
1803 initEvents: function()
1805 if(this.expandable){
1806 var headerEl = this.headerEl();
1809 headerEl.on('click', this.onToggleClick, this);
1814 this.el.on('click', this.onClick, this);
1819 onToggleClick : function()
1821 var headerEl = this.headerEl();
1837 if(this.fireEvent('expand', this)) {
1839 this.expanded = true;
1841 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1843 this.el.select('.panel-body',true).first().removeClass('hide');
1845 var toggleEl = this.toggleEl();
1851 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1856 collapse : function()
1858 if(this.fireEvent('collapse', this)) {
1860 this.expanded = false;
1862 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1863 this.el.select('.panel-body',true).first().addClass('hide');
1865 var toggleEl = this.toggleEl();
1871 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1875 toggleEl : function()
1877 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1881 return this.el.select('.panel-heading .fa',true).first();
1884 headerEl : function()
1886 if(!this.el || !this.panel.length || !this.header.length){
1890 return this.el.select('.panel-heading',true).first()
1895 if(!this.el || !this.panel.length){
1899 return this.el.select('.panel-body',true).first()
1902 titleEl : function()
1904 if(!this.el || !this.panel.length || !this.header.length){
1908 return this.el.select('.panel-title',true).first();
1911 setTitle : function(v)
1913 var titleEl = this.titleEl();
1919 titleEl.dom.innerHTML = v;
1922 getTitle : function()
1925 var titleEl = this.titleEl();
1931 return titleEl.dom.innerHTML;
1934 setRightTitle : function(v)
1936 var t = this.el.select('.panel-header-right',true).first();
1942 t.dom.innerHTML = v;
1945 onClick : function(e)
1949 this.fireEvent('click', this, e);
1956 * This is BS4's Card element.. - similar to our containers probably..
1960 * @class Roo.bootstrap.Card
1961 * @extends Roo.bootstrap.Component
1962 * Bootstrap Card class
1965 * possible... may not be implemented..
1966 * @cfg {String} header_image src url of image.
1967 * @cfg {String|Object} header
1968 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1969 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1971 * @cfg {String} title
1972 * @cfg {String} subtitle
1973 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1974 * @cfg {String} footer
1976 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1978 * @cfg {String} margin (0|1|2|3|4|5|auto)
1979 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1980 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1981 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1982 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1983 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1984 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1986 * @cfg {String} padding (0|1|2|3|4|5)
1987 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1988 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1989 * @cfg {String} padding_left (0|1|2|3|4|5)
1990 * @cfg {String} padding_right (0|1|2|3|4|5)
1991 * @cfg {String} padding_x (0|1|2|3|4|5)
1992 * @cfg {String} padding_y (0|1|2|3|4|5)
1994 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1995 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1996 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1997 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1998 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000 * @config {Boolean} dragable if this card can be dragged.
2001 * @config {String} drag_group group for drag
2002 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2003 * @config {String} drop_group group for drag
2005 * @config {Boolean} collapsable can the body be collapsed.
2006 * @config {Boolean} collapsed is the body collapsed when rendered...
2007 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2008 * @config {Boolean} rotated is the body rotated when rendered...
2011 * Create a new Container
2012 * @param {Object} config The config object
2015 Roo.bootstrap.Card = function(config){
2016 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2022 * When a element a card is dropped
2023 * @param {Roo.bootstrap.Card} this
2026 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2027 * @param {String} position 'above' or 'below'
2028 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2034 * When a element a card is rotate
2035 * @param {Roo.bootstrap.Card} this
2036 * @param {Roo.Element} n the node being dropped?
2037 * @param {Boolean} rotate status
2042 * When a card element is dragged over ready to drop (return false to block dropable)
2043 * @param {Roo.bootstrap.Card} this
2044 * @param {Object} data from dragdrop
2052 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2057 margin: '', /// may be better in component?
2087 collapsable : false,
2096 childContainer : false,
2097 dropEl : false, /// the dom placeholde element that indicates drop location.
2098 containerEl: false, // body container
2099 bodyEl: false, // card-body
2100 headerContainerEl : false, //
2102 header_imageEl : false,
2105 layoutCls : function()
2109 Roo.log(this.margin_bottom.length);
2110 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2111 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2113 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2114 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2116 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2117 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2121 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2122 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2123 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2127 // more generic support?
2135 // Roo.log("Call onRender: " + this.xtype);
2136 /* We are looking at something like this.
2138 <img src="..." class="card-img-top" alt="...">
2139 <div class="card-body">
2140 <h5 class="card-title">Card title</h5>
2141 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2143 >> this bit is really the body...
2144 <div> << we will ad dthis in hopefully it will not break shit.
2146 ** card text does not actually have any styling...
2148 <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>
2151 <a href="#" class="card-link">Card link</a>
2154 <div class="card-footer">
2155 <small class="text-muted">Last updated 3 mins ago</small>
2159 getAutoCreate : function(){
2167 if (this.weight.length && this.weight != 'light') {
2168 cfg.cls += ' text-white';
2170 cfg.cls += ' text-dark'; // need as it's nested..
2172 if (this.weight.length) {
2173 cfg.cls += ' bg-' + this.weight;
2176 cfg.cls += ' ' + this.layoutCls();
2179 var hdr_ctr = false;
2180 if (this.header.length) {
2182 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2183 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2191 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2197 if (this.collapsable) {
2200 cls : 'd-block user-select-none',
2204 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2209 hdr.cn.push(hdr_ctr);
2214 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2219 if (this.header_image.length) {
2222 cls : 'card-img-top',
2223 src: this.header_image // escape?
2228 cls : 'card-img-top d-none'
2234 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2238 if (this.collapsable || this.rotateable) {
2241 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2248 if (this.title.length) {
2252 src: this.title // escape?
2256 if (this.subtitle.length) {
2260 src: this.subtitle // escape?
2266 cls : 'roo-card-body-ctr'
2269 if (this.html.length) {
2275 // fixme ? handle objects?
2277 if (this.footer.length) {
2280 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2285 cfg.cn.push({cls : 'card-footer d-none'});
2294 getCardHeader : function()
2296 var ret = this.el.select('.card-header',true).first();
2297 if (ret.hasClass('d-none')) {
2298 ret.removeClass('d-none');
2303 getCardFooter : function()
2305 var ret = this.el.select('.card-footer',true).first();
2306 if (ret.hasClass('d-none')) {
2307 ret.removeClass('d-none');
2312 getCardImageTop : function()
2314 var ret = this.header_imageEl;
2315 if (ret.hasClass('d-none')) {
2316 ret.removeClass('d-none');
2322 getChildContainer : function()
2328 return this.el.select('.roo-card-body-ctr',true).first();
2331 initEvents: function()
2333 this.bodyEl = this.el.select('.card-body',true).first();
2334 this.containerEl = this.getChildContainer();
2336 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2337 containerScroll: true,
2338 ddGroup: this.drag_group || 'default_card_drag_group'
2340 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2342 if (this.dropable) {
2343 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2344 containerScroll: true,
2345 ddGroup: this.drop_group || 'default_card_drag_group'
2347 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2348 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2349 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2350 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2351 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2354 if (this.collapsable) {
2355 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2357 if (this.rotateable) {
2358 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2360 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2362 this.footerEl = this.el.select('.card-footer',true).first();
2363 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2364 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2365 this.headerEl = this.el.select('.card-header',true).first();
2368 this.el.addClass('roo-card-rotated');
2369 this.fireEvent('rotate', this, true);
2371 this.header_imageEl = this.el.select('.card-img-top',true).first();
2372 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2375 getDragData : function(e)
2377 var target = this.getEl();
2379 //this.handleSelection(e);
2384 nodes: this.getEl(),
2389 dragData.ddel = target.dom ; // the div element
2390 Roo.log(target.getWidth( ));
2391 dragData.ddel.style.width = target.getWidth() + 'px';
2398 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2399 * whole Element becomes the target, and this causes the drop gesture to append.
2401 * Returns an object:
2404 position : 'below' or 'above'
2405 card : relateive to card OBJECT (or true for no cards listed)
2406 items_n : relative to nth item in list
2407 card_n : relative to nth card in list
2412 getTargetFromEvent : function(e, dragged_card_el)
2414 var target = e.getTarget();
2415 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2416 target = target.parentNode;
2427 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2428 // see if target is one of the 'cards'...
2431 //Roo.log(this.items.length);
2434 var last_card_n = 0;
2436 for (var i = 0;i< this.items.length;i++) {
2438 if (!this.items[i].el.hasClass('card')) {
2441 pos = this.getDropPoint(e, this.items[i].el.dom);
2443 cards_len = ret.cards.length;
2444 //Roo.log(this.items[i].el.dom.id);
2445 ret.cards.push(this.items[i]);
2447 if (ret.card_n < 0 && pos == 'above') {
2448 ret.position = cards_len > 0 ? 'below' : pos;
2449 ret.items_n = i > 0 ? i - 1 : 0;
2450 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2451 ret.card = ret.cards[ret.card_n];
2454 if (!ret.cards.length) {
2456 ret.position = 'below';
2460 // could not find a card.. stick it at the end..
2461 if (ret.card_n < 0) {
2462 ret.card_n = last_card_n;
2463 ret.card = ret.cards[last_card_n];
2464 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2465 ret.position = 'below';
2468 if (this.items[ret.items_n].el == dragged_card_el) {
2472 if (ret.position == 'below') {
2473 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2475 if (card_after && card_after.el == dragged_card_el) {
2482 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2484 if (card_before && card_before.el == dragged_card_el) {
2491 onNodeEnter : function(n, dd, e, data){
2494 onNodeOver : function(n, dd, e, data)
2497 var target_info = this.getTargetFromEvent(e,data.source.el);
2498 if (target_info === false) {
2499 this.dropPlaceHolder('hide');
2502 Roo.log(['getTargetFromEvent', target_info ]);
2505 if (this.fireEvent('cardover', this, [ data ]) === false) {
2509 this.dropPlaceHolder('show', target_info,data);
2513 onNodeOut : function(n, dd, e, data){
2514 this.dropPlaceHolder('hide');
2517 onNodeDrop : function(n, dd, e, data)
2520 // call drop - return false if
2522 // this could actually fail - if the Network drops..
2523 // we will ignore this at present..- client should probably reload
2524 // the whole set of cards if stuff like that fails.
2527 var info = this.getTargetFromEvent(e,data.source.el);
2528 if (info === false) {
2531 this.dropPlaceHolder('hide');
2535 this.acceptCard(data.source, info.position, info.card, info.items_n);
2539 firstChildCard : function()
2541 for (var i = 0;i< this.items.length;i++) {
2543 if (!this.items[i].el.hasClass('card')) {
2546 return this.items[i];
2548 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2553 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2555 acceptCard : function(move_card, position, next_to_card )
2557 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2561 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2563 move_card.parent().removeCard(move_card);
2566 var dom = move_card.el.dom;
2567 dom.style.width = ''; // clear with - which is set by drag.
2569 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2570 var cardel = next_to_card.el.dom;
2572 if (position == 'above' ) {
2573 cardel.parentNode.insertBefore(dom, cardel);
2574 } else if (cardel.nextSibling) {
2575 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2577 cardel.parentNode.append(dom);
2580 // card container???
2581 this.containerEl.dom.append(dom);
2584 //FIXME HANDLE card = true
2586 // add this to the correct place in items.
2588 // remove Card from items.
2591 if (this.items.length) {
2593 //Roo.log([info.items_n, info.position, this.items.length]);
2594 for (var i =0; i < this.items.length; i++) {
2595 if (i == to_items_n && position == 'above') {
2596 nitems.push(move_card);
2598 nitems.push(this.items[i]);
2599 if (i == to_items_n && position == 'below') {
2600 nitems.push(move_card);
2603 this.items = nitems;
2604 Roo.log(this.items);
2606 this.items.push(move_card);
2609 move_card.parentId = this.id;
2615 removeCard : function(c)
2617 this.items = this.items.filter(function(e) { return e != c });
2620 dom.parentNode.removeChild(dom);
2621 dom.style.width = ''; // clear with - which is set by drag.
2626 /** Decide whether to drop above or below a View node. */
2627 getDropPoint : function(e, n, dd)
2632 if (n == this.containerEl.dom) {
2635 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2636 var c = t + (b - t) / 2;
2637 var y = Roo.lib.Event.getPageY(e);
2644 onToggleCollapse : function(e)
2646 if (this.collapsed) {
2647 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2648 this.collapsableEl.addClass('show');
2649 this.collapsed = false;
2652 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2653 this.collapsableEl.removeClass('show');
2654 this.collapsed = true;
2659 onToggleRotate : function(e)
2661 this.collapsableEl.removeClass('show');
2662 this.footerEl.removeClass('d-none');
2663 this.el.removeClass('roo-card-rotated');
2664 this.el.removeClass('d-none');
2667 this.collapsableEl.addClass('show');
2668 this.rotated = false;
2669 this.fireEvent('rotate', this, this.rotated);
2672 this.el.addClass('roo-card-rotated');
2673 this.footerEl.addClass('d-none');
2674 this.el.select('.roo-collapsable').removeClass('show');
2676 this.rotated = true;
2677 this.fireEvent('rotate', this, this.rotated);
2681 dropPlaceHolder: function (action, info, data)
2683 if (this.dropEl === false) {
2684 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2688 this.dropEl.removeClass(['d-none', 'd-block']);
2689 if (action == 'hide') {
2691 this.dropEl.addClass('d-none');
2694 // FIXME - info.card == true!!!
2695 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2697 if (info.card !== true) {
2698 var cardel = info.card.el.dom;
2700 if (info.position == 'above') {
2701 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2702 } else if (cardel.nextSibling) {
2703 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2705 cardel.parentNode.append(this.dropEl.dom);
2708 // card container???
2709 this.containerEl.dom.append(this.dropEl.dom);
2712 this.dropEl.addClass('d-block roo-card-dropzone');
2714 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2721 setHeaderText: function(html)
2724 if (this.headerContainerEl) {
2725 this.headerContainerEl.dom.innerHTML = html;
2728 onHeaderImageLoad : function(ev, he)
2730 if (!this.header_image_fit_square) {
2734 var hw = he.naturalHeight / he.naturalWidth;
2737 //var w = he.dom.naturalWidth;
2740 he.style.position = 'relative';
2742 var nw = (ww * (1/hw));
2743 Roo.get(he).setSize( ww * (1/hw), ww);
2744 he.style.left = ((ww - nw)/ 2) + 'px';
2745 he.style.position = 'relative';
2756 * Card header - holder for the card header elements.
2761 * @class Roo.bootstrap.CardHeader
2762 * @extends Roo.bootstrap.Element
2763 * Bootstrap CardHeader class
2765 * Create a new Card Header - that you can embed children into
2766 * @param {Object} config The config object
2769 Roo.bootstrap.CardHeader = function(config){
2770 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2773 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2776 container_method : 'getCardHeader'
2789 * Card footer - holder for the card footer elements.
2794 * @class Roo.bootstrap.CardFooter
2795 * @extends Roo.bootstrap.Element
2796 * Bootstrap CardFooter class
2798 * Create a new Card Footer - that you can embed children into
2799 * @param {Object} config The config object
2802 Roo.bootstrap.CardFooter = function(config){
2803 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2806 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2809 container_method : 'getCardFooter'
2822 * Card header - holder for the card header elements.
2827 * @class Roo.bootstrap.CardImageTop
2828 * @extends Roo.bootstrap.Element
2829 * Bootstrap CardImageTop class
2831 * Create a new Card Image Top container
2832 * @param {Object} config The config object
2835 Roo.bootstrap.CardImageTop = function(config){
2836 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2839 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2842 container_method : 'getCardImageTop'
2857 * @class Roo.bootstrap.ButtonUploader
2858 * @extends Roo.bootstrap.Button
2859 * Bootstrap Button Uploader class - it's a button which when you add files to it
2862 * @cfg {Number} errorTimeout default 3000
2863 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2864 * @cfg {Array} html The button text.
2868 * Create a new CardUploader
2869 * @param {Object} config The config object
2872 Roo.bootstrap.ButtonUploader = function(config){
2876 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2882 * @event beforeselect
2883 * When button is pressed, before show upload files dialog is shown
2884 * @param {Roo.bootstrap.UploaderButton} this
2887 'beforeselect' : true,
2889 * @event fired when files have been selected,
2890 * When a the download link is clicked
2891 * @param {Roo.bootstrap.Card} this
2892 * @param {Object} The image information data contains
2899 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2902 errorTimeout : 3000,
2906 fileCollection : false,
2909 getAutoCreate : function()
2916 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2919 multiple : 'multiple',
2921 cls : 'd-none roo-card-upload-selector'
2933 initEvents : function()
2936 Roo.bootstrap.Button.prototype.initEvents.call(this);
2942 this.urlAPI = (window.createObjectURL && window) ||
2943 (window.URL && URL.revokeObjectURL && URL) ||
2944 (window.webkitURL && webkitURL);
2949 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2951 this.selectorEl.on('change', this.onFileSelected, this);
2958 onClick : function(e)
2962 if ( this.fireEvent('beforeselect', this) === false) {
2966 this.selectorEl.dom.click();
2970 onFileSelected : function(e)
2974 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2978 Roo.each(this.selectorEl.dom.files, function(file){
2979 var url = this.urlAPI.createObjectURL(file); // not sure...
2980 this.fireEvent('uploaded', this, [file, url]);
2989 * addCard - add an Attachment to the uploader
2990 * @param data - the data about the image to upload
2994 title : "Title of file",
2995 is_uploaded : false,
2996 src : "http://.....",
2997 srcfile : { the File upload object },
2998 mimetype : file.type,
3001 .. any other data...
3026 * @class Roo.bootstrap.Img
3027 * @extends Roo.bootstrap.Component
3028 * Bootstrap Img class
3029 * @cfg {Boolean} imgResponsive false | true
3030 * @cfg {String} border rounded | circle | thumbnail
3031 * @cfg {String} src image source
3032 * @cfg {String} alt image alternative text
3033 * @cfg {String} href a tag href
3034 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3035 * @cfg {String} xsUrl xs image source
3036 * @cfg {String} smUrl sm image source
3037 * @cfg {String} mdUrl md image source
3038 * @cfg {String} lgUrl lg image source
3041 * Create a new Input
3042 * @param {Object} config The config object
3045 Roo.bootstrap.Img = function(config){
3046 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3052 * The img click event for the img.
3053 * @param {Roo.EventObject} e
3059 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3061 imgResponsive: true,
3071 getAutoCreate : function()
3073 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3074 return this.createSingleImg();
3079 cls: 'roo-image-responsive-group',
3084 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3086 if(!_this[size + 'Url']){
3092 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3093 html: _this.html || cfg.html,
3094 src: _this[size + 'Url']
3097 img.cls += ' roo-image-responsive-' + size;
3099 var s = ['xs', 'sm', 'md', 'lg'];
3101 s.splice(s.indexOf(size), 1);
3103 Roo.each(s, function(ss){
3104 img.cls += ' hidden-' + ss;
3107 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3108 cfg.cls += ' img-' + _this.border;
3112 cfg.alt = _this.alt;
3125 a.target = _this.target;
3129 cfg.cn.push((_this.href) ? a : img);
3136 createSingleImg : function()
3140 cls: (this.imgResponsive) ? 'img-responsive' : '',
3142 src : 'about:blank' // just incase src get's set to undefined?!?
3145 cfg.html = this.html || cfg.html;
3147 cfg.src = this.src || cfg.src;
3149 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3150 cfg.cls += ' img-' + this.border;
3167 a.target = this.target;
3172 return (this.href) ? a : cfg;
3175 initEvents: function()
3178 this.el.on('click', this.onClick, this);
3183 onClick : function(e)
3185 Roo.log('img onclick');
3186 this.fireEvent('click', this, e);
3189 * Sets the url of the image - used to update it
3190 * @param {String} url the url of the image
3193 setSrc : function(url)
3197 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3198 this.el.dom.src = url;
3202 this.el.select('img', true).first().dom.src = url;
3218 * @class Roo.bootstrap.Link
3219 * @extends Roo.bootstrap.Component
3220 * Bootstrap Link Class
3221 * @cfg {String} alt image alternative text
3222 * @cfg {String} href a tag href
3223 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3224 * @cfg {String} html the content of the link.
3225 * @cfg {String} anchor name for the anchor link
3226 * @cfg {String} fa - favicon
3228 * @cfg {Boolean} preventDefault (true | false) default false
3232 * Create a new Input
3233 * @param {Object} config The config object
3236 Roo.bootstrap.Link = function(config){
3237 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3243 * The img click event for the img.
3244 * @param {Roo.EventObject} e
3250 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3254 preventDefault: false,
3260 getAutoCreate : function()
3262 var html = this.html || '';
3264 if (this.fa !== false) {
3265 html = '<i class="fa fa-' + this.fa + '"></i>';
3270 // anchor's do not require html/href...
3271 if (this.anchor === false) {
3273 cfg.href = this.href || '#';
3275 cfg.name = this.anchor;
3276 if (this.html !== false || this.fa !== false) {
3279 if (this.href !== false) {
3280 cfg.href = this.href;
3284 if(this.alt !== false){
3289 if(this.target !== false) {
3290 cfg.target = this.target;
3296 initEvents: function() {
3298 if(!this.href || this.preventDefault){
3299 this.el.on('click', this.onClick, this);
3303 onClick : function(e)
3305 if(this.preventDefault){
3308 //Roo.log('img onclick');
3309 this.fireEvent('click', this, e);
3322 * @class Roo.bootstrap.Header
3323 * @extends Roo.bootstrap.Component
3324 * Bootstrap Header class
3325 * @cfg {String} html content of header
3326 * @cfg {Number} level (1|2|3|4|5|6) default 1
3329 * Create a new Header
3330 * @param {Object} config The config object
3334 Roo.bootstrap.Header = function(config){
3335 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3338 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3346 getAutoCreate : function(){
3351 tag: 'h' + (1 *this.level),
3352 html: this.html || ''
3364 * Ext JS Library 1.1.1
3365 * Copyright(c) 2006-2007, Ext JS, LLC.
3367 * Originally Released Under LGPL - original licence link has changed is not relivant.
3370 * <script type="text/javascript">
3374 * @class Roo.bootstrap.MenuMgr
3375 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3378 Roo.bootstrap.MenuMgr = function(){
3379 var menus, active, groups = {}, attached = false, lastShow = new Date();
3381 // private - called when first menu is created
3384 active = new Roo.util.MixedCollection();
3385 Roo.get(document).addKeyListener(27, function(){
3386 if(active.length > 0){
3394 if(active && active.length > 0){
3395 var c = active.clone();
3405 if(active.length < 1){
3406 Roo.get(document).un("mouseup", onMouseDown);
3414 var last = active.last();
3415 lastShow = new Date();
3418 Roo.get(document).on("mouseup", onMouseDown);
3423 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3424 m.parentMenu.activeChild = m;
3425 }else if(last && last.isVisible()){
3426 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3431 function onBeforeHide(m){
3433 m.activeChild.hide();
3435 if(m.autoHideTimer){
3436 clearTimeout(m.autoHideTimer);
3437 delete m.autoHideTimer;
3442 function onBeforeShow(m){
3443 var pm = m.parentMenu;
3444 if(!pm && !m.allowOtherMenus){
3446 }else if(pm && pm.activeChild && active != m){
3447 pm.activeChild.hide();
3451 // private this should really trigger on mouseup..
3452 function onMouseDown(e){
3453 Roo.log("on Mouse Up");
3455 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3456 Roo.log("MenuManager hideAll");
3465 function onBeforeCheck(mi, state){
3467 var g = groups[mi.group];
3468 for(var i = 0, l = g.length; i < l; i++){
3470 g[i].setChecked(false);
3479 * Hides all menus that are currently visible
3481 hideAll : function(){
3486 register : function(menu){
3490 menus[menu.id] = menu;
3491 menu.on("beforehide", onBeforeHide);
3492 menu.on("hide", onHide);
3493 menu.on("beforeshow", onBeforeShow);
3494 menu.on("show", onShow);
3496 if(g && menu.events["checkchange"]){
3500 groups[g].push(menu);
3501 menu.on("checkchange", onCheck);
3506 * Returns a {@link Roo.menu.Menu} object
3507 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3508 * be used to generate and return a new Menu instance.
3510 get : function(menu){
3511 if(typeof menu == "string"){ // menu id
3513 }else if(menu.events){ // menu instance
3516 /*else if(typeof menu.length == 'number'){ // array of menu items?
3517 return new Roo.bootstrap.Menu({items:menu});
3518 }else{ // otherwise, must be a config
3519 return new Roo.bootstrap.Menu(menu);
3526 unregister : function(menu){
3527 delete menus[menu.id];
3528 menu.un("beforehide", onBeforeHide);
3529 menu.un("hide", onHide);
3530 menu.un("beforeshow", onBeforeShow);
3531 menu.un("show", onShow);
3533 if(g && menu.events["checkchange"]){
3534 groups[g].remove(menu);
3535 menu.un("checkchange", onCheck);
3540 registerCheckable : function(menuItem){
3541 var g = menuItem.group;
3546 groups[g].push(menuItem);
3547 menuItem.on("beforecheckchange", onBeforeCheck);
3552 unregisterCheckable : function(menuItem){
3553 var g = menuItem.group;
3555 groups[g].remove(menuItem);
3556 menuItem.un("beforecheckchange", onBeforeCheck);
3568 * @class Roo.bootstrap.Menu
3569 * @extends Roo.bootstrap.Component
3570 * Bootstrap Menu class - container for MenuItems
3571 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3572 * @cfg {bool} hidden if the menu should be hidden when rendered.
3573 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3574 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3578 * @param {Object} config The config object
3582 Roo.bootstrap.Menu = function(config){
3583 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3584 if (this.registerMenu && this.type != 'treeview') {
3585 Roo.bootstrap.MenuMgr.register(this);
3592 * Fires before this menu is displayed (return false to block)
3593 * @param {Roo.menu.Menu} this
3598 * Fires before this menu is hidden (return false to block)
3599 * @param {Roo.menu.Menu} this
3604 * Fires after this menu is displayed
3605 * @param {Roo.menu.Menu} this
3610 * Fires after this menu is hidden
3611 * @param {Roo.menu.Menu} this
3616 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3617 * @param {Roo.menu.Menu} this
3618 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3619 * @param {Roo.EventObject} e
3624 * Fires when the mouse is hovering over this menu
3625 * @param {Roo.menu.Menu} this
3626 * @param {Roo.EventObject} e
3627 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3632 * Fires when the mouse exits this menu
3633 * @param {Roo.menu.Menu} this
3634 * @param {Roo.EventObject} e
3635 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3640 * Fires when a menu item contained in this menu is clicked
3641 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3642 * @param {Roo.EventObject} e
3646 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3649 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3653 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3656 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3658 registerMenu : true,
3660 menuItems :false, // stores the menu items..
3670 getChildContainer : function() {
3674 getAutoCreate : function(){
3676 //if (['right'].indexOf(this.align)!==-1) {
3677 // cfg.cn[1].cls += ' pull-right'
3683 cls : 'dropdown-menu' ,
3684 style : 'z-index:1000'
3688 if (this.type === 'submenu') {
3689 cfg.cls = 'submenu active';
3691 if (this.type === 'treeview') {
3692 cfg.cls = 'treeview-menu';
3697 initEvents : function() {
3699 // Roo.log("ADD event");
3700 // Roo.log(this.triggerEl.dom);
3702 this.triggerEl.on('click', this.onTriggerClick, this);
3704 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3707 if (this.triggerEl.hasClass('nav-item')) {
3708 // dropdown toggle on the 'a' in BS4?
3709 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3711 this.triggerEl.addClass('dropdown-toggle');
3714 this.el.on('touchstart' , this.onTouch, this);
3716 this.el.on('click' , this.onClick, this);
3718 this.el.on("mouseover", this.onMouseOver, this);
3719 this.el.on("mouseout", this.onMouseOut, this);
3723 findTargetItem : function(e)
3725 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3729 //Roo.log(t); Roo.log(t.id);
3731 //Roo.log(this.menuitems);
3732 return this.menuitems.get(t.id);
3734 //return this.items.get(t.menuItemId);
3740 onTouch : function(e)
3742 Roo.log("menu.onTouch");
3743 //e.stopEvent(); this make the user popdown broken
3747 onClick : function(e)
3749 Roo.log("menu.onClick");
3751 var t = this.findTargetItem(e);
3752 if(!t || t.isContainer){
3757 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3758 if(t == this.activeItem && t.shouldDeactivate(e)){
3759 this.activeItem.deactivate();
3760 delete this.activeItem;
3764 this.setActiveItem(t, true);
3772 Roo.log('pass click event');
3776 this.fireEvent("click", this, t, e);
3780 if(!t.href.length || t.href == '#'){
3781 (function() { _this.hide(); }).defer(100);
3786 onMouseOver : function(e){
3787 var t = this.findTargetItem(e);
3790 // if(t.canActivate && !t.disabled){
3791 // this.setActiveItem(t, true);
3795 this.fireEvent("mouseover", this, e, t);
3797 isVisible : function(){
3798 return !this.hidden;
3800 onMouseOut : function(e){
3801 var t = this.findTargetItem(e);
3804 // if(t == this.activeItem && t.shouldDeactivate(e)){
3805 // this.activeItem.deactivate();
3806 // delete this.activeItem;
3809 this.fireEvent("mouseout", this, e, t);
3814 * Displays this menu relative to another element
3815 * @param {String/HTMLElement/Roo.Element} element The element to align to
3816 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3817 * the element (defaults to this.defaultAlign)
3818 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3820 show : function(el, pos, parentMenu)
3822 if (false === this.fireEvent("beforeshow", this)) {
3823 Roo.log("show canceled");
3826 this.parentMenu = parentMenu;
3831 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3834 * Displays this menu at a specific xy position
3835 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3836 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3838 showAt : function(xy, parentMenu, /* private: */_e){
3839 this.parentMenu = parentMenu;
3844 this.fireEvent("beforeshow", this);
3845 //xy = this.el.adjustForConstraints(xy);
3849 this.hideMenuItems();
3850 this.hidden = false;
3851 this.triggerEl.addClass('open');
3852 this.el.addClass('show');
3854 // reassign x when hitting right
3855 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3856 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3859 // reassign y when hitting bottom
3860 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3861 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3864 // but the list may align on trigger left or trigger top... should it be a properity?
3866 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3871 this.fireEvent("show", this);
3877 this.doFocus.defer(50, this);
3881 doFocus : function(){
3883 this.focusEl.focus();
3888 * Hides this menu and optionally all parent menus
3889 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3891 hide : function(deep)
3893 if (false === this.fireEvent("beforehide", this)) {
3894 Roo.log("hide canceled");
3897 this.hideMenuItems();
3898 if(this.el && this.isVisible()){
3900 if(this.activeItem){
3901 this.activeItem.deactivate();
3902 this.activeItem = null;
3904 this.triggerEl.removeClass('open');;
3905 this.el.removeClass('show');
3907 this.fireEvent("hide", this);
3909 if(deep === true && this.parentMenu){
3910 this.parentMenu.hide(true);
3914 onTriggerClick : function(e)
3916 Roo.log('trigger click');
3918 var target = e.getTarget();
3920 Roo.log(target.nodeName.toLowerCase());
3922 if(target.nodeName.toLowerCase() === 'i'){
3928 onTriggerPress : function(e)
3930 Roo.log('trigger press');
3931 //Roo.log(e.getTarget());
3932 // Roo.log(this.triggerEl.dom);
3934 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3935 var pel = Roo.get(e.getTarget());
3936 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3937 Roo.log('is treeview or dropdown?');
3941 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3945 if (this.isVisible()) {
3950 this.show(this.triggerEl, '?', false);
3953 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3960 hideMenuItems : function()
3962 Roo.log("hide Menu Items");
3967 this.el.select('.open',true).each(function(aa) {
3969 aa.removeClass('open');
3973 addxtypeChild : function (tree, cntr) {
3974 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3976 this.menuitems.add(comp);
3988 this.getEl().dom.innerHTML = '';
3989 this.menuitems.clear();
4003 * @class Roo.bootstrap.MenuItem
4004 * @extends Roo.bootstrap.Component
4005 * Bootstrap MenuItem class
4006 * @cfg {String} html the menu label
4007 * @cfg {String} href the link
4008 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4009 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4010 * @cfg {Boolean} active used on sidebars to highlight active itesm
4011 * @cfg {String} fa favicon to show on left of menu item.
4012 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4016 * Create a new MenuItem
4017 * @param {Object} config The config object
4021 Roo.bootstrap.MenuItem = function(config){
4022 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4027 * The raw click event for the entire grid.
4028 * @param {Roo.bootstrap.MenuItem} this
4029 * @param {Roo.EventObject} e
4035 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4039 preventDefault: false,
4040 isContainer : false,
4044 getAutoCreate : function(){
4046 if(this.isContainer){
4049 cls: 'dropdown-menu-item '
4059 cls : 'dropdown-item',
4064 if (this.fa !== false) {
4067 cls : 'fa fa-' + this.fa
4076 cls: 'dropdown-menu-item',
4079 if (this.parent().type == 'treeview') {
4080 cfg.cls = 'treeview-menu';
4083 cfg.cls += ' active';
4088 anc.href = this.href || cfg.cn[0].href ;
4089 ctag.html = this.html || cfg.cn[0].html ;
4093 initEvents: function()
4095 if (this.parent().type == 'treeview') {
4096 this.el.select('a').on('click', this.onClick, this);
4100 this.menu.parentType = this.xtype;
4101 this.menu.triggerEl = this.el;
4102 this.menu = this.addxtype(Roo.apply({}, this.menu));
4106 onClick : function(e)
4108 Roo.log('item on click ');
4110 if(this.preventDefault){
4113 //this.parent().hideMenuItems();
4115 this.fireEvent('click', this, e);
4134 * @class Roo.bootstrap.MenuSeparator
4135 * @extends Roo.bootstrap.Component
4136 * Bootstrap MenuSeparator class
4139 * Create a new MenuItem
4140 * @param {Object} config The config object
4144 Roo.bootstrap.MenuSeparator = function(config){
4145 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4148 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4150 getAutoCreate : function(){
4169 * @class Roo.bootstrap.Modal
4170 * @extends Roo.bootstrap.Component
4171 * Bootstrap Modal class
4172 * @cfg {String} title Title of dialog
4173 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4174 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4175 * @cfg {Boolean} specificTitle default false
4176 * @cfg {Array} buttons Array of buttons or standard button set..
4177 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4178 * @cfg {Boolean} animate default true
4179 * @cfg {Boolean} allow_close default true
4180 * @cfg {Boolean} fitwindow default false
4181 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4182 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4183 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4184 * @cfg {String} size (sm|lg|xl) default empty
4185 * @cfg {Number} max_width set the max width of modal
4186 * @cfg {Boolean} editableTitle can the title be edited
4191 * Create a new Modal Dialog
4192 * @param {Object} config The config object
4195 Roo.bootstrap.Modal = function(config){
4196 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4201 * The raw btnclick event for the button
4202 * @param {Roo.EventObject} e
4207 * Fire when dialog resize
4208 * @param {Roo.bootstrap.Modal} this
4209 * @param {Roo.EventObject} e
4213 * @event titlechanged
4214 * Fire when the editable title has been changed
4215 * @param {Roo.bootstrap.Modal} this
4216 * @param {Roo.EventObject} value
4218 "titlechanged" : true
4221 this.buttons = this.buttons || [];
4224 this.tmpl = Roo.factory(this.tmpl);
4229 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4231 title : 'test dialog',
4241 specificTitle: false,
4243 buttonPosition: 'right',
4265 editableTitle : false,
4267 onRender : function(ct, position)
4269 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4272 var cfg = Roo.apply({}, this.getAutoCreate());
4275 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4277 //if (!cfg.name.length) {
4281 cfg.cls += ' ' + this.cls;
4284 cfg.style = this.style;
4286 this.el = Roo.get(document.body).createChild(cfg, position);
4288 //var type = this.el.dom.type;
4291 if(this.tabIndex !== undefined){
4292 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4295 this.dialogEl = this.el.select('.modal-dialog',true).first();
4296 this.bodyEl = this.el.select('.modal-body',true).first();
4297 this.closeEl = this.el.select('.modal-header .close', true).first();
4298 this.headerEl = this.el.select('.modal-header',true).first();
4299 this.titleEl = this.el.select('.modal-title',true).first();
4300 this.footerEl = this.el.select('.modal-footer',true).first();
4302 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4304 //this.el.addClass("x-dlg-modal");
4306 if (this.buttons.length) {
4307 Roo.each(this.buttons, function(bb) {
4308 var b = Roo.apply({}, bb);
4309 b.xns = b.xns || Roo.bootstrap;
4310 b.xtype = b.xtype || 'Button';
4311 if (typeof(b.listeners) == 'undefined') {
4312 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4315 var btn = Roo.factory(b);
4317 btn.render(this.getButtonContainer());
4321 // render the children.
4324 if(typeof(this.items) != 'undefined'){
4325 var items = this.items;
4328 for(var i =0;i < items.length;i++) {
4329 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4333 this.items = nitems;
4335 // where are these used - they used to be body/close/footer
4339 //this.el.addClass([this.fieldClass, this.cls]);
4343 getAutoCreate : function()
4345 // we will default to modal-body-overflow - might need to remove or make optional later.
4347 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4348 html : this.html || ''
4353 cls : 'modal-title',
4357 if(this.specificTitle){ // WTF is this?
4362 if (this.allow_close && Roo.bootstrap.version == 3) {
4372 if (this.editableTitle) {
4374 cls: 'form-control roo-editable-title d-none',
4380 if (this.allow_close && Roo.bootstrap.version == 4) {
4390 if(this.size.length){
4391 size = 'modal-' + this.size;
4394 var footer = Roo.bootstrap.version == 3 ?
4396 cls : 'modal-footer',
4400 cls: 'btn-' + this.buttonPosition
4405 { // BS4 uses mr-auto on left buttons....
4406 cls : 'modal-footer'
4417 cls: "modal-dialog " + size,
4420 cls : "modal-content",
4423 cls : 'modal-header',
4438 modal.cls += ' fade';
4444 getChildContainer : function() {
4449 getButtonContainer : function() {
4451 return Roo.bootstrap.version == 4 ?
4452 this.el.select('.modal-footer',true).first()
4453 : this.el.select('.modal-footer div',true).first();
4456 initEvents : function()
4458 if (this.allow_close) {
4459 this.closeEl.on('click', this.hide, this);
4461 Roo.EventManager.onWindowResize(this.resize, this, true);
4462 if (this.editableTitle) {
4463 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4464 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4465 this.headerEditEl.on('keyup', function(e) {
4466 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4467 this.toggleHeaderInput(false)
4470 this.headerEditEl.on('blur', function(e) {
4471 this.toggleHeaderInput(false)
4480 this.maskEl.setSize(
4481 Roo.lib.Dom.getViewWidth(true),
4482 Roo.lib.Dom.getViewHeight(true)
4485 if (this.fitwindow) {
4487 this.dialogEl.setStyle( { 'max-width' : '100%' });
4489 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4490 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4495 if(this.max_width !== 0) {
4497 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4500 this.setSize(w, this.height);
4504 if(this.max_height) {
4505 this.setSize(w,Math.min(
4507 Roo.lib.Dom.getViewportHeight(true) - 60
4513 if(!this.fit_content) {
4514 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4518 this.setSize(w, Math.min(
4520 this.headerEl.getHeight() +
4521 this.footerEl.getHeight() +
4522 this.getChildHeight(this.bodyEl.dom.childNodes),
4523 Roo.lib.Dom.getViewportHeight(true) - 60)
4529 setSize : function(w,h)
4540 if (!this.rendered) {
4543 this.toggleHeaderInput(false);
4544 //this.el.setStyle('display', 'block');
4545 this.el.removeClass('hideing');
4546 this.el.dom.style.display='block';
4548 Roo.get(document.body).addClass('modal-open');
4550 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4553 this.el.addClass('show');
4554 this.el.addClass('in');
4557 this.el.addClass('show');
4558 this.el.addClass('in');
4561 // not sure how we can show data in here..
4563 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4566 Roo.get(document.body).addClass("x-body-masked");
4568 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4569 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4570 this.maskEl.dom.style.display = 'block';
4571 this.maskEl.addClass('show');
4576 this.fireEvent('show', this);
4578 // set zindex here - otherwise it appears to be ignored...
4579 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4582 this.items.forEach( function(e) {
4583 e.layout ? e.layout() : false;
4591 if(this.fireEvent("beforehide", this) !== false){
4593 this.maskEl.removeClass('show');
4595 this.maskEl.dom.style.display = '';
4596 Roo.get(document.body).removeClass("x-body-masked");
4597 this.el.removeClass('in');
4598 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4600 if(this.animate){ // why
4601 this.el.addClass('hideing');
4602 this.el.removeClass('show');
4604 if (!this.el.hasClass('hideing')) {
4605 return; // it's been shown again...
4608 this.el.dom.style.display='';
4610 Roo.get(document.body).removeClass('modal-open');
4611 this.el.removeClass('hideing');
4615 this.el.removeClass('show');
4616 this.el.dom.style.display='';
4617 Roo.get(document.body).removeClass('modal-open');
4620 this.fireEvent('hide', this);
4623 isVisible : function()
4626 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4630 addButton : function(str, cb)
4634 var b = Roo.apply({}, { html : str } );
4635 b.xns = b.xns || Roo.bootstrap;
4636 b.xtype = b.xtype || 'Button';
4637 if (typeof(b.listeners) == 'undefined') {
4638 b.listeners = { click : cb.createDelegate(this) };
4641 var btn = Roo.factory(b);
4643 btn.render(this.getButtonContainer());
4649 setDefaultButton : function(btn)
4651 //this.el.select('.modal-footer').()
4654 resizeTo: function(w,h)
4656 this.dialogEl.setWidth(w);
4658 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4660 this.bodyEl.setHeight(h - diff);
4662 this.fireEvent('resize', this);
4665 setContentSize : function(w, h)
4669 onButtonClick: function(btn,e)
4672 this.fireEvent('btnclick', btn.name, e);
4675 * Set the title of the Dialog
4676 * @param {String} str new Title
4678 setTitle: function(str) {
4679 this.titleEl.dom.innerHTML = str;
4683 * Set the body of the Dialog
4684 * @param {String} str new Title
4686 setBody: function(str) {
4687 this.bodyEl.dom.innerHTML = str;
4690 * Set the body of the Dialog using the template
4691 * @param {Obj} data - apply this data to the template and replace the body contents.
4693 applyBody: function(obj)
4696 Roo.log("Error - using apply Body without a template");
4699 this.tmpl.overwrite(this.bodyEl, obj);
4702 getChildHeight : function(child_nodes)
4706 child_nodes.length == 0
4711 var child_height = 0;
4713 for(var i = 0; i < child_nodes.length; i++) {
4716 * for modal with tabs...
4717 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4719 var layout_childs = child_nodes[i].childNodes;
4721 for(var j = 0; j < layout_childs.length; j++) {
4723 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4725 var layout_body_childs = layout_childs[j].childNodes;
4727 for(var k = 0; k < layout_body_childs.length; k++) {
4729 if(layout_body_childs[k].classList.contains('navbar')) {
4730 child_height += layout_body_childs[k].offsetHeight;
4734 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4736 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4738 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4740 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4741 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4756 child_height += child_nodes[i].offsetHeight;
4757 // Roo.log(child_nodes[i].offsetHeight);
4760 return child_height;
4762 toggleHeaderInput : function(is_edit)
4764 if (!this.editableTitle) {
4765 return; // not editable.
4767 if (is_edit && this.is_header_editing) {
4768 return; // already editing..
4772 this.headerEditEl.dom.value = this.title;
4773 this.headerEditEl.removeClass('d-none');
4774 this.headerEditEl.dom.focus();
4775 this.titleEl.addClass('d-none');
4777 this.is_header_editing = true;
4780 // flip back to not editing.
4781 this.title = this.headerEditEl.dom.value;
4782 this.headerEditEl.addClass('d-none');
4783 this.titleEl.removeClass('d-none');
4784 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4785 this.is_header_editing = false;
4786 this.fireEvent('titlechanged', this, this.title);
4795 Roo.apply(Roo.bootstrap.Modal, {
4797 * Button config that displays a single OK button
4806 * Button config that displays Yes and No buttons
4822 * Button config that displays OK and Cancel buttons
4837 * Button config that displays Yes, No and Cancel buttons
4862 * messagebox - can be used as a replace
4866 * @class Roo.MessageBox
4867 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4871 Roo.Msg.alert('Status', 'Changes saved successfully.');
4873 // Prompt for user data:
4874 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4876 // process text value...
4880 // Show a dialog using config options:
4882 title:'Save Changes?',
4883 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4884 buttons: Roo.Msg.YESNOCANCEL,
4891 Roo.bootstrap.MessageBox = function(){
4892 var dlg, opt, mask, waitTimer;
4893 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4894 var buttons, activeTextEl, bwidth;
4898 var handleButton = function(button){
4900 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4904 var handleHide = function(){
4906 dlg.el.removeClass(opt.cls);
4909 // Roo.TaskMgr.stop(waitTimer);
4910 // waitTimer = null;
4915 var updateButtons = function(b){
4918 buttons["ok"].hide();
4919 buttons["cancel"].hide();
4920 buttons["yes"].hide();
4921 buttons["no"].hide();
4922 dlg.footerEl.hide();
4926 dlg.footerEl.show();
4927 for(var k in buttons){
4928 if(typeof buttons[k] != "function"){
4931 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4932 width += buttons[k].el.getWidth()+15;
4942 var handleEsc = function(d, k, e){
4943 if(opt && opt.closable !== false){
4953 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4954 * @return {Roo.BasicDialog} The BasicDialog element
4956 getDialog : function(){
4958 dlg = new Roo.bootstrap.Modal( {
4961 //constraintoviewport:false,
4963 //collapsible : false,
4968 //buttonAlign:"center",
4969 closeClick : function(){
4970 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4973 handleButton("cancel");
4978 dlg.on("hide", handleHide);
4980 //dlg.addKeyListener(27, handleEsc);
4982 this.buttons = buttons;
4983 var bt = this.buttonText;
4984 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4985 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4986 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4987 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4989 bodyEl = dlg.bodyEl.createChild({
4991 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4992 '<textarea class="roo-mb-textarea"></textarea>' +
4993 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4995 msgEl = bodyEl.dom.firstChild;
4996 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4997 textboxEl.enableDisplayMode();
4998 textboxEl.addKeyListener([10,13], function(){
4999 if(dlg.isVisible() && opt && opt.buttons){
5002 }else if(opt.buttons.yes){
5003 handleButton("yes");
5007 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5008 textareaEl.enableDisplayMode();
5009 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5010 progressEl.enableDisplayMode();
5012 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5013 var pf = progressEl.dom.firstChild;
5015 pp = Roo.get(pf.firstChild);
5016 pp.setHeight(pf.offsetHeight);
5024 * Updates the message box body text
5025 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5026 * the XHTML-compliant non-breaking space character '&#160;')
5027 * @return {Roo.MessageBox} This message box
5029 updateText : function(text)
5031 if(!dlg.isVisible() && !opt.width){
5032 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5033 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5035 msgEl.innerHTML = text || ' ';
5037 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5038 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5040 Math.min(opt.width || cw , this.maxWidth),
5041 Math.max(opt.minWidth || this.minWidth, bwidth)
5044 activeTextEl.setWidth(w);
5046 if(dlg.isVisible()){
5047 dlg.fixedcenter = false;
5049 // to big, make it scroll. = But as usual stupid IE does not support
5052 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5053 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5054 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5056 bodyEl.dom.style.height = '';
5057 bodyEl.dom.style.overflowY = '';
5060 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5062 bodyEl.dom.style.overflowX = '';
5065 dlg.setContentSize(w, bodyEl.getHeight());
5066 if(dlg.isVisible()){
5067 dlg.fixedcenter = true;
5073 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5074 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5075 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5076 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5077 * @return {Roo.MessageBox} This message box
5079 updateProgress : function(value, text){
5081 this.updateText(text);
5084 if (pp) { // weird bug on my firefox - for some reason this is not defined
5085 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5086 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5092 * Returns true if the message box is currently displayed
5093 * @return {Boolean} True if the message box is visible, else false
5095 isVisible : function(){
5096 return dlg && dlg.isVisible();
5100 * Hides the message box if it is displayed
5103 if(this.isVisible()){
5109 * Displays a new message box, or reinitializes an existing message box, based on the config options
5110 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5111 * The following config object properties are supported:
5113 Property Type Description
5114 ---------- --------------- ------------------------------------------------------------------------------------
5115 animEl String/Element An id or Element from which the message box should animate as it opens and
5116 closes (defaults to undefined)
5117 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5118 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5119 closable Boolean False to hide the top-right close button (defaults to true). Note that
5120 progress and wait dialogs will ignore this property and always hide the
5121 close button as they can only be closed programmatically.
5122 cls String A custom CSS class to apply to the message box element
5123 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5124 displayed (defaults to 75)
5125 fn Function A callback function to execute after closing the dialog. The arguments to the
5126 function will be btn (the name of the button that was clicked, if applicable,
5127 e.g. "ok"), and text (the value of the active text field, if applicable).
5128 Progress and wait dialogs will ignore this option since they do not respond to
5129 user actions and can only be closed programmatically, so any required function
5130 should be called by the same code after it closes the dialog.
5131 icon String A CSS class that provides a background image to be used as an icon for
5132 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5133 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5134 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5135 modal Boolean False to allow user interaction with the page while the message box is
5136 displayed (defaults to true)
5137 msg String A string that will replace the existing message box body text (defaults
5138 to the XHTML-compliant non-breaking space character ' ')
5139 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5140 progress Boolean True to display a progress bar (defaults to false)
5141 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5142 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5143 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5144 title String The title text
5145 value String The string value to set into the active textbox element if displayed
5146 wait Boolean True to display a progress bar (defaults to false)
5147 width Number The width of the dialog in pixels
5154 msg: 'Please enter your address:',
5156 buttons: Roo.MessageBox.OKCANCEL,
5159 animEl: 'addAddressBtn'
5162 * @param {Object} config Configuration options
5163 * @return {Roo.MessageBox} This message box
5165 show : function(options)
5168 // this causes nightmares if you show one dialog after another
5169 // especially on callbacks..
5171 if(this.isVisible()){
5174 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5175 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5176 Roo.log("New Dialog Message:" + options.msg )
5177 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5178 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5181 var d = this.getDialog();
5183 d.setTitle(opt.title || " ");
5184 d.closeEl.setDisplayed(opt.closable !== false);
5185 activeTextEl = textboxEl;
5186 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5191 textareaEl.setHeight(typeof opt.multiline == "number" ?
5192 opt.multiline : this.defaultTextHeight);
5193 activeTextEl = textareaEl;
5202 progressEl.setDisplayed(opt.progress === true);
5204 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5206 this.updateProgress(0);
5207 activeTextEl.dom.value = opt.value || "";
5209 dlg.setDefaultButton(activeTextEl);
5211 var bs = opt.buttons;
5215 }else if(bs && bs.yes){
5216 db = buttons["yes"];
5218 dlg.setDefaultButton(db);
5220 bwidth = updateButtons(opt.buttons);
5221 this.updateText(opt.msg);
5223 d.el.addClass(opt.cls);
5225 d.proxyDrag = opt.proxyDrag === true;
5226 d.modal = opt.modal !== false;
5227 d.mask = opt.modal !== false ? mask : false;
5229 // force it to the end of the z-index stack so it gets a cursor in FF
5230 document.body.appendChild(dlg.el.dom);
5231 d.animateTarget = null;
5232 d.show(options.animEl);
5238 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5239 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5240 * and closing the message box when the process is complete.
5241 * @param {String} title The title bar text
5242 * @param {String} msg The message box body text
5243 * @return {Roo.MessageBox} This message box
5245 progress : function(title, msg){
5252 minWidth: this.minProgressWidth,
5259 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5260 * If a callback function is passed it will be called after the user clicks the button, and the
5261 * id of the button that was clicked will be passed as the only parameter to the callback
5262 * (could also be the top-right close button).
5263 * @param {String} title The title bar text
5264 * @param {String} msg The message box body text
5265 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5266 * @param {Object} scope (optional) The scope of the callback function
5267 * @return {Roo.MessageBox} This message box
5269 alert : function(title, msg, fn, scope)
5284 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5285 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5286 * You are responsible for closing the message box when the process is complete.
5287 * @param {String} msg The message box body text
5288 * @param {String} title (optional) The title bar text
5289 * @return {Roo.MessageBox} This message box
5291 wait : function(msg, title){
5302 waitTimer = Roo.TaskMgr.start({
5304 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5312 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5313 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5314 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5315 * @param {String} title The title bar text
5316 * @param {String} msg The message box body text
5317 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5318 * @param {Object} scope (optional) The scope of the callback function
5319 * @return {Roo.MessageBox} This message box
5321 confirm : function(title, msg, fn, scope){
5325 buttons: this.YESNO,
5334 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5335 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5336 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5337 * (could also be the top-right close button) and the text that was entered will be passed as the two
5338 * parameters to the callback.
5339 * @param {String} title The title bar text
5340 * @param {String} msg The message box body text
5341 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5342 * @param {Object} scope (optional) The scope of the callback function
5343 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5344 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5345 * @return {Roo.MessageBox} This message box
5347 prompt : function(title, msg, fn, scope, multiline){
5351 buttons: this.OKCANCEL,
5356 multiline: multiline,
5363 * Button config that displays a single OK button
5368 * Button config that displays Yes and No buttons
5371 YESNO : {yes:true, no:true},
5373 * Button config that displays OK and Cancel buttons
5376 OKCANCEL : {ok:true, cancel:true},
5378 * Button config that displays Yes, No and Cancel buttons
5381 YESNOCANCEL : {yes:true, no:true, cancel:true},
5384 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5387 defaultTextHeight : 75,
5389 * The maximum width in pixels of the message box (defaults to 600)
5394 * The minimum width in pixels of the message box (defaults to 100)
5399 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5400 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5403 minProgressWidth : 250,
5405 * An object containing the default button text strings that can be overriden for localized language support.
5406 * Supported properties are: ok, cancel, yes and no.
5407 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5420 * Shorthand for {@link Roo.MessageBox}
5422 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5423 Roo.Msg = Roo.Msg || Roo.MessageBox;
5432 * @class Roo.bootstrap.Navbar
5433 * @extends Roo.bootstrap.Component
5434 * Bootstrap Navbar class
5437 * Create a new Navbar
5438 * @param {Object} config The config object
5442 Roo.bootstrap.Navbar = function(config){
5443 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5447 * @event beforetoggle
5448 * Fire before toggle the menu
5449 * @param {Roo.EventObject} e
5451 "beforetoggle" : true
5455 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5464 getAutoCreate : function(){
5467 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5471 initEvents :function ()
5473 //Roo.log(this.el.select('.navbar-toggle',true));
5474 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5481 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5483 var size = this.el.getSize();
5484 this.maskEl.setSize(size.width, size.height);
5485 this.maskEl.enableDisplayMode("block");
5494 getChildContainer : function()
5496 if (this.el && this.el.select('.collapse').getCount()) {
5497 return this.el.select('.collapse',true).first();
5512 onToggle : function()
5515 if(this.fireEvent('beforetoggle', this) === false){
5518 var ce = this.el.select('.navbar-collapse',true).first();
5520 if (!ce.hasClass('show')) {
5530 * Expand the navbar pulldown
5532 expand : function ()
5535 var ce = this.el.select('.navbar-collapse',true).first();
5536 if (ce.hasClass('collapsing')) {
5539 ce.dom.style.height = '';
5541 ce.addClass('in'); // old...
5542 ce.removeClass('collapse');
5543 ce.addClass('show');
5544 var h = ce.getHeight();
5546 ce.removeClass('show');
5547 // at this point we should be able to see it..
5548 ce.addClass('collapsing');
5550 ce.setHeight(0); // resize it ...
5551 ce.on('transitionend', function() {
5552 //Roo.log('done transition');
5553 ce.removeClass('collapsing');
5554 ce.addClass('show');
5555 ce.removeClass('collapse');
5557 ce.dom.style.height = '';
5558 }, this, { single: true} );
5560 ce.dom.scrollTop = 0;
5563 * Collapse the navbar pulldown
5565 collapse : function()
5567 var ce = this.el.select('.navbar-collapse',true).first();
5569 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5570 // it's collapsed or collapsing..
5573 ce.removeClass('in'); // old...
5574 ce.setHeight(ce.getHeight());
5575 ce.removeClass('show');
5576 ce.addClass('collapsing');
5578 ce.on('transitionend', function() {
5579 ce.dom.style.height = '';
5580 ce.removeClass('collapsing');
5581 ce.addClass('collapse');
5582 }, this, { single: true} );
5602 * @class Roo.bootstrap.NavSimplebar
5603 * @extends Roo.bootstrap.Navbar
5604 * Bootstrap Sidebar class
5606 * @cfg {Boolean} inverse is inverted color
5608 * @cfg {String} type (nav | pills | tabs)
5609 * @cfg {Boolean} arrangement stacked | justified
5610 * @cfg {String} align (left | right) alignment
5612 * @cfg {Boolean} main (true|false) main nav bar? default false
5613 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5615 * @cfg {String} tag (header|footer|nav|div) default is nav
5617 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5621 * Create a new Sidebar
5622 * @param {Object} config The config object
5626 Roo.bootstrap.NavSimplebar = function(config){
5627 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5630 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5646 getAutoCreate : function(){
5650 tag : this.tag || 'div',
5651 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5653 if (['light','white'].indexOf(this.weight) > -1) {
5654 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5656 cfg.cls += ' bg-' + this.weight;
5659 cfg.cls += ' navbar-inverse';
5663 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5665 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5674 cls: 'nav nav-' + this.xtype,
5680 this.type = this.type || 'nav';
5681 if (['tabs','pills'].indexOf(this.type) != -1) {
5682 cfg.cn[0].cls += ' nav-' + this.type
5686 if (this.type!=='nav') {
5687 Roo.log('nav type must be nav/tabs/pills')
5689 cfg.cn[0].cls += ' navbar-nav'
5695 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5696 cfg.cn[0].cls += ' nav-' + this.arrangement;
5700 if (this.align === 'right') {
5701 cfg.cn[0].cls += ' navbar-right';
5726 * navbar-expand-md fixed-top
5730 * @class Roo.bootstrap.NavHeaderbar
5731 * @extends Roo.bootstrap.NavSimplebar
5732 * Bootstrap Sidebar class
5734 * @cfg {String} brand what is brand
5735 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5736 * @cfg {String} brand_href href of the brand
5737 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5738 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5739 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5740 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5743 * Create a new Sidebar
5744 * @param {Object} config The config object
5748 Roo.bootstrap.NavHeaderbar = function(config){
5749 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5753 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5760 desktopCenter : false,
5763 getAutoCreate : function(){
5766 tag: this.nav || 'nav',
5767 cls: 'navbar navbar-expand-md',
5773 if (this.desktopCenter) {
5774 cn.push({cls : 'container', cn : []});
5782 cls: 'navbar-toggle navbar-toggler',
5783 'data-toggle': 'collapse',
5788 html: 'Toggle navigation'
5792 cls: 'icon-bar navbar-toggler-icon'
5805 cn.push( Roo.bootstrap.version == 4 ? btn : {
5807 cls: 'navbar-header',
5816 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5820 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5822 if (['light','white'].indexOf(this.weight) > -1) {
5823 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5825 cfg.cls += ' bg-' + this.weight;
5828 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5829 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5831 // tag can override this..
5833 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5836 if (this.brand !== '') {
5837 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5838 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5840 href: this.brand_href ? this.brand_href : '#',
5841 cls: 'navbar-brand',
5849 cfg.cls += ' main-nav';
5857 getHeaderChildContainer : function()
5859 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5860 return this.el.select('.navbar-header',true).first();
5863 return this.getChildContainer();
5866 getChildContainer : function()
5869 return this.el.select('.roo-navbar-collapse',true).first();
5874 initEvents : function()
5876 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5878 if (this.autohide) {
5883 Roo.get(document).on('scroll',function(e) {
5884 var ns = Roo.get(document).getScroll().top;
5885 var os = prevScroll;
5889 ft.removeClass('slideDown');
5890 ft.addClass('slideUp');
5893 ft.removeClass('slideUp');
5894 ft.addClass('slideDown');
5915 * @class Roo.bootstrap.NavSidebar
5916 * @extends Roo.bootstrap.Navbar
5917 * Bootstrap Sidebar class
5920 * Create a new Sidebar
5921 * @param {Object} config The config object
5925 Roo.bootstrap.NavSidebar = function(config){
5926 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5929 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5931 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5933 getAutoCreate : function(){
5938 cls: 'sidebar sidebar-nav'
5960 * @class Roo.bootstrap.NavGroup
5961 * @extends Roo.bootstrap.Component
5962 * Bootstrap NavGroup class
5963 * @cfg {String} align (left|right)
5964 * @cfg {Boolean} inverse
5965 * @cfg {String} type (nav|pills|tab) default nav
5966 * @cfg {String} navId - reference Id for navbar.
5967 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5970 * Create a new nav group
5971 * @param {Object} config The config object
5974 Roo.bootstrap.NavGroup = function(config){
5975 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5978 Roo.bootstrap.NavGroup.register(this);
5982 * Fires when the active item changes
5983 * @param {Roo.bootstrap.NavGroup} this
5984 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5985 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5992 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6004 getAutoCreate : function()
6006 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6012 if (Roo.bootstrap.version == 4) {
6013 if (['tabs','pills'].indexOf(this.type) != -1) {
6014 cfg.cls += ' nav-' + this.type;
6016 // trying to remove so header bar can right align top?
6017 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6018 // do not use on header bar...
6019 cfg.cls += ' navbar-nav';
6024 if (['tabs','pills'].indexOf(this.type) != -1) {
6025 cfg.cls += ' nav-' + this.type
6027 if (this.type !== 'nav') {
6028 Roo.log('nav type must be nav/tabs/pills')
6030 cfg.cls += ' navbar-nav'
6034 if (this.parent() && this.parent().sidebar) {
6037 cls: 'dashboard-menu sidebar-menu'
6043 if (this.form === true) {
6046 cls: 'navbar-form form-inline'
6048 //nav navbar-right ml-md-auto
6049 if (this.align === 'right') {
6050 cfg.cls += ' navbar-right ml-md-auto';
6052 cfg.cls += ' navbar-left';
6056 if (this.align === 'right') {
6057 cfg.cls += ' navbar-right ml-md-auto';
6059 cfg.cls += ' mr-auto';
6063 cfg.cls += ' navbar-inverse';
6071 * sets the active Navigation item
6072 * @param {Roo.bootstrap.NavItem} the new current navitem
6074 setActiveItem : function(item)
6077 Roo.each(this.navItems, function(v){
6082 v.setActive(false, true);
6089 item.setActive(true, true);
6090 this.fireEvent('changed', this, item, prev);
6095 * gets the active Navigation item
6096 * @return {Roo.bootstrap.NavItem} the current navitem
6098 getActive : function()
6102 Roo.each(this.navItems, function(v){
6113 indexOfNav : function()
6117 Roo.each(this.navItems, function(v,i){
6128 * adds a Navigation item
6129 * @param {Roo.bootstrap.NavItem} the navitem to add
6131 addItem : function(cfg)
6133 if (this.form && Roo.bootstrap.version == 4) {
6136 var cn = new Roo.bootstrap.NavItem(cfg);
6138 cn.parentId = this.id;
6139 cn.onRender(this.el, null);
6143 * register a Navigation item
6144 * @param {Roo.bootstrap.NavItem} the navitem to add
6146 register : function(item)
6148 this.navItems.push( item);
6149 item.navId = this.navId;
6154 * clear all the Navigation item
6157 clearAll : function()
6160 this.el.dom.innerHTML = '';
6163 getNavItem: function(tabId)
6166 Roo.each(this.navItems, function(e) {
6167 if (e.tabId == tabId) {
6177 setActiveNext : function()
6179 var i = this.indexOfNav(this.getActive());
6180 if (i > this.navItems.length) {
6183 this.setActiveItem(this.navItems[i+1]);
6185 setActivePrev : function()
6187 var i = this.indexOfNav(this.getActive());
6191 this.setActiveItem(this.navItems[i-1]);
6193 clearWasActive : function(except) {
6194 Roo.each(this.navItems, function(e) {
6195 if (e.tabId != except.tabId && e.was_active) {
6196 e.was_active = false;
6203 getWasActive : function ()
6206 Roo.each(this.navItems, function(e) {
6221 Roo.apply(Roo.bootstrap.NavGroup, {
6225 * register a Navigation Group
6226 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6228 register : function(navgrp)
6230 this.groups[navgrp.navId] = navgrp;
6234 * fetch a Navigation Group based on the navigation ID
6235 * @param {string} the navgroup to add
6236 * @returns {Roo.bootstrap.NavGroup} the navgroup
6238 get: function(navId) {
6239 if (typeof(this.groups[navId]) == 'undefined') {
6241 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6243 return this.groups[navId] ;
6258 * @class Roo.bootstrap.NavItem
6259 * @extends Roo.bootstrap.Component
6260 * Bootstrap Navbar.NavItem class
6261 * @cfg {String} href link to
6262 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6263 * @cfg {Boolean} button_outline show and outlined button
6264 * @cfg {String} html content of button
6265 * @cfg {String} badge text inside badge
6266 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6267 * @cfg {String} glyphicon DEPRICATED - use fa
6268 * @cfg {String} icon DEPRICATED - use fa
6269 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6270 * @cfg {Boolean} active Is item active
6271 * @cfg {Boolean} disabled Is item disabled
6272 * @cfg {String} linkcls Link Class
6273 * @cfg {Boolean} preventDefault (true | false) default false
6274 * @cfg {String} tabId the tab that this item activates.
6275 * @cfg {String} tagtype (a|span) render as a href or span?
6276 * @cfg {Boolean} animateRef (true|false) link to element default false
6279 * Create a new Navbar Item
6280 * @param {Object} config The config object
6282 Roo.bootstrap.NavItem = function(config){
6283 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6288 * The raw click event for the entire grid.
6289 * @param {Roo.EventObject} e
6294 * Fires when the active item active state changes
6295 * @param {Roo.bootstrap.NavItem} this
6296 * @param {boolean} state the new state
6302 * Fires when scroll to element
6303 * @param {Roo.bootstrap.NavItem} this
6304 * @param {Object} options
6305 * @param {Roo.EventObject} e
6313 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6322 preventDefault : false,
6330 button_outline : false,
6334 getAutoCreate : function(){
6341 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6344 cfg.cls += ' active' ;
6346 if (this.disabled) {
6347 cfg.cls += ' disabled';
6351 if (this.button_weight.length) {
6352 cfg.tag = this.href ? 'a' : 'button';
6353 cfg.html = this.html || '';
6354 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6356 cfg.href = this.href;
6359 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6361 cfg.cls += " nav-html";
6364 // menu .. should add dropdown-menu class - so no need for carat..
6366 if (this.badge !== '') {
6368 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6373 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6377 href : this.href || "#",
6378 html: this.html || '',
6382 if (this.tagtype == 'a') {
6383 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6387 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6388 } else if (this.fa) {
6389 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6390 } else if(this.glyphicon) {
6391 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6393 cfg.cn[0].cls += " nav-html";
6397 cfg.cn[0].html += " <span class='caret'></span>";
6401 if (this.badge !== '') {
6402 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6410 onRender : function(ct, position)
6412 // Roo.log("Call onRender: " + this.xtype);
6413 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6417 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6418 this.navLink = this.el.select('.nav-link',true).first();
6419 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6424 initEvents: function()
6426 if (typeof (this.menu) != 'undefined') {
6427 this.menu.parentType = this.xtype;
6428 this.menu.triggerEl = this.el;
6429 this.menu = this.addxtype(Roo.apply({}, this.menu));
6432 this.el.on('click', this.onClick, this);
6434 //if(this.tagtype == 'span'){
6435 // this.el.select('span',true).on('click', this.onClick, this);
6438 // at this point parent should be available..
6439 this.parent().register(this);
6442 onClick : function(e)
6444 if (e.getTarget('.dropdown-menu-item')) {
6445 // did you click on a menu itemm.... - then don't trigger onclick..
6450 this.preventDefault ||
6453 Roo.log("NavItem - prevent Default?");
6457 if (this.disabled) {
6461 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6462 if (tg && tg.transition) {
6463 Roo.log("waiting for the transitionend");
6469 //Roo.log("fire event clicked");
6470 if(this.fireEvent('click', this, e) === false){
6474 if(this.tagtype == 'span'){
6478 //Roo.log(this.href);
6479 var ael = this.el.select('a',true).first();
6482 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6483 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6484 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6485 return; // ignore... - it's a 'hash' to another page.
6487 Roo.log("NavItem - prevent Default?");
6489 this.scrollToElement(e);
6493 var p = this.parent();
6495 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6496 if (typeof(p.setActiveItem) !== 'undefined') {
6497 p.setActiveItem(this);
6501 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6502 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6503 // remove the collapsed menu expand...
6504 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6508 isActive: function () {
6511 setActive : function(state, fire, is_was_active)
6513 if (this.active && !state && this.navId) {
6514 this.was_active = true;
6515 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6517 nv.clearWasActive(this);
6521 this.active = state;
6524 this.el.removeClass('active');
6525 this.navLink ? this.navLink.removeClass('active') : false;
6526 } else if (!this.el.hasClass('active')) {
6528 this.el.addClass('active');
6529 if (Roo.bootstrap.version == 4 && this.navLink ) {
6530 this.navLink.addClass('active');
6535 this.fireEvent('changed', this, state);
6538 // show a panel if it's registered and related..
6540 if (!this.navId || !this.tabId || !state || is_was_active) {
6544 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6548 var pan = tg.getPanelByName(this.tabId);
6552 // if we can not flip to new panel - go back to old nav highlight..
6553 if (false == tg.showPanel(pan)) {
6554 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6556 var onav = nv.getWasActive();
6558 onav.setActive(true, false, true);
6567 // this should not be here...
6568 setDisabled : function(state)
6570 this.disabled = state;
6572 this.el.removeClass('disabled');
6573 } else if (!this.el.hasClass('disabled')) {
6574 this.el.addClass('disabled');
6580 * Fetch the element to display the tooltip on.
6581 * @return {Roo.Element} defaults to this.el
6583 tooltipEl : function()
6585 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6588 scrollToElement : function(e)
6590 var c = document.body;
6593 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6595 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6596 c = document.documentElement;
6599 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6605 var o = target.calcOffsetsTo(c);
6612 this.fireEvent('scrollto', this, options, e);
6614 Roo.get(c).scrollTo('top', options.value, true);
6619 * Set the HTML (text content) of the item
6620 * @param {string} html content for the nav item
6622 setHtml : function(html)
6625 this.htmlEl.dom.innerHTML = html;
6637 * <span> icon </span>
6638 * <span> text </span>
6639 * <span>badge </span>
6643 * @class Roo.bootstrap.NavSidebarItem
6644 * @extends Roo.bootstrap.NavItem
6645 * Bootstrap Navbar.NavSidebarItem class
6646 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6647 * {Boolean} open is the menu open
6648 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6649 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6650 * {String} buttonSize (sm|md|lg)the extra classes for the button
6651 * {Boolean} showArrow show arrow next to the text (default true)
6653 * Create a new Navbar Button
6654 * @param {Object} config The config object
6656 Roo.bootstrap.NavSidebarItem = function(config){
6657 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6662 * The raw click event for the entire grid.
6663 * @param {Roo.EventObject} e
6668 * Fires when the active item active state changes
6669 * @param {Roo.bootstrap.NavSidebarItem} this
6670 * @param {boolean} state the new state
6678 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6680 badgeWeight : 'default',
6686 buttonWeight : 'default',
6692 getAutoCreate : function(){
6697 href : this.href || '#',
6703 if(this.buttonView){
6706 href : this.href || '#',
6707 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6720 cfg.cls += ' active';
6723 if (this.disabled) {
6724 cfg.cls += ' disabled';
6727 cfg.cls += ' open x-open';
6730 if (this.glyphicon || this.icon) {
6731 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6732 a.cn.push({ tag : 'i', cls : c }) ;
6735 if(!this.buttonView){
6738 html : this.html || ''
6745 if (this.badge !== '') {
6746 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6752 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6755 a.cls += ' dropdown-toggle treeview' ;
6761 initEvents : function()
6763 if (typeof (this.menu) != 'undefined') {
6764 this.menu.parentType = this.xtype;
6765 this.menu.triggerEl = this.el;
6766 this.menu = this.addxtype(Roo.apply({}, this.menu));
6769 this.el.on('click', this.onClick, this);
6771 if(this.badge !== ''){
6772 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6777 onClick : function(e)
6784 if(this.preventDefault){
6788 this.fireEvent('click', this, e);
6791 disable : function()
6793 this.setDisabled(true);
6798 this.setDisabled(false);
6801 setDisabled : function(state)
6803 if(this.disabled == state){
6807 this.disabled = state;
6810 this.el.addClass('disabled');
6814 this.el.removeClass('disabled');
6819 setActive : function(state)
6821 if(this.active == state){
6825 this.active = state;
6828 this.el.addClass('active');
6832 this.el.removeClass('active');
6837 isActive: function ()
6842 setBadge : function(str)
6848 this.badgeEl.dom.innerHTML = str;
6863 Roo.namespace('Roo.bootstrap.breadcrumb');
6867 * @class Roo.bootstrap.breadcrumb.Nav
6868 * @extends Roo.bootstrap.Component
6869 * Bootstrap Breadcrumb Nav Class
6871 * @children Roo.bootstrap.breadcrumb.Item
6874 * Create a new breadcrumb.Nav
6875 * @param {Object} config The config object
6879 Roo.bootstrap.breadcrumb.Nav = function(config){
6880 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6885 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6887 getAutoCreate : function()
6904 initEvents: function()
6906 this.olEl = this.el.select('ol',true).first();
6908 getChildContainer : function()
6924 * @class Roo.bootstrap.breadcrumb.Nav
6925 * @extends Roo.bootstrap.Component
6926 * Bootstrap Breadcrumb Nav Class
6928 * @children Roo.bootstrap.breadcrumb.Component
6929 * @cfg {String} html the content of the link.
6930 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6931 * @cfg {Boolean} active is it active
6935 * Create a new breadcrumb.Nav
6936 * @param {Object} config The config object
6939 Roo.bootstrap.breadcrumb.Item = function(config){
6940 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6945 * The img click event for the img.
6946 * @param {Roo.EventObject} e
6953 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6958 getAutoCreate : function()
6963 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6965 if (this.href !== false) {
6972 cfg.html = this.html;
6978 initEvents: function()
6981 this.el.select('a', true).first().on('click',this.onClick, this)
6985 onClick : function(e)
6988 this.fireEvent('click',this, e);
7001 * @class Roo.bootstrap.Row
7002 * @extends Roo.bootstrap.Component
7003 * Bootstrap Row class (contains columns...)
7007 * @param {Object} config The config object
7010 Roo.bootstrap.Row = function(config){
7011 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7014 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7016 getAutoCreate : function(){
7035 * @class Roo.bootstrap.Pagination
7036 * @extends Roo.bootstrap.Component
7037 * Bootstrap Pagination class
7038 * @cfg {String} size xs | sm | md | lg
7039 * @cfg {Boolean} inverse false | true
7042 * Create a new Pagination
7043 * @param {Object} config The config object
7046 Roo.bootstrap.Pagination = function(config){
7047 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7050 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7056 getAutoCreate : function(){
7062 cfg.cls += ' inverse';
7068 cfg.cls += " " + this.cls;
7086 * @class Roo.bootstrap.PaginationItem
7087 * @extends Roo.bootstrap.Component
7088 * Bootstrap PaginationItem class
7089 * @cfg {String} html text
7090 * @cfg {String} href the link
7091 * @cfg {Boolean} preventDefault (true | false) default true
7092 * @cfg {Boolean} active (true | false) default false
7093 * @cfg {Boolean} disabled default false
7097 * Create a new PaginationItem
7098 * @param {Object} config The config object
7102 Roo.bootstrap.PaginationItem = function(config){
7103 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7108 * The raw click event for the entire grid.
7109 * @param {Roo.EventObject} e
7115 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7119 preventDefault: true,
7124 getAutoCreate : function(){
7130 href : this.href ? this.href : '#',
7131 html : this.html ? this.html : ''
7141 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7145 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7151 initEvents: function() {
7153 this.el.on('click', this.onClick, this);
7156 onClick : function(e)
7158 Roo.log('PaginationItem on click ');
7159 if(this.preventDefault){
7167 this.fireEvent('click', this, e);
7183 * @class Roo.bootstrap.Slider
7184 * @extends Roo.bootstrap.Component
7185 * Bootstrap Slider class
7188 * Create a new Slider
7189 * @param {Object} config The config object
7192 Roo.bootstrap.Slider = function(config){
7193 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7196 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7198 getAutoCreate : function(){
7202 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7206 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7218 * Ext JS Library 1.1.1
7219 * Copyright(c) 2006-2007, Ext JS, LLC.
7221 * Originally Released Under LGPL - original licence link has changed is not relivant.
7224 * <script type="text/javascript">
7229 * @class Roo.grid.ColumnModel
7230 * @extends Roo.util.Observable
7231 * This is the default implementation of a ColumnModel used by the Grid. It defines
7232 * the columns in the grid.
7235 var colModel = new Roo.grid.ColumnModel([
7236 {header: "Ticker", width: 60, sortable: true, locked: true},
7237 {header: "Company Name", width: 150, sortable: true},
7238 {header: "Market Cap.", width: 100, sortable: true},
7239 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7240 {header: "Employees", width: 100, sortable: true, resizable: false}
7245 * The config options listed for this class are options which may appear in each
7246 * individual column definition.
7247 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7249 * @param {Object} config An Array of column config objects. See this class's
7250 * config objects for details.
7252 Roo.grid.ColumnModel = function(config){
7254 * The config passed into the constructor
7256 this.config = config;
7259 // if no id, create one
7260 // if the column does not have a dataIndex mapping,
7261 // map it to the order it is in the config
7262 for(var i = 0, len = config.length; i < len; i++){
7264 if(typeof c.dataIndex == "undefined"){
7267 if(typeof c.renderer == "string"){
7268 c.renderer = Roo.util.Format[c.renderer];
7270 if(typeof c.id == "undefined"){
7273 if(c.editor && c.editor.xtype){
7274 c.editor = Roo.factory(c.editor, Roo.grid);
7276 if(c.editor && c.editor.isFormField){
7277 c.editor = new Roo.grid.GridEditor(c.editor);
7279 this.lookup[c.id] = c;
7283 * The width of columns which have no width specified (defaults to 100)
7286 this.defaultWidth = 100;
7289 * Default sortable of columns which have no sortable specified (defaults to false)
7292 this.defaultSortable = false;
7296 * @event widthchange
7297 * Fires when the width of a column changes.
7298 * @param {ColumnModel} this
7299 * @param {Number} columnIndex The column index
7300 * @param {Number} newWidth The new width
7302 "widthchange": true,
7304 * @event headerchange
7305 * Fires when the text of a header changes.
7306 * @param {ColumnModel} this
7307 * @param {Number} columnIndex The column index
7308 * @param {Number} newText The new header text
7310 "headerchange": true,
7312 * @event hiddenchange
7313 * Fires when a column is hidden or "unhidden".
7314 * @param {ColumnModel} this
7315 * @param {Number} columnIndex The column index
7316 * @param {Boolean} hidden true if hidden, false otherwise
7318 "hiddenchange": true,
7320 * @event columnmoved
7321 * Fires when a column is moved.
7322 * @param {ColumnModel} this
7323 * @param {Number} oldIndex
7324 * @param {Number} newIndex
7326 "columnmoved" : true,
7328 * @event columlockchange
7329 * Fires when a column's locked state is changed
7330 * @param {ColumnModel} this
7331 * @param {Number} colIndex
7332 * @param {Boolean} locked true if locked
7334 "columnlockchange" : true
7336 Roo.grid.ColumnModel.superclass.constructor.call(this);
7338 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7340 * @cfg {String} header The header text to display in the Grid view.
7343 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7344 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7345 * specified, the column's index is used as an index into the Record's data Array.
7348 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7349 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7352 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7353 * Defaults to the value of the {@link #defaultSortable} property.
7354 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7357 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7360 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7363 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7366 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7369 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7370 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7371 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7372 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7375 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7378 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7381 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7384 * @cfg {String} cursor (Optional)
7387 * @cfg {String} tooltip (Optional)
7390 * @cfg {Number} xs (Optional)
7393 * @cfg {Number} sm (Optional)
7396 * @cfg {Number} md (Optional)
7399 * @cfg {Number} lg (Optional)
7402 * Returns the id of the column at the specified index.
7403 * @param {Number} index The column index
7404 * @return {String} the id
7406 getColumnId : function(index){
7407 return this.config[index].id;
7411 * Returns the column for a specified id.
7412 * @param {String} id The column id
7413 * @return {Object} the column
7415 getColumnById : function(id){
7416 return this.lookup[id];
7421 * Returns the column for a specified dataIndex.
7422 * @param {String} dataIndex The column dataIndex
7423 * @return {Object|Boolean} the column or false if not found
7425 getColumnByDataIndex: function(dataIndex){
7426 var index = this.findColumnIndex(dataIndex);
7427 return index > -1 ? this.config[index] : false;
7431 * Returns the index for a specified column id.
7432 * @param {String} id The column id
7433 * @return {Number} the index, or -1 if not found
7435 getIndexById : function(id){
7436 for(var i = 0, len = this.config.length; i < len; i++){
7437 if(this.config[i].id == id){
7445 * Returns the index for a specified column dataIndex.
7446 * @param {String} dataIndex The column dataIndex
7447 * @return {Number} the index, or -1 if not found
7450 findColumnIndex : function(dataIndex){
7451 for(var i = 0, len = this.config.length; i < len; i++){
7452 if(this.config[i].dataIndex == dataIndex){
7460 moveColumn : function(oldIndex, newIndex){
7461 var c = this.config[oldIndex];
7462 this.config.splice(oldIndex, 1);
7463 this.config.splice(newIndex, 0, c);
7464 this.dataMap = null;
7465 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7468 isLocked : function(colIndex){
7469 return this.config[colIndex].locked === true;
7472 setLocked : function(colIndex, value, suppressEvent){
7473 if(this.isLocked(colIndex) == value){
7476 this.config[colIndex].locked = value;
7478 this.fireEvent("columnlockchange", this, colIndex, value);
7482 getTotalLockedWidth : function(){
7484 for(var i = 0; i < this.config.length; i++){
7485 if(this.isLocked(i) && !this.isHidden(i)){
7486 this.totalWidth += this.getColumnWidth(i);
7492 getLockedCount : function(){
7493 for(var i = 0, len = this.config.length; i < len; i++){
7494 if(!this.isLocked(i)){
7499 return this.config.length;
7503 * Returns the number of columns.
7506 getColumnCount : function(visibleOnly){
7507 if(visibleOnly === true){
7509 for(var i = 0, len = this.config.length; i < len; i++){
7510 if(!this.isHidden(i)){
7516 return this.config.length;
7520 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7521 * @param {Function} fn
7522 * @param {Object} scope (optional)
7523 * @return {Array} result
7525 getColumnsBy : function(fn, scope){
7527 for(var i = 0, len = this.config.length; i < len; i++){
7528 var c = this.config[i];
7529 if(fn.call(scope||this, c, i) === true){
7537 * Returns true if the specified column is sortable.
7538 * @param {Number} col The column index
7541 isSortable : function(col){
7542 if(typeof this.config[col].sortable == "undefined"){
7543 return this.defaultSortable;
7545 return this.config[col].sortable;
7549 * Returns the rendering (formatting) function defined for the column.
7550 * @param {Number} col The column index.
7551 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7553 getRenderer : function(col){
7554 if(!this.config[col].renderer){
7555 return Roo.grid.ColumnModel.defaultRenderer;
7557 return this.config[col].renderer;
7561 * Sets the rendering (formatting) function for a column.
7562 * @param {Number} col The column index
7563 * @param {Function} fn The function to use to process the cell's raw data
7564 * to return HTML markup for the grid view. The render function is called with
7565 * the following parameters:<ul>
7566 * <li>Data value.</li>
7567 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7568 * <li>css A CSS style string to apply to the table cell.</li>
7569 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7570 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7571 * <li>Row index</li>
7572 * <li>Column index</li>
7573 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7575 setRenderer : function(col, fn){
7576 this.config[col].renderer = fn;
7580 * Returns the width for the specified column.
7581 * @param {Number} col The column index
7584 getColumnWidth : function(col){
7585 return this.config[col].width * 1 || this.defaultWidth;
7589 * Sets the width for a column.
7590 * @param {Number} col The column index
7591 * @param {Number} width The new width
7593 setColumnWidth : function(col, width, suppressEvent){
7594 this.config[col].width = width;
7595 this.totalWidth = null;
7597 this.fireEvent("widthchange", this, col, width);
7602 * Returns the total width of all columns.
7603 * @param {Boolean} includeHidden True to include hidden column widths
7606 getTotalWidth : function(includeHidden){
7607 if(!this.totalWidth){
7608 this.totalWidth = 0;
7609 for(var i = 0, len = this.config.length; i < len; i++){
7610 if(includeHidden || !this.isHidden(i)){
7611 this.totalWidth += this.getColumnWidth(i);
7615 return this.totalWidth;
7619 * Returns the header for the specified column.
7620 * @param {Number} col The column index
7623 getColumnHeader : function(col){
7624 return this.config[col].header;
7628 * Sets the header for a column.
7629 * @param {Number} col The column index
7630 * @param {String} header The new header
7632 setColumnHeader : function(col, header){
7633 this.config[col].header = header;
7634 this.fireEvent("headerchange", this, col, header);
7638 * Returns the tooltip for the specified column.
7639 * @param {Number} col The column index
7642 getColumnTooltip : function(col){
7643 return this.config[col].tooltip;
7646 * Sets the tooltip for a column.
7647 * @param {Number} col The column index
7648 * @param {String} tooltip The new tooltip
7650 setColumnTooltip : function(col, tooltip){
7651 this.config[col].tooltip = tooltip;
7655 * Returns the dataIndex for the specified column.
7656 * @param {Number} col The column index
7659 getDataIndex : function(col){
7660 return this.config[col].dataIndex;
7664 * Sets the dataIndex for a column.
7665 * @param {Number} col The column index
7666 * @param {Number} dataIndex The new dataIndex
7668 setDataIndex : function(col, dataIndex){
7669 this.config[col].dataIndex = dataIndex;
7675 * Returns true if the cell is editable.
7676 * @param {Number} colIndex The column index
7677 * @param {Number} rowIndex The row index - this is nto actually used..?
7680 isCellEditable : function(colIndex, rowIndex){
7681 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7685 * Returns the editor defined for the cell/column.
7686 * return false or null to disable editing.
7687 * @param {Number} colIndex The column index
7688 * @param {Number} rowIndex The row index
7691 getCellEditor : function(colIndex, rowIndex){
7692 return this.config[colIndex].editor;
7696 * Sets if a column is editable.
7697 * @param {Number} col The column index
7698 * @param {Boolean} editable True if the column is editable
7700 setEditable : function(col, editable){
7701 this.config[col].editable = editable;
7706 * Returns true if the column is hidden.
7707 * @param {Number} colIndex The column index
7710 isHidden : function(colIndex){
7711 return this.config[colIndex].hidden;
7716 * Returns true if the column width cannot be changed
7718 isFixed : function(colIndex){
7719 return this.config[colIndex].fixed;
7723 * Returns true if the column can be resized
7726 isResizable : function(colIndex){
7727 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7730 * Sets if a column is hidden.
7731 * @param {Number} colIndex The column index
7732 * @param {Boolean} hidden True if the column is hidden
7734 setHidden : function(colIndex, hidden){
7735 this.config[colIndex].hidden = hidden;
7736 this.totalWidth = null;
7737 this.fireEvent("hiddenchange", this, colIndex, hidden);
7741 * Sets the editor for a column.
7742 * @param {Number} col The column index
7743 * @param {Object} editor The editor object
7745 setEditor : function(col, editor){
7746 this.config[col].editor = editor;
7750 Roo.grid.ColumnModel.defaultRenderer = function(value)
7752 if(typeof value == "object") {
7755 if(typeof value == "string" && value.length < 1){
7759 return String.format("{0}", value);
7762 // Alias for backwards compatibility
7763 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7766 * Ext JS Library 1.1.1
7767 * Copyright(c) 2006-2007, Ext JS, LLC.
7769 * Originally Released Under LGPL - original licence link has changed is not relivant.
7772 * <script type="text/javascript">
7776 * @class Roo.LoadMask
7777 * A simple utility class for generically masking elements while loading data. If the element being masked has
7778 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7779 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7780 * element's UpdateManager load indicator and will be destroyed after the initial load.
7782 * Create a new LoadMask
7783 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7784 * @param {Object} config The config object
7786 Roo.LoadMask = function(el, config){
7787 this.el = Roo.get(el);
7788 Roo.apply(this, config);
7790 this.store.on('beforeload', this.onBeforeLoad, this);
7791 this.store.on('load', this.onLoad, this);
7792 this.store.on('loadexception', this.onLoadException, this);
7793 this.removeMask = false;
7795 var um = this.el.getUpdateManager();
7796 um.showLoadIndicator = false; // disable the default indicator
7797 um.on('beforeupdate', this.onBeforeLoad, this);
7798 um.on('update', this.onLoad, this);
7799 um.on('failure', this.onLoad, this);
7800 this.removeMask = true;
7804 Roo.LoadMask.prototype = {
7806 * @cfg {Boolean} removeMask
7807 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7808 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7812 * The text to display in a centered loading message box (defaults to 'Loading...')
7816 * @cfg {String} msgCls
7817 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7819 msgCls : 'x-mask-loading',
7822 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7828 * Disables the mask to prevent it from being displayed
7830 disable : function(){
7831 this.disabled = true;
7835 * Enables the mask so that it can be displayed
7837 enable : function(){
7838 this.disabled = false;
7841 onLoadException : function()
7845 if (typeof(arguments[3]) != 'undefined') {
7846 Roo.MessageBox.alert("Error loading",arguments[3]);
7850 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7851 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7858 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7863 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7867 onBeforeLoad : function(){
7869 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7874 destroy : function(){
7876 this.store.un('beforeload', this.onBeforeLoad, this);
7877 this.store.un('load', this.onLoad, this);
7878 this.store.un('loadexception', this.onLoadException, this);
7880 var um = this.el.getUpdateManager();
7881 um.un('beforeupdate', this.onBeforeLoad, this);
7882 um.un('update', this.onLoad, this);
7883 um.un('failure', this.onLoad, this);
7894 * @class Roo.bootstrap.Table
7895 * @extends Roo.bootstrap.Component
7896 * Bootstrap Table class
7897 * @cfg {String} cls table class
7898 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7899 * @cfg {String} bgcolor Specifies the background color for a table
7900 * @cfg {Number} border Specifies whether the table cells should have borders or not
7901 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7902 * @cfg {Number} cellspacing Specifies the space between cells
7903 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7904 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7905 * @cfg {String} sortable Specifies that the table should be sortable
7906 * @cfg {String} summary Specifies a summary of the content of a table
7907 * @cfg {Number} width Specifies the width of a table
7908 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7910 * @cfg {boolean} striped Should the rows be alternative striped
7911 * @cfg {boolean} bordered Add borders to the table
7912 * @cfg {boolean} hover Add hover highlighting
7913 * @cfg {boolean} condensed Format condensed
7914 * @cfg {boolean} responsive Format condensed
7915 * @cfg {Boolean} loadMask (true|false) default false
7916 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7917 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7918 * @cfg {Boolean} rowSelection (true|false) default false
7919 * @cfg {Boolean} cellSelection (true|false) default false
7920 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7921 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7922 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7923 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7927 * Create a new Table
7928 * @param {Object} config The config object
7931 Roo.bootstrap.Table = function(config){
7932 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7937 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7938 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7939 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7940 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7942 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7944 this.sm.grid = this;
7945 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7946 this.sm = this.selModel;
7947 this.sm.xmodule = this.xmodule || false;
7950 if (this.cm && typeof(this.cm.config) == 'undefined') {
7951 this.colModel = new Roo.grid.ColumnModel(this.cm);
7952 this.cm = this.colModel;
7953 this.cm.xmodule = this.xmodule || false;
7956 this.store= Roo.factory(this.store, Roo.data);
7957 this.ds = this.store;
7958 this.ds.xmodule = this.xmodule || false;
7961 if (this.footer && this.store) {
7962 this.footer.dataSource = this.ds;
7963 this.footer = Roo.factory(this.footer);
7970 * Fires when a cell is clicked
7971 * @param {Roo.bootstrap.Table} this
7972 * @param {Roo.Element} el
7973 * @param {Number} rowIndex
7974 * @param {Number} columnIndex
7975 * @param {Roo.EventObject} e
7979 * @event celldblclick
7980 * Fires when a cell is double clicked
7981 * @param {Roo.bootstrap.Table} this
7982 * @param {Roo.Element} el
7983 * @param {Number} rowIndex
7984 * @param {Number} columnIndex
7985 * @param {Roo.EventObject} e
7987 "celldblclick" : true,
7990 * Fires when a row is clicked
7991 * @param {Roo.bootstrap.Table} this
7992 * @param {Roo.Element} el
7993 * @param {Number} rowIndex
7994 * @param {Roo.EventObject} e
7998 * @event rowdblclick
7999 * Fires when a row is double clicked
8000 * @param {Roo.bootstrap.Table} this
8001 * @param {Roo.Element} el
8002 * @param {Number} rowIndex
8003 * @param {Roo.EventObject} e
8005 "rowdblclick" : true,
8008 * Fires when a mouseover occur
8009 * @param {Roo.bootstrap.Table} this
8010 * @param {Roo.Element} el
8011 * @param {Number} rowIndex
8012 * @param {Number} columnIndex
8013 * @param {Roo.EventObject} e
8018 * Fires when a mouseout occur
8019 * @param {Roo.bootstrap.Table} this
8020 * @param {Roo.Element} el
8021 * @param {Number} rowIndex
8022 * @param {Number} columnIndex
8023 * @param {Roo.EventObject} e
8028 * Fires when a row is rendered, so you can change add a style to it.
8029 * @param {Roo.bootstrap.Table} this
8030 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8034 * @event rowsrendered
8035 * Fires when all the rows have been rendered
8036 * @param {Roo.bootstrap.Table} this
8038 'rowsrendered' : true,
8040 * @event contextmenu
8041 * The raw contextmenu event for the entire grid.
8042 * @param {Roo.EventObject} e
8044 "contextmenu" : true,
8046 * @event rowcontextmenu
8047 * Fires when a row is right clicked
8048 * @param {Roo.bootstrap.Table} this
8049 * @param {Number} rowIndex
8050 * @param {Roo.EventObject} e
8052 "rowcontextmenu" : true,
8054 * @event cellcontextmenu
8055 * Fires when a cell is right clicked
8056 * @param {Roo.bootstrap.Table} this
8057 * @param {Number} rowIndex
8058 * @param {Number} cellIndex
8059 * @param {Roo.EventObject} e
8061 "cellcontextmenu" : true,
8063 * @event headercontextmenu
8064 * Fires when a header is right clicked
8065 * @param {Roo.bootstrap.Table} this
8066 * @param {Number} columnIndex
8067 * @param {Roo.EventObject} e
8069 "headercontextmenu" : true
8073 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8099 rowSelection : false,
8100 cellSelection : false,
8103 // Roo.Element - the tbody
8105 // Roo.Element - thead element
8108 container: false, // used by gridpanel...
8114 auto_hide_footer : false,
8116 getAutoCreate : function()
8118 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8125 if (this.scrollBody) {
8126 cfg.cls += ' table-body-fixed';
8129 cfg.cls += ' table-striped';
8133 cfg.cls += ' table-hover';
8135 if (this.bordered) {
8136 cfg.cls += ' table-bordered';
8138 if (this.condensed) {
8139 cfg.cls += ' table-condensed';
8141 if (this.responsive) {
8142 cfg.cls += ' table-responsive';
8146 cfg.cls+= ' ' +this.cls;
8149 // this lot should be simplifed...
8162 ].forEach(function(k) {
8170 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8173 if(this.store || this.cm){
8174 if(this.headerShow){
8175 cfg.cn.push(this.renderHeader());
8178 cfg.cn.push(this.renderBody());
8180 if(this.footerShow){
8181 cfg.cn.push(this.renderFooter());
8183 // where does this come from?
8184 //cfg.cls+= ' TableGrid';
8187 return { cn : [ cfg ] };
8190 initEvents : function()
8192 if(!this.store || !this.cm){
8195 if (this.selModel) {
8196 this.selModel.initEvents();
8200 //Roo.log('initEvents with ds!!!!');
8202 this.mainBody = this.el.select('tbody', true).first();
8203 this.mainHead = this.el.select('thead', true).first();
8204 this.mainFoot = this.el.select('tfoot', true).first();
8210 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8211 e.on('click', _this.sort, _this);
8214 this.mainBody.on("click", this.onClick, this);
8215 this.mainBody.on("dblclick", this.onDblClick, this);
8217 // why is this done????? = it breaks dialogs??
8218 //this.parent().el.setStyle('position', 'relative');
8222 this.footer.parentId = this.id;
8223 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8226 this.el.select('tfoot tr td').first().addClass('hide');
8231 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8234 this.store.on('load', this.onLoad, this);
8235 this.store.on('beforeload', this.onBeforeLoad, this);
8236 this.store.on('update', this.onUpdate, this);
8237 this.store.on('add', this.onAdd, this);
8238 this.store.on("clear", this.clear, this);
8240 this.el.on("contextmenu", this.onContextMenu, this);
8242 this.mainBody.on('scroll', this.onBodyScroll, this);
8244 this.cm.on("headerchange", this.onHeaderChange, this);
8246 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8250 onContextMenu : function(e, t)
8252 this.processEvent("contextmenu", e);
8255 processEvent : function(name, e)
8257 if (name != 'touchstart' ) {
8258 this.fireEvent(name, e);
8261 var t = e.getTarget();
8263 var cell = Roo.get(t);
8269 if(cell.findParent('tfoot', false, true)){
8273 if(cell.findParent('thead', false, true)){
8275 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8276 cell = Roo.get(t).findParent('th', false, true);
8278 Roo.log("failed to find th in thead?");
8279 Roo.log(e.getTarget());
8284 var cellIndex = cell.dom.cellIndex;
8286 var ename = name == 'touchstart' ? 'click' : name;
8287 this.fireEvent("header" + ename, this, cellIndex, e);
8292 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8293 cell = Roo.get(t).findParent('td', false, true);
8295 Roo.log("failed to find th in tbody?");
8296 Roo.log(e.getTarget());
8301 var row = cell.findParent('tr', false, true);
8302 var cellIndex = cell.dom.cellIndex;
8303 var rowIndex = row.dom.rowIndex - 1;
8307 this.fireEvent("row" + name, this, rowIndex, e);
8311 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8317 onMouseover : function(e, el)
8319 var cell = Roo.get(el);
8325 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8326 cell = cell.findParent('td', false, true);
8329 var row = cell.findParent('tr', false, true);
8330 var cellIndex = cell.dom.cellIndex;
8331 var rowIndex = row.dom.rowIndex - 1; // start from 0
8333 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8337 onMouseout : function(e, el)
8339 var cell = Roo.get(el);
8345 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8346 cell = cell.findParent('td', false, true);
8349 var row = cell.findParent('tr', false, true);
8350 var cellIndex = cell.dom.cellIndex;
8351 var rowIndex = row.dom.rowIndex - 1; // start from 0
8353 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8357 onClick : function(e, el)
8359 var cell = Roo.get(el);
8361 if(!cell || (!this.cellSelection && !this.rowSelection)){
8365 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8366 cell = cell.findParent('td', false, true);
8369 if(!cell || typeof(cell) == 'undefined'){
8373 var row = cell.findParent('tr', false, true);
8375 if(!row || typeof(row) == 'undefined'){
8379 var cellIndex = cell.dom.cellIndex;
8380 var rowIndex = this.getRowIndex(row);
8382 // why??? - should these not be based on SelectionModel?
8383 if(this.cellSelection){
8384 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8387 if(this.rowSelection){
8388 this.fireEvent('rowclick', this, row, rowIndex, e);
8394 onDblClick : function(e,el)
8396 var cell = Roo.get(el);
8398 if(!cell || (!this.cellSelection && !this.rowSelection)){
8402 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8403 cell = cell.findParent('td', false, true);
8406 if(!cell || typeof(cell) == 'undefined'){
8410 var row = cell.findParent('tr', false, true);
8412 if(!row || typeof(row) == 'undefined'){
8416 var cellIndex = cell.dom.cellIndex;
8417 var rowIndex = this.getRowIndex(row);
8419 if(this.cellSelection){
8420 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8423 if(this.rowSelection){
8424 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8428 sort : function(e,el)
8430 var col = Roo.get(el);
8432 if(!col.hasClass('sortable')){
8436 var sort = col.attr('sort');
8439 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8443 this.store.sortInfo = {field : sort, direction : dir};
8446 Roo.log("calling footer first");
8447 this.footer.onClick('first');
8450 this.store.load({ params : { start : 0 } });
8454 renderHeader : function()
8462 this.totalWidth = 0;
8464 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8466 var config = cm.config[i];
8470 cls : 'x-hcol-' + i,
8472 html: cm.getColumnHeader(i)
8477 if(typeof(config.sortable) != 'undefined' && config.sortable){
8479 c.html = '<i class="glyphicon"></i>' + c.html;
8482 // could use BS4 hidden-..-down
8484 if(typeof(config.lgHeader) != 'undefined'){
8485 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8488 if(typeof(config.mdHeader) != 'undefined'){
8489 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8492 if(typeof(config.smHeader) != 'undefined'){
8493 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8496 if(typeof(config.xsHeader) != 'undefined'){
8497 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8504 if(typeof(config.tooltip) != 'undefined'){
8505 c.tooltip = config.tooltip;
8508 if(typeof(config.colspan) != 'undefined'){
8509 c.colspan = config.colspan;
8512 if(typeof(config.hidden) != 'undefined' && config.hidden){
8513 c.style += ' display:none;';
8516 if(typeof(config.dataIndex) != 'undefined'){
8517 c.sort = config.dataIndex;
8522 if(typeof(config.align) != 'undefined' && config.align.length){
8523 c.style += ' text-align:' + config.align + ';';
8526 if(typeof(config.width) != 'undefined'){
8527 c.style += ' width:' + config.width + 'px;';
8528 this.totalWidth += config.width;
8530 this.totalWidth += 100; // assume minimum of 100 per column?
8533 if(typeof(config.cls) != 'undefined'){
8534 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8537 ['xs','sm','md','lg'].map(function(size){
8539 if(typeof(config[size]) == 'undefined'){
8543 if (!config[size]) { // 0 = hidden
8544 // BS 4 '0' is treated as hide that column and below.
8545 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8549 c.cls += ' col-' + size + '-' + config[size] + (
8550 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8562 renderBody : function()
8572 colspan : this.cm.getColumnCount()
8582 renderFooter : function()
8592 colspan : this.cm.getColumnCount()
8606 // Roo.log('ds onload');
8611 var ds = this.store;
8613 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8614 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8615 if (_this.store.sortInfo) {
8617 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8618 e.select('i', true).addClass(['glyphicon-arrow-up']);
8621 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8622 e.select('i', true).addClass(['glyphicon-arrow-down']);
8627 var tbody = this.mainBody;
8629 if(ds.getCount() > 0){
8630 ds.data.each(function(d,rowIndex){
8631 var row = this.renderRow(cm, ds, rowIndex);
8633 tbody.createChild(row);
8637 if(row.cellObjects.length){
8638 Roo.each(row.cellObjects, function(r){
8639 _this.renderCellObject(r);
8646 var tfoot = this.el.select('tfoot', true).first();
8648 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8650 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8652 var total = this.ds.getTotalCount();
8654 if(this.footer.pageSize < total){
8655 this.mainFoot.show();
8659 Roo.each(this.el.select('tbody td', true).elements, function(e){
8660 e.on('mouseover', _this.onMouseover, _this);
8663 Roo.each(this.el.select('tbody td', true).elements, function(e){
8664 e.on('mouseout', _this.onMouseout, _this);
8666 this.fireEvent('rowsrendered', this);
8672 onUpdate : function(ds,record)
8674 this.refreshRow(record);
8678 onRemove : function(ds, record, index, isUpdate){
8679 if(isUpdate !== true){
8680 this.fireEvent("beforerowremoved", this, index, record);
8682 var bt = this.mainBody.dom;
8684 var rows = this.el.select('tbody > tr', true).elements;
8686 if(typeof(rows[index]) != 'undefined'){
8687 bt.removeChild(rows[index].dom);
8690 // if(bt.rows[index]){
8691 // bt.removeChild(bt.rows[index]);
8694 if(isUpdate !== true){
8695 //this.stripeRows(index);
8696 //this.syncRowHeights(index, index);
8698 this.fireEvent("rowremoved", this, index, record);
8702 onAdd : function(ds, records, rowIndex)
8704 //Roo.log('on Add called');
8705 // - note this does not handle multiple adding very well..
8706 var bt = this.mainBody.dom;
8707 for (var i =0 ; i < records.length;i++) {
8708 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8709 //Roo.log(records[i]);
8710 //Roo.log(this.store.getAt(rowIndex+i));
8711 this.insertRow(this.store, rowIndex + i, false);
8718 refreshRow : function(record){
8719 var ds = this.store, index;
8720 if(typeof record == 'number'){
8722 record = ds.getAt(index);
8724 index = ds.indexOf(record);
8726 return; // should not happen - but seems to
8729 this.insertRow(ds, index, true);
8731 this.onRemove(ds, record, index+1, true);
8733 //this.syncRowHeights(index, index);
8735 this.fireEvent("rowupdated", this, index, record);
8738 insertRow : function(dm, rowIndex, isUpdate){
8741 this.fireEvent("beforerowsinserted", this, rowIndex);
8743 //var s = this.getScrollState();
8744 var row = this.renderRow(this.cm, this.store, rowIndex);
8745 // insert before rowIndex..
8746 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8750 if(row.cellObjects.length){
8751 Roo.each(row.cellObjects, function(r){
8752 _this.renderCellObject(r);
8757 this.fireEvent("rowsinserted", this, rowIndex);
8758 //this.syncRowHeights(firstRow, lastRow);
8759 //this.stripeRows(firstRow);
8766 getRowDom : function(rowIndex)
8768 var rows = this.el.select('tbody > tr', true).elements;
8770 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8773 // returns the object tree for a tr..
8776 renderRow : function(cm, ds, rowIndex)
8778 var d = ds.getAt(rowIndex);
8782 cls : 'x-row-' + rowIndex,
8786 var cellObjects = [];
8788 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8789 var config = cm.config[i];
8791 var renderer = cm.getRenderer(i);
8795 if(typeof(renderer) !== 'undefined'){
8796 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8798 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8799 // and are rendered into the cells after the row is rendered - using the id for the element.
8801 if(typeof(value) === 'object'){
8811 rowIndex : rowIndex,
8816 this.fireEvent('rowclass', this, rowcfg);
8820 cls : rowcfg.rowClass + ' x-col-' + i,
8822 html: (typeof(value) === 'object') ? '' : value
8829 if(typeof(config.colspan) != 'undefined'){
8830 td.colspan = config.colspan;
8833 if(typeof(config.hidden) != 'undefined' && config.hidden){
8834 td.style += ' display:none;';
8837 if(typeof(config.align) != 'undefined' && config.align.length){
8838 td.style += ' text-align:' + config.align + ';';
8840 if(typeof(config.valign) != 'undefined' && config.valign.length){
8841 td.style += ' vertical-align:' + config.valign + ';';
8844 if(typeof(config.width) != 'undefined'){
8845 td.style += ' width:' + config.width + 'px;';
8848 if(typeof(config.cursor) != 'undefined'){
8849 td.style += ' cursor:' + config.cursor + ';';
8852 if(typeof(config.cls) != 'undefined'){
8853 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8856 ['xs','sm','md','lg'].map(function(size){
8858 if(typeof(config[size]) == 'undefined'){
8864 if (!config[size]) { // 0 = hidden
8865 // BS 4 '0' is treated as hide that column and below.
8866 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8870 td.cls += ' col-' + size + '-' + config[size] + (
8871 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8881 row.cellObjects = cellObjects;
8889 onBeforeLoad : function()
8898 this.el.select('tbody', true).first().dom.innerHTML = '';
8901 * Show or hide a row.
8902 * @param {Number} rowIndex to show or hide
8903 * @param {Boolean} state hide
8905 setRowVisibility : function(rowIndex, state)
8907 var bt = this.mainBody.dom;
8909 var rows = this.el.select('tbody > tr', true).elements;
8911 if(typeof(rows[rowIndex]) == 'undefined'){
8914 rows[rowIndex].dom.style.display = state ? '' : 'none';
8918 getSelectionModel : function(){
8920 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8922 return this.selModel;
8925 * Render the Roo.bootstrap object from renderder
8927 renderCellObject : function(r)
8931 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8933 var t = r.cfg.render(r.container);
8936 Roo.each(r.cfg.cn, function(c){
8938 container: t.getChildContainer(),
8941 _this.renderCellObject(child);
8946 getRowIndex : function(row)
8950 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8961 * Returns the grid's underlying element = used by panel.Grid
8962 * @return {Element} The element
8964 getGridEl : function(){
8968 * Forces a resize - used by panel.Grid
8969 * @return {Element} The element
8971 autoSize : function()
8973 //var ctr = Roo.get(this.container.dom.parentElement);
8974 var ctr = Roo.get(this.el.dom);
8976 var thd = this.getGridEl().select('thead',true).first();
8977 var tbd = this.getGridEl().select('tbody', true).first();
8978 var tfd = this.getGridEl().select('tfoot', true).first();
8980 var cw = ctr.getWidth();
8981 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8985 tbd.setWidth(ctr.getWidth());
8986 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8987 // this needs fixing for various usage - currently only hydra job advers I think..
8989 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8991 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8994 cw = Math.max(cw, this.totalWidth);
8995 this.getGridEl().select('tbody tr',true).setWidth(cw);
8997 // resize 'expandable coloumn?
8999 return; // we doe not have a view in this design..
9002 onBodyScroll: function()
9004 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9006 this.mainHead.setStyle({
9007 'position' : 'relative',
9008 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9014 var scrollHeight = this.mainBody.dom.scrollHeight;
9016 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9018 var height = this.mainBody.getHeight();
9020 if(scrollHeight - height == scrollTop) {
9022 var total = this.ds.getTotalCount();
9024 if(this.footer.cursor + this.footer.pageSize < total){
9026 this.footer.ds.load({
9028 start : this.footer.cursor + this.footer.pageSize,
9029 limit : this.footer.pageSize
9039 onHeaderChange : function()
9041 var header = this.renderHeader();
9042 var table = this.el.select('table', true).first();
9044 this.mainHead.remove();
9045 this.mainHead = table.createChild(header, this.mainBody, false);
9048 onHiddenChange : function(colModel, colIndex, hidden)
9050 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9051 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9053 this.CSS.updateRule(thSelector, "display", "");
9054 this.CSS.updateRule(tdSelector, "display", "");
9057 this.CSS.updateRule(thSelector, "display", "none");
9058 this.CSS.updateRule(tdSelector, "display", "none");
9061 this.onHeaderChange();
9065 setColumnWidth: function(col_index, width)
9067 // width = "md-2 xs-2..."
9068 if(!this.colModel.config[col_index]) {
9072 var w = width.split(" ");
9074 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9076 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9079 for(var j = 0; j < w.length; j++) {
9085 var size_cls = w[j].split("-");
9087 if(!Number.isInteger(size_cls[1] * 1)) {
9091 if(!this.colModel.config[col_index][size_cls[0]]) {
9095 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9099 h_row[0].classList.replace(
9100 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9101 "col-"+size_cls[0]+"-"+size_cls[1]
9104 for(var i = 0; i < rows.length; i++) {
9106 var size_cls = w[j].split("-");
9108 if(!Number.isInteger(size_cls[1] * 1)) {
9112 if(!this.colModel.config[col_index][size_cls[0]]) {
9116 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9120 rows[i].classList.replace(
9121 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9122 "col-"+size_cls[0]+"-"+size_cls[1]
9126 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9141 * @class Roo.bootstrap.TableCell
9142 * @extends Roo.bootstrap.Component
9143 * Bootstrap TableCell class
9144 * @cfg {String} html cell contain text
9145 * @cfg {String} cls cell class
9146 * @cfg {String} tag cell tag (td|th) default td
9147 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9148 * @cfg {String} align Aligns the content in a cell
9149 * @cfg {String} axis Categorizes cells
9150 * @cfg {String} bgcolor Specifies the background color of a cell
9151 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9152 * @cfg {Number} colspan Specifies the number of columns a cell should span
9153 * @cfg {String} headers Specifies one or more header cells a cell is related to
9154 * @cfg {Number} height Sets the height of a cell
9155 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9156 * @cfg {Number} rowspan Sets the number of rows a cell should span
9157 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9158 * @cfg {String} valign Vertical aligns the content in a cell
9159 * @cfg {Number} width Specifies the width of a cell
9162 * Create a new TableCell
9163 * @param {Object} config The config object
9166 Roo.bootstrap.TableCell = function(config){
9167 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9170 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9190 getAutoCreate : function(){
9191 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9211 cfg.align=this.align
9217 cfg.bgcolor=this.bgcolor
9220 cfg.charoff=this.charoff
9223 cfg.colspan=this.colspan
9226 cfg.headers=this.headers
9229 cfg.height=this.height
9232 cfg.nowrap=this.nowrap
9235 cfg.rowspan=this.rowspan
9238 cfg.scope=this.scope
9241 cfg.valign=this.valign
9244 cfg.width=this.width
9263 * @class Roo.bootstrap.TableRow
9264 * @extends Roo.bootstrap.Component
9265 * Bootstrap TableRow class
9266 * @cfg {String} cls row class
9267 * @cfg {String} align Aligns the content in a table row
9268 * @cfg {String} bgcolor Specifies a background color for a table row
9269 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9270 * @cfg {String} valign Vertical aligns the content in a table row
9273 * Create a new TableRow
9274 * @param {Object} config The config object
9277 Roo.bootstrap.TableRow = function(config){
9278 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9281 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9289 getAutoCreate : function(){
9290 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9300 cfg.align = this.align;
9303 cfg.bgcolor = this.bgcolor;
9306 cfg.charoff = this.charoff;
9309 cfg.valign = this.valign;
9327 * @class Roo.bootstrap.TableBody
9328 * @extends Roo.bootstrap.Component
9329 * Bootstrap TableBody class
9330 * @cfg {String} cls element class
9331 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9332 * @cfg {String} align Aligns the content inside the element
9333 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9334 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9337 * Create a new TableBody
9338 * @param {Object} config The config object
9341 Roo.bootstrap.TableBody = function(config){
9342 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9345 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9353 getAutoCreate : function(){
9354 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9368 cfg.align = this.align;
9371 cfg.charoff = this.charoff;
9374 cfg.valign = this.valign;
9381 // initEvents : function()
9388 // this.store = Roo.factory(this.store, Roo.data);
9389 // this.store.on('load', this.onLoad, this);
9391 // this.store.load();
9395 // onLoad: function ()
9397 // this.fireEvent('load', this);
9407 * Ext JS Library 1.1.1
9408 * Copyright(c) 2006-2007, Ext JS, LLC.
9410 * Originally Released Under LGPL - original licence link has changed is not relivant.
9413 * <script type="text/javascript">
9416 // as we use this in bootstrap.
9417 Roo.namespace('Roo.form');
9419 * @class Roo.form.Action
9420 * Internal Class used to handle form actions
9422 * @param {Roo.form.BasicForm} el The form element or its id
9423 * @param {Object} config Configuration options
9428 // define the action interface
9429 Roo.form.Action = function(form, options){
9431 this.options = options || {};
9434 * Client Validation Failed
9437 Roo.form.Action.CLIENT_INVALID = 'client';
9439 * Server Validation Failed
9442 Roo.form.Action.SERVER_INVALID = 'server';
9444 * Connect to Server Failed
9447 Roo.form.Action.CONNECT_FAILURE = 'connect';
9449 * Reading Data from Server Failed
9452 Roo.form.Action.LOAD_FAILURE = 'load';
9454 Roo.form.Action.prototype = {
9456 failureType : undefined,
9457 response : undefined,
9461 run : function(options){
9466 success : function(response){
9471 handleResponse : function(response){
9475 // default connection failure
9476 failure : function(response){
9478 this.response = response;
9479 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9480 this.form.afterAction(this, false);
9483 processResponse : function(response){
9484 this.response = response;
9485 if(!response.responseText){
9488 this.result = this.handleResponse(response);
9492 // utility functions used internally
9493 getUrl : function(appendParams){
9494 var url = this.options.url || this.form.url || this.form.el.dom.action;
9496 var p = this.getParams();
9498 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9504 getMethod : function(){
9505 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9508 getParams : function(){
9509 var bp = this.form.baseParams;
9510 var p = this.options.params;
9512 if(typeof p == "object"){
9513 p = Roo.urlEncode(Roo.applyIf(p, bp));
9514 }else if(typeof p == 'string' && bp){
9515 p += '&' + Roo.urlEncode(bp);
9518 p = Roo.urlEncode(bp);
9523 createCallback : function(){
9525 success: this.success,
9526 failure: this.failure,
9528 timeout: (this.form.timeout*1000),
9529 upload: this.form.fileUpload ? this.success : undefined
9534 Roo.form.Action.Submit = function(form, options){
9535 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9538 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9541 haveProgress : false,
9542 uploadComplete : false,
9544 // uploadProgress indicator.
9545 uploadProgress : function()
9547 if (!this.form.progressUrl) {
9551 if (!this.haveProgress) {
9552 Roo.MessageBox.progress("Uploading", "Uploading");
9554 if (this.uploadComplete) {
9555 Roo.MessageBox.hide();
9559 this.haveProgress = true;
9561 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9563 var c = new Roo.data.Connection();
9565 url : this.form.progressUrl,
9570 success : function(req){
9571 //console.log(data);
9575 rdata = Roo.decode(req.responseText)
9577 Roo.log("Invalid data from server..");
9581 if (!rdata || !rdata.success) {
9583 Roo.MessageBox.alert(Roo.encode(rdata));
9586 var data = rdata.data;
9588 if (this.uploadComplete) {
9589 Roo.MessageBox.hide();
9594 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9595 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9598 this.uploadProgress.defer(2000,this);
9601 failure: function(data) {
9602 Roo.log('progress url failed ');
9613 // run get Values on the form, so it syncs any secondary forms.
9614 this.form.getValues();
9616 var o = this.options;
9617 var method = this.getMethod();
9618 var isPost = method == 'POST';
9619 if(o.clientValidation === false || this.form.isValid()){
9621 if (this.form.progressUrl) {
9622 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9623 (new Date() * 1) + '' + Math.random());
9628 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9629 form:this.form.el.dom,
9630 url:this.getUrl(!isPost),
9632 params:isPost ? this.getParams() : null,
9633 isUpload: this.form.fileUpload,
9634 formData : this.form.formData
9637 this.uploadProgress();
9639 }else if (o.clientValidation !== false){ // client validation failed
9640 this.failureType = Roo.form.Action.CLIENT_INVALID;
9641 this.form.afterAction(this, false);
9645 success : function(response)
9647 this.uploadComplete= true;
9648 if (this.haveProgress) {
9649 Roo.MessageBox.hide();
9653 var result = this.processResponse(response);
9654 if(result === true || result.success){
9655 this.form.afterAction(this, true);
9659 this.form.markInvalid(result.errors);
9660 this.failureType = Roo.form.Action.SERVER_INVALID;
9662 this.form.afterAction(this, false);
9664 failure : function(response)
9666 this.uploadComplete= true;
9667 if (this.haveProgress) {
9668 Roo.MessageBox.hide();
9671 this.response = response;
9672 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9673 this.form.afterAction(this, false);
9676 handleResponse : function(response){
9677 if(this.form.errorReader){
9678 var rs = this.form.errorReader.read(response);
9681 for(var i = 0, len = rs.records.length; i < len; i++) {
9682 var r = rs.records[i];
9686 if(errors.length < 1){
9690 success : rs.success,
9696 ret = Roo.decode(response.responseText);
9700 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9710 Roo.form.Action.Load = function(form, options){
9711 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9712 this.reader = this.form.reader;
9715 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9720 Roo.Ajax.request(Roo.apply(
9721 this.createCallback(), {
9722 method:this.getMethod(),
9723 url:this.getUrl(false),
9724 params:this.getParams()
9728 success : function(response){
9730 var result = this.processResponse(response);
9731 if(result === true || !result.success || !result.data){
9732 this.failureType = Roo.form.Action.LOAD_FAILURE;
9733 this.form.afterAction(this, false);
9736 this.form.clearInvalid();
9737 this.form.setValues(result.data);
9738 this.form.afterAction(this, true);
9741 handleResponse : function(response){
9742 if(this.form.reader){
9743 var rs = this.form.reader.read(response);
9744 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9746 success : rs.success,
9750 return Roo.decode(response.responseText);
9754 Roo.form.Action.ACTION_TYPES = {
9755 'load' : Roo.form.Action.Load,
9756 'submit' : Roo.form.Action.Submit
9765 * @class Roo.bootstrap.Form
9766 * @extends Roo.bootstrap.Component
9767 * Bootstrap Form class
9768 * @cfg {String} method GET | POST (default POST)
9769 * @cfg {String} labelAlign top | left (default top)
9770 * @cfg {String} align left | right - for navbars
9771 * @cfg {Boolean} loadMask load mask when submit (default true)
9776 * @param {Object} config The config object
9780 Roo.bootstrap.Form = function(config){
9782 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9784 Roo.bootstrap.Form.popover.apply();
9788 * @event clientvalidation
9789 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9790 * @param {Form} this
9791 * @param {Boolean} valid true if the form has passed client-side validation
9793 clientvalidation: true,
9795 * @event beforeaction
9796 * Fires before any action is performed. Return false to cancel the action.
9797 * @param {Form} this
9798 * @param {Action} action The action to be performed
9802 * @event actionfailed
9803 * Fires when an action fails.
9804 * @param {Form} this
9805 * @param {Action} action The action that failed
9807 actionfailed : true,
9809 * @event actioncomplete
9810 * Fires when an action is completed.
9811 * @param {Form} this
9812 * @param {Action} action The action that completed
9814 actioncomplete : true
9818 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9821 * @cfg {String} method
9822 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9827 * The URL to use for form actions if one isn't supplied in the action options.
9830 * @cfg {Boolean} fileUpload
9831 * Set to true if this form is a file upload.
9835 * @cfg {Object} baseParams
9836 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9840 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9844 * @cfg {Sting} align (left|right) for navbar forms
9849 activeAction : null,
9852 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9853 * element by passing it or its id or mask the form itself by passing in true.
9856 waitMsgTarget : false,
9861 * @cfg {Boolean} errorMask (true|false) default false
9866 * @cfg {Number} maskOffset Default 100
9871 * @cfg {Boolean} maskBody
9875 getAutoCreate : function(){
9879 method : this.method || 'POST',
9880 id : this.id || Roo.id(),
9883 if (this.parent().xtype.match(/^Nav/)) {
9884 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9888 if (this.labelAlign == 'left' ) {
9889 cfg.cls += ' form-horizontal';
9895 initEvents : function()
9897 this.el.on('submit', this.onSubmit, this);
9898 // this was added as random key presses on the form where triggering form submit.
9899 this.el.on('keypress', function(e) {
9900 if (e.getCharCode() != 13) {
9903 // we might need to allow it for textareas.. and some other items.
9904 // check e.getTarget().
9906 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9910 Roo.log("keypress blocked");
9918 onSubmit : function(e){
9923 * Returns true if client-side validation on the form is successful.
9926 isValid : function(){
9927 var items = this.getItems();
9931 items.each(function(f){
9937 Roo.log('invalid field: ' + f.name);
9941 if(!target && f.el.isVisible(true)){
9947 if(this.errorMask && !valid){
9948 Roo.bootstrap.Form.popover.mask(this, target);
9955 * Returns true if any fields in this form have changed since their original load.
9958 isDirty : function(){
9960 var items = this.getItems();
9961 items.each(function(f){
9971 * Performs a predefined action (submit or load) or custom actions you define on this form.
9972 * @param {String} actionName The name of the action type
9973 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9974 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9975 * accept other config options):
9977 Property Type Description
9978 ---------------- --------------- ----------------------------------------------------------------------------------
9979 url String The url for the action (defaults to the form's url)
9980 method String The form method to use (defaults to the form's method, or POST if not defined)
9981 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9982 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9983 validate the form on the client (defaults to false)
9985 * @return {BasicForm} this
9987 doAction : function(action, options){
9988 if(typeof action == 'string'){
9989 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9991 if(this.fireEvent('beforeaction', this, action) !== false){
9992 this.beforeAction(action);
9993 action.run.defer(100, action);
9999 beforeAction : function(action){
10000 var o = action.options;
10005 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10007 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10010 // not really supported yet.. ??
10012 //if(this.waitMsgTarget === true){
10013 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10014 //}else if(this.waitMsgTarget){
10015 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10016 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10018 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10024 afterAction : function(action, success){
10025 this.activeAction = null;
10026 var o = action.options;
10031 Roo.get(document.body).unmask();
10037 //if(this.waitMsgTarget === true){
10038 // this.el.unmask();
10039 //}else if(this.waitMsgTarget){
10040 // this.waitMsgTarget.unmask();
10042 // Roo.MessageBox.updateProgress(1);
10043 // Roo.MessageBox.hide();
10050 Roo.callback(o.success, o.scope, [this, action]);
10051 this.fireEvent('actioncomplete', this, action);
10055 // failure condition..
10056 // we have a scenario where updates need confirming.
10057 // eg. if a locking scenario exists..
10058 // we look for { errors : { needs_confirm : true }} in the response.
10060 (typeof(action.result) != 'undefined') &&
10061 (typeof(action.result.errors) != 'undefined') &&
10062 (typeof(action.result.errors.needs_confirm) != 'undefined')
10065 Roo.log("not supported yet");
10068 Roo.MessageBox.confirm(
10069 "Change requires confirmation",
10070 action.result.errorMsg,
10075 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10085 Roo.callback(o.failure, o.scope, [this, action]);
10086 // show an error message if no failed handler is set..
10087 if (!this.hasListener('actionfailed')) {
10088 Roo.log("need to add dialog support");
10090 Roo.MessageBox.alert("Error",
10091 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10092 action.result.errorMsg :
10093 "Saving Failed, please check your entries or try again"
10098 this.fireEvent('actionfailed', this, action);
10103 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10104 * @param {String} id The value to search for
10107 findField : function(id){
10108 var items = this.getItems();
10109 var field = items.get(id);
10111 items.each(function(f){
10112 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10119 return field || null;
10122 * Mark fields in this form invalid in bulk.
10123 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10124 * @return {BasicForm} this
10126 markInvalid : function(errors){
10127 if(errors instanceof Array){
10128 for(var i = 0, len = errors.length; i < len; i++){
10129 var fieldError = errors[i];
10130 var f = this.findField(fieldError.id);
10132 f.markInvalid(fieldError.msg);
10138 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10139 field.markInvalid(errors[id]);
10143 //Roo.each(this.childForms || [], function (f) {
10144 // f.markInvalid(errors);
10151 * Set values for fields in this form in bulk.
10152 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10153 * @return {BasicForm} this
10155 setValues : function(values){
10156 if(values instanceof Array){ // array of objects
10157 for(var i = 0, len = values.length; i < len; i++){
10159 var f = this.findField(v.id);
10161 f.setValue(v.value);
10162 if(this.trackResetOnLoad){
10163 f.originalValue = f.getValue();
10167 }else{ // object hash
10170 if(typeof values[id] != 'function' && (field = this.findField(id))){
10172 if (field.setFromData &&
10173 field.valueField &&
10174 field.displayField &&
10175 // combos' with local stores can
10176 // be queried via setValue()
10177 // to set their value..
10178 (field.store && !field.store.isLocal)
10182 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10183 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10184 field.setFromData(sd);
10186 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10188 field.setFromData(values);
10191 field.setValue(values[id]);
10195 if(this.trackResetOnLoad){
10196 field.originalValue = field.getValue();
10202 //Roo.each(this.childForms || [], function (f) {
10203 // f.setValues(values);
10210 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10211 * they are returned as an array.
10212 * @param {Boolean} asString
10215 getValues : function(asString){
10216 //if (this.childForms) {
10217 // copy values from the child forms
10218 // Roo.each(this.childForms, function (f) {
10219 // this.setValues(f.getValues());
10225 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10226 if(asString === true){
10229 return Roo.urlDecode(fs);
10233 * Returns the fields in this form as an object with key/value pairs.
10234 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10237 getFieldValues : function(with_hidden)
10239 var items = this.getItems();
10241 items.each(function(f){
10243 if (!f.getName()) {
10247 var v = f.getValue();
10249 if (f.inputType =='radio') {
10250 if (typeof(ret[f.getName()]) == 'undefined') {
10251 ret[f.getName()] = ''; // empty..
10254 if (!f.el.dom.checked) {
10258 v = f.el.dom.value;
10262 if(f.xtype == 'MoneyField'){
10263 ret[f.currencyName] = f.getCurrency();
10266 // not sure if this supported any more..
10267 if ((typeof(v) == 'object') && f.getRawValue) {
10268 v = f.getRawValue() ; // dates..
10270 // combo boxes where name != hiddenName...
10271 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10272 ret[f.name] = f.getRawValue();
10274 ret[f.getName()] = v;
10281 * Clears all invalid messages in this form.
10282 * @return {BasicForm} this
10284 clearInvalid : function(){
10285 var items = this.getItems();
10287 items.each(function(f){
10295 * Resets this form.
10296 * @return {BasicForm} this
10298 reset : function(){
10299 var items = this.getItems();
10300 items.each(function(f){
10304 Roo.each(this.childForms || [], function (f) {
10312 getItems : function()
10314 var r=new Roo.util.MixedCollection(false, function(o){
10315 return o.id || (o.id = Roo.id());
10317 var iter = function(el) {
10324 Roo.each(el.items,function(e) {
10333 hideFields : function(items)
10335 Roo.each(items, function(i){
10337 var f = this.findField(i);
10348 showFields : function(items)
10350 Roo.each(items, function(i){
10352 var f = this.findField(i);
10365 Roo.apply(Roo.bootstrap.Form, {
10381 intervalID : false,
10387 if(this.isApplied){
10392 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10393 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10394 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10395 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10398 this.maskEl.top.enableDisplayMode("block");
10399 this.maskEl.left.enableDisplayMode("block");
10400 this.maskEl.bottom.enableDisplayMode("block");
10401 this.maskEl.right.enableDisplayMode("block");
10403 this.toolTip = new Roo.bootstrap.Tooltip({
10404 cls : 'roo-form-error-popover',
10406 'left' : ['r-l', [-2,0], 'right'],
10407 'right' : ['l-r', [2,0], 'left'],
10408 'bottom' : ['tl-bl', [0,2], 'top'],
10409 'top' : [ 'bl-tl', [0,-2], 'bottom']
10413 this.toolTip.render(Roo.get(document.body));
10415 this.toolTip.el.enableDisplayMode("block");
10417 Roo.get(document.body).on('click', function(){
10421 Roo.get(document.body).on('touchstart', function(){
10425 this.isApplied = true
10428 mask : function(form, target)
10432 this.target = target;
10434 if(!this.form.errorMask || !target.el){
10438 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10440 Roo.log(scrollable);
10442 var ot = this.target.el.calcOffsetsTo(scrollable);
10444 var scrollTo = ot[1] - this.form.maskOffset;
10446 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10448 scrollable.scrollTo('top', scrollTo);
10450 var box = this.target.el.getBox();
10452 var zIndex = Roo.bootstrap.Modal.zIndex++;
10455 this.maskEl.top.setStyle('position', 'absolute');
10456 this.maskEl.top.setStyle('z-index', zIndex);
10457 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10458 this.maskEl.top.setLeft(0);
10459 this.maskEl.top.setTop(0);
10460 this.maskEl.top.show();
10462 this.maskEl.left.setStyle('position', 'absolute');
10463 this.maskEl.left.setStyle('z-index', zIndex);
10464 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10465 this.maskEl.left.setLeft(0);
10466 this.maskEl.left.setTop(box.y - this.padding);
10467 this.maskEl.left.show();
10469 this.maskEl.bottom.setStyle('position', 'absolute');
10470 this.maskEl.bottom.setStyle('z-index', zIndex);
10471 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10472 this.maskEl.bottom.setLeft(0);
10473 this.maskEl.bottom.setTop(box.bottom + this.padding);
10474 this.maskEl.bottom.show();
10476 this.maskEl.right.setStyle('position', 'absolute');
10477 this.maskEl.right.setStyle('z-index', zIndex);
10478 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10479 this.maskEl.right.setLeft(box.right + this.padding);
10480 this.maskEl.right.setTop(box.y - this.padding);
10481 this.maskEl.right.show();
10483 this.toolTip.bindEl = this.target.el;
10485 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10487 var tip = this.target.blankText;
10489 if(this.target.getValue() !== '' ) {
10491 if (this.target.invalidText.length) {
10492 tip = this.target.invalidText;
10493 } else if (this.target.regexText.length){
10494 tip = this.target.regexText;
10498 this.toolTip.show(tip);
10500 this.intervalID = window.setInterval(function() {
10501 Roo.bootstrap.Form.popover.unmask();
10504 window.onwheel = function(){ return false;};
10506 (function(){ this.isMasked = true; }).defer(500, this);
10510 unmask : function()
10512 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10516 this.maskEl.top.setStyle('position', 'absolute');
10517 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10518 this.maskEl.top.hide();
10520 this.maskEl.left.setStyle('position', 'absolute');
10521 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10522 this.maskEl.left.hide();
10524 this.maskEl.bottom.setStyle('position', 'absolute');
10525 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10526 this.maskEl.bottom.hide();
10528 this.maskEl.right.setStyle('position', 'absolute');
10529 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10530 this.maskEl.right.hide();
10532 this.toolTip.hide();
10534 this.toolTip.el.hide();
10536 window.onwheel = function(){ return true;};
10538 if(this.intervalID){
10539 window.clearInterval(this.intervalID);
10540 this.intervalID = false;
10543 this.isMasked = false;
10553 * Ext JS Library 1.1.1
10554 * Copyright(c) 2006-2007, Ext JS, LLC.
10556 * Originally Released Under LGPL - original licence link has changed is not relivant.
10559 * <script type="text/javascript">
10562 * @class Roo.form.VTypes
10563 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10566 Roo.form.VTypes = function(){
10567 // closure these in so they are only created once.
10568 var alpha = /^[a-zA-Z_]+$/;
10569 var alphanum = /^[a-zA-Z0-9_]+$/;
10570 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10571 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10573 // All these messages and functions are configurable
10576 * The function used to validate email addresses
10577 * @param {String} value The email address
10579 'email' : function(v){
10580 return email.test(v);
10583 * The error text to display when the email validation function returns false
10586 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10588 * The keystroke filter mask to be applied on email input
10591 'emailMask' : /[a-z0-9_\.\-@]/i,
10594 * The function used to validate URLs
10595 * @param {String} value The URL
10597 'url' : function(v){
10598 return url.test(v);
10601 * The error text to display when the url validation function returns false
10604 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10607 * The function used to validate alpha values
10608 * @param {String} value The value
10610 'alpha' : function(v){
10611 return alpha.test(v);
10614 * The error text to display when the alpha validation function returns false
10617 'alphaText' : 'This field should only contain letters and _',
10619 * The keystroke filter mask to be applied on alpha input
10622 'alphaMask' : /[a-z_]/i,
10625 * The function used to validate alphanumeric values
10626 * @param {String} value The value
10628 'alphanum' : function(v){
10629 return alphanum.test(v);
10632 * The error text to display when the alphanumeric validation function returns false
10635 'alphanumText' : 'This field should only contain letters, numbers and _',
10637 * The keystroke filter mask to be applied on alphanumeric input
10640 'alphanumMask' : /[a-z0-9_]/i
10650 * @class Roo.bootstrap.Input
10651 * @extends Roo.bootstrap.Component
10652 * Bootstrap Input class
10653 * @cfg {Boolean} disabled is it disabled
10654 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10655 * @cfg {String} name name of the input
10656 * @cfg {string} fieldLabel - the label associated
10657 * @cfg {string} placeholder - placeholder to put in text.
10658 * @cfg {string} before - input group add on before
10659 * @cfg {string} after - input group add on after
10660 * @cfg {string} size - (lg|sm) or leave empty..
10661 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10662 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10663 * @cfg {Number} md colspan out of 12 for computer-sized screens
10664 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10665 * @cfg {string} value default value of the input
10666 * @cfg {Number} labelWidth set the width of label
10667 * @cfg {Number} labellg set the width of label (1-12)
10668 * @cfg {Number} labelmd set the width of label (1-12)
10669 * @cfg {Number} labelsm set the width of label (1-12)
10670 * @cfg {Number} labelxs set the width of label (1-12)
10671 * @cfg {String} labelAlign (top|left)
10672 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10673 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10674 * @cfg {String} indicatorpos (left|right) default left
10675 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10676 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10677 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10679 * @cfg {String} align (left|center|right) Default left
10680 * @cfg {Boolean} forceFeedback (true|false) Default false
10683 * Create a new Input
10684 * @param {Object} config The config object
10687 Roo.bootstrap.Input = function(config){
10689 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10694 * Fires when this field receives input focus.
10695 * @param {Roo.form.Field} this
10700 * Fires when this field loses input focus.
10701 * @param {Roo.form.Field} this
10705 * @event specialkey
10706 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10707 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10708 * @param {Roo.form.Field} this
10709 * @param {Roo.EventObject} e The event object
10714 * Fires just before the field blurs if the field value has changed.
10715 * @param {Roo.form.Field} this
10716 * @param {Mixed} newValue The new value
10717 * @param {Mixed} oldValue The original value
10722 * Fires after the field has been marked as invalid.
10723 * @param {Roo.form.Field} this
10724 * @param {String} msg The validation message
10729 * Fires after the field has been validated with no errors.
10730 * @param {Roo.form.Field} this
10735 * Fires after the key up
10736 * @param {Roo.form.Field} this
10737 * @param {Roo.EventObject} e The event Object
10742 * Fires after the user pastes into input
10743 * @param {Roo.form.Field} this
10744 * @param {Roo.EventObject} e The event Object
10750 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10752 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10753 automatic validation (defaults to "keyup").
10755 validationEvent : "keyup",
10757 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10759 validateOnBlur : true,
10761 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10763 validationDelay : 250,
10765 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10767 focusClass : "x-form-focus", // not needed???
10771 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10773 invalidClass : "has-warning",
10776 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10778 validClass : "has-success",
10781 * @cfg {Boolean} hasFeedback (true|false) default true
10783 hasFeedback : true,
10786 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10788 invalidFeedbackClass : "glyphicon-warning-sign",
10791 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10793 validFeedbackClass : "glyphicon-ok",
10796 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10798 selectOnFocus : false,
10801 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10805 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10810 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10812 disableKeyFilter : false,
10815 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10819 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10823 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10825 blankText : "Please complete this mandatory field",
10828 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10832 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10834 maxLength : Number.MAX_VALUE,
10836 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10838 minLengthText : "The minimum length for this field is {0}",
10840 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10842 maxLengthText : "The maximum length for this field is {0}",
10846 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10847 * If available, this function will be called only after the basic validators all return true, and will be passed the
10848 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10852 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10853 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10854 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10858 * @cfg {String} regexText -- Depricated - use Invalid Text
10863 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10869 autocomplete: false,
10873 inputType : 'text',
10876 placeholder: false,
10881 preventMark: false,
10882 isFormField : true,
10885 labelAlign : false,
10888 formatedValue : false,
10889 forceFeedback : false,
10891 indicatorpos : 'left',
10901 parentLabelAlign : function()
10904 while (parent.parent()) {
10905 parent = parent.parent();
10906 if (typeof(parent.labelAlign) !='undefined') {
10907 return parent.labelAlign;
10914 getAutoCreate : function()
10916 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10922 if(this.inputType != 'hidden'){
10923 cfg.cls = 'form-group' //input-group
10929 type : this.inputType,
10930 value : this.value,
10931 cls : 'form-control',
10932 placeholder : this.placeholder || '',
10933 autocomplete : this.autocomplete || 'new-password'
10935 if (this.inputType == 'file') {
10936 input.style = 'overflow:hidden'; // why not in CSS?
10939 if(this.capture.length){
10940 input.capture = this.capture;
10943 if(this.accept.length){
10944 input.accept = this.accept + "/*";
10948 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10951 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10952 input.maxLength = this.maxLength;
10955 if (this.disabled) {
10956 input.disabled=true;
10959 if (this.readOnly) {
10960 input.readonly=true;
10964 input.name = this.name;
10968 input.cls += ' input-' + this.size;
10972 ['xs','sm','md','lg'].map(function(size){
10973 if (settings[size]) {
10974 cfg.cls += ' col-' + size + '-' + settings[size];
10978 var inputblock = input;
10982 cls: 'glyphicon form-control-feedback'
10985 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10988 cls : 'has-feedback',
10996 if (this.before || this.after) {
10999 cls : 'input-group',
11003 if (this.before && typeof(this.before) == 'string') {
11005 inputblock.cn.push({
11007 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11011 if (this.before && typeof(this.before) == 'object') {
11012 this.before = Roo.factory(this.before);
11014 inputblock.cn.push({
11016 cls : 'roo-input-before input-group-prepend input-group-' +
11017 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11021 inputblock.cn.push(input);
11023 if (this.after && typeof(this.after) == 'string') {
11024 inputblock.cn.push({
11026 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11030 if (this.after && typeof(this.after) == 'object') {
11031 this.after = Roo.factory(this.after);
11033 inputblock.cn.push({
11035 cls : 'roo-input-after input-group-append input-group-' +
11036 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11040 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11041 inputblock.cls += ' has-feedback';
11042 inputblock.cn.push(feedback);
11047 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11048 tooltip : 'This field is required'
11050 if (this.allowBlank ) {
11051 indicator.style = this.allowBlank ? ' display:none' : '';
11053 if (align ==='left' && this.fieldLabel.length) {
11055 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11062 cls : 'control-label col-form-label',
11063 html : this.fieldLabel
11074 var labelCfg = cfg.cn[1];
11075 var contentCfg = cfg.cn[2];
11077 if(this.indicatorpos == 'right'){
11082 cls : 'control-label col-form-label',
11086 html : this.fieldLabel
11100 labelCfg = cfg.cn[0];
11101 contentCfg = cfg.cn[1];
11105 if(this.labelWidth > 12){
11106 labelCfg.style = "width: " + this.labelWidth + 'px';
11109 if(this.labelWidth < 13 && this.labelmd == 0){
11110 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11113 if(this.labellg > 0){
11114 labelCfg.cls += ' col-lg-' + this.labellg;
11115 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11118 if(this.labelmd > 0){
11119 labelCfg.cls += ' col-md-' + this.labelmd;
11120 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11123 if(this.labelsm > 0){
11124 labelCfg.cls += ' col-sm-' + this.labelsm;
11125 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11128 if(this.labelxs > 0){
11129 labelCfg.cls += ' col-xs-' + this.labelxs;
11130 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11134 } else if ( this.fieldLabel.length) {
11141 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11142 tooltip : 'This field is required',
11143 style : this.allowBlank ? ' display:none' : ''
11147 //cls : 'input-group-addon',
11148 html : this.fieldLabel
11156 if(this.indicatorpos == 'right'){
11161 //cls : 'input-group-addon',
11162 html : this.fieldLabel
11167 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11168 tooltip : 'This field is required',
11169 style : this.allowBlank ? ' display:none' : ''
11189 if (this.parentType === 'Navbar' && this.parent().bar) {
11190 cfg.cls += ' navbar-form';
11193 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11194 // on BS4 we do this only if not form
11195 cfg.cls += ' navbar-form';
11203 * return the real input element.
11205 inputEl: function ()
11207 return this.el.select('input.form-control',true).first();
11210 tooltipEl : function()
11212 return this.inputEl();
11215 indicatorEl : function()
11217 if (Roo.bootstrap.version == 4) {
11218 return false; // not enabled in v4 yet.
11221 var indicator = this.el.select('i.roo-required-indicator',true).first();
11231 setDisabled : function(v)
11233 var i = this.inputEl().dom;
11235 i.removeAttribute('disabled');
11239 i.setAttribute('disabled','true');
11241 initEvents : function()
11244 this.inputEl().on("keydown" , this.fireKey, this);
11245 this.inputEl().on("focus", this.onFocus, this);
11246 this.inputEl().on("blur", this.onBlur, this);
11248 this.inputEl().relayEvent('keyup', this);
11249 this.inputEl().relayEvent('paste', this);
11251 this.indicator = this.indicatorEl();
11253 if(this.indicator){
11254 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11257 // reference to original value for reset
11258 this.originalValue = this.getValue();
11259 //Roo.form.TextField.superclass.initEvents.call(this);
11260 if(this.validationEvent == 'keyup'){
11261 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11262 this.inputEl().on('keyup', this.filterValidation, this);
11264 else if(this.validationEvent !== false){
11265 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11268 if(this.selectOnFocus){
11269 this.on("focus", this.preFocus, this);
11272 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11273 this.inputEl().on("keypress", this.filterKeys, this);
11275 this.inputEl().relayEvent('keypress', this);
11278 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11279 this.el.on("click", this.autoSize, this);
11282 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11283 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11286 if (typeof(this.before) == 'object') {
11287 this.before.render(this.el.select('.roo-input-before',true).first());
11289 if (typeof(this.after) == 'object') {
11290 this.after.render(this.el.select('.roo-input-after',true).first());
11293 this.inputEl().on('change', this.onChange, this);
11296 filterValidation : function(e){
11297 if(!e.isNavKeyPress()){
11298 this.validationTask.delay(this.validationDelay);
11302 * Validates the field value
11303 * @return {Boolean} True if the value is valid, else false
11305 validate : function(){
11306 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11307 if(this.disabled || this.validateValue(this.getRawValue())){
11312 this.markInvalid();
11318 * Validates a value according to the field's validation rules and marks the field as invalid
11319 * if the validation fails
11320 * @param {Mixed} value The value to validate
11321 * @return {Boolean} True if the value is valid, else false
11323 validateValue : function(value)
11325 if(this.getVisibilityEl().hasClass('hidden')){
11329 if(value.length < 1) { // if it's blank
11330 if(this.allowBlank){
11336 if(value.length < this.minLength){
11339 if(value.length > this.maxLength){
11343 var vt = Roo.form.VTypes;
11344 if(!vt[this.vtype](value, this)){
11348 if(typeof this.validator == "function"){
11349 var msg = this.validator(value);
11353 if (typeof(msg) == 'string') {
11354 this.invalidText = msg;
11358 if(this.regex && !this.regex.test(value)){
11366 fireKey : function(e){
11367 //Roo.log('field ' + e.getKey());
11368 if(e.isNavKeyPress()){
11369 this.fireEvent("specialkey", this, e);
11372 focus : function (selectText){
11374 this.inputEl().focus();
11375 if(selectText === true){
11376 this.inputEl().dom.select();
11382 onFocus : function(){
11383 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11384 // this.el.addClass(this.focusClass);
11386 if(!this.hasFocus){
11387 this.hasFocus = true;
11388 this.startValue = this.getValue();
11389 this.fireEvent("focus", this);
11393 beforeBlur : Roo.emptyFn,
11397 onBlur : function(){
11399 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11400 //this.el.removeClass(this.focusClass);
11402 this.hasFocus = false;
11403 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11406 var v = this.getValue();
11407 if(String(v) !== String(this.startValue)){
11408 this.fireEvent('change', this, v, this.startValue);
11410 this.fireEvent("blur", this);
11413 onChange : function(e)
11415 var v = this.getValue();
11416 if(String(v) !== String(this.startValue)){
11417 this.fireEvent('change', this, v, this.startValue);
11423 * Resets the current field value to the originally loaded value and clears any validation messages
11425 reset : function(){
11426 this.setValue(this.originalValue);
11430 * Returns the name of the field
11431 * @return {Mixed} name The name field
11433 getName: function(){
11437 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11438 * @return {Mixed} value The field value
11440 getValue : function(){
11442 var v = this.inputEl().getValue();
11447 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11448 * @return {Mixed} value The field value
11450 getRawValue : function(){
11451 var v = this.inputEl().getValue();
11457 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11458 * @param {Mixed} value The value to set
11460 setRawValue : function(v){
11461 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11464 selectText : function(start, end){
11465 var v = this.getRawValue();
11467 start = start === undefined ? 0 : start;
11468 end = end === undefined ? v.length : end;
11469 var d = this.inputEl().dom;
11470 if(d.setSelectionRange){
11471 d.setSelectionRange(start, end);
11472 }else if(d.createTextRange){
11473 var range = d.createTextRange();
11474 range.moveStart("character", start);
11475 range.moveEnd("character", v.length-end);
11482 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11483 * @param {Mixed} value The value to set
11485 setValue : function(v){
11488 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11494 processValue : function(value){
11495 if(this.stripCharsRe){
11496 var newValue = value.replace(this.stripCharsRe, '');
11497 if(newValue !== value){
11498 this.setRawValue(newValue);
11505 preFocus : function(){
11507 if(this.selectOnFocus){
11508 this.inputEl().dom.select();
11511 filterKeys : function(e){
11512 var k = e.getKey();
11513 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11516 var c = e.getCharCode(), cc = String.fromCharCode(c);
11517 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11520 if(!this.maskRe.test(cc)){
11525 * Clear any invalid styles/messages for this field
11527 clearInvalid : function(){
11529 if(!this.el || this.preventMark){ // not rendered
11534 this.el.removeClass([this.invalidClass, 'is-invalid']);
11536 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11538 var feedback = this.el.select('.form-control-feedback', true).first();
11541 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11546 if(this.indicator){
11547 this.indicator.removeClass('visible');
11548 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11551 this.fireEvent('valid', this);
11555 * Mark this field as valid
11557 markValid : function()
11559 if(!this.el || this.preventMark){ // not rendered...
11563 this.el.removeClass([this.invalidClass, this.validClass]);
11564 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11566 var feedback = this.el.select('.form-control-feedback', true).first();
11569 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11572 if(this.indicator){
11573 this.indicator.removeClass('visible');
11574 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11582 if(this.allowBlank && !this.getRawValue().length){
11585 if (Roo.bootstrap.version == 3) {
11586 this.el.addClass(this.validClass);
11588 this.inputEl().addClass('is-valid');
11591 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11593 var feedback = this.el.select('.form-control-feedback', true).first();
11596 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11597 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11602 this.fireEvent('valid', this);
11606 * Mark this field as invalid
11607 * @param {String} msg The validation message
11609 markInvalid : function(msg)
11611 if(!this.el || this.preventMark){ // not rendered
11615 this.el.removeClass([this.invalidClass, this.validClass]);
11616 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11618 var feedback = this.el.select('.form-control-feedback', true).first();
11621 this.el.select('.form-control-feedback', true).first().removeClass(
11622 [this.invalidFeedbackClass, this.validFeedbackClass]);
11629 if(this.allowBlank && !this.getRawValue().length){
11633 if(this.indicator){
11634 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11635 this.indicator.addClass('visible');
11637 if (Roo.bootstrap.version == 3) {
11638 this.el.addClass(this.invalidClass);
11640 this.inputEl().addClass('is-invalid');
11645 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11647 var feedback = this.el.select('.form-control-feedback', true).first();
11650 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11652 if(this.getValue().length || this.forceFeedback){
11653 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11660 this.fireEvent('invalid', this, msg);
11663 SafariOnKeyDown : function(event)
11665 // this is a workaround for a password hang bug on chrome/ webkit.
11666 if (this.inputEl().dom.type != 'password') {
11670 var isSelectAll = false;
11672 if(this.inputEl().dom.selectionEnd > 0){
11673 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11675 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11676 event.preventDefault();
11681 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11683 event.preventDefault();
11684 // this is very hacky as keydown always get's upper case.
11686 var cc = String.fromCharCode(event.getCharCode());
11687 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11691 adjustWidth : function(tag, w){
11692 tag = tag.toLowerCase();
11693 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11694 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11695 if(tag == 'input'){
11698 if(tag == 'textarea'){
11701 }else if(Roo.isOpera){
11702 if(tag == 'input'){
11705 if(tag == 'textarea'){
11713 setFieldLabel : function(v)
11715 if(!this.rendered){
11719 if(this.indicatorEl()){
11720 var ar = this.el.select('label > span',true);
11722 if (ar.elements.length) {
11723 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11724 this.fieldLabel = v;
11728 var br = this.el.select('label',true);
11730 if(br.elements.length) {
11731 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11732 this.fieldLabel = v;
11736 Roo.log('Cannot Found any of label > span || label in input');
11740 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11741 this.fieldLabel = v;
11756 * @class Roo.bootstrap.TextArea
11757 * @extends Roo.bootstrap.Input
11758 * Bootstrap TextArea class
11759 * @cfg {Number} cols Specifies the visible width of a text area
11760 * @cfg {Number} rows Specifies the visible number of lines in a text area
11761 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11762 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11763 * @cfg {string} html text
11766 * Create a new TextArea
11767 * @param {Object} config The config object
11770 Roo.bootstrap.TextArea = function(config){
11771 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11775 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11785 getAutoCreate : function(){
11787 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11793 if(this.inputType != 'hidden'){
11794 cfg.cls = 'form-group' //input-group
11802 value : this.value || '',
11803 html: this.html || '',
11804 cls : 'form-control',
11805 placeholder : this.placeholder || ''
11809 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11810 input.maxLength = this.maxLength;
11814 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11818 input.cols = this.cols;
11821 if (this.readOnly) {
11822 input.readonly = true;
11826 input.name = this.name;
11830 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11834 ['xs','sm','md','lg'].map(function(size){
11835 if (settings[size]) {
11836 cfg.cls += ' col-' + size + '-' + settings[size];
11840 var inputblock = input;
11842 if(this.hasFeedback && !this.allowBlank){
11846 cls: 'glyphicon form-control-feedback'
11850 cls : 'has-feedback',
11859 if (this.before || this.after) {
11862 cls : 'input-group',
11866 inputblock.cn.push({
11868 cls : 'input-group-addon',
11873 inputblock.cn.push(input);
11875 if(this.hasFeedback && !this.allowBlank){
11876 inputblock.cls += ' has-feedback';
11877 inputblock.cn.push(feedback);
11881 inputblock.cn.push({
11883 cls : 'input-group-addon',
11890 if (align ==='left' && this.fieldLabel.length) {
11895 cls : 'control-label',
11896 html : this.fieldLabel
11907 if(this.labelWidth > 12){
11908 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11911 if(this.labelWidth < 13 && this.labelmd == 0){
11912 this.labelmd = this.labelWidth;
11915 if(this.labellg > 0){
11916 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11917 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11920 if(this.labelmd > 0){
11921 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11922 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11925 if(this.labelsm > 0){
11926 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11927 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11930 if(this.labelxs > 0){
11931 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11932 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11935 } else if ( this.fieldLabel.length) {
11940 //cls : 'input-group-addon',
11941 html : this.fieldLabel
11959 if (this.disabled) {
11960 input.disabled=true;
11967 * return the real textarea element.
11969 inputEl: function ()
11971 return this.el.select('textarea.form-control',true).first();
11975 * Clear any invalid styles/messages for this field
11977 clearInvalid : function()
11980 if(!this.el || this.preventMark){ // not rendered
11984 var label = this.el.select('label', true).first();
11985 var icon = this.el.select('i.fa-star', true).first();
11990 this.el.removeClass( this.validClass);
11991 this.inputEl().removeClass('is-invalid');
11993 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11995 var feedback = this.el.select('.form-control-feedback', true).first();
11998 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12003 this.fireEvent('valid', this);
12007 * Mark this field as valid
12009 markValid : function()
12011 if(!this.el || this.preventMark){ // not rendered
12015 this.el.removeClass([this.invalidClass, this.validClass]);
12016 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12018 var feedback = this.el.select('.form-control-feedback', true).first();
12021 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12024 if(this.disabled || this.allowBlank){
12028 var label = this.el.select('label', true).first();
12029 var icon = this.el.select('i.fa-star', true).first();
12034 if (Roo.bootstrap.version == 3) {
12035 this.el.addClass(this.validClass);
12037 this.inputEl().addClass('is-valid');
12041 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12043 var feedback = this.el.select('.form-control-feedback', true).first();
12046 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12047 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12052 this.fireEvent('valid', this);
12056 * Mark this field as invalid
12057 * @param {String} msg The validation message
12059 markInvalid : function(msg)
12061 if(!this.el || this.preventMark){ // not rendered
12065 this.el.removeClass([this.invalidClass, this.validClass]);
12066 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12068 var feedback = this.el.select('.form-control-feedback', true).first();
12071 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12074 if(this.disabled || this.allowBlank){
12078 var label = this.el.select('label', true).first();
12079 var icon = this.el.select('i.fa-star', true).first();
12081 if(!this.getValue().length && label && !icon){
12082 this.el.createChild({
12084 cls : 'text-danger fa fa-lg fa-star',
12085 tooltip : 'This field is required',
12086 style : 'margin-right:5px;'
12090 if (Roo.bootstrap.version == 3) {
12091 this.el.addClass(this.invalidClass);
12093 this.inputEl().addClass('is-invalid');
12096 // fixme ... this may be depricated need to test..
12097 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12099 var feedback = this.el.select('.form-control-feedback', true).first();
12102 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12104 if(this.getValue().length || this.forceFeedback){
12105 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12112 this.fireEvent('invalid', this, msg);
12120 * trigger field - base class for combo..
12125 * @class Roo.bootstrap.TriggerField
12126 * @extends Roo.bootstrap.Input
12127 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12128 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12129 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12130 * for which you can provide a custom implementation. For example:
12132 var trigger = new Roo.bootstrap.TriggerField();
12133 trigger.onTriggerClick = myTriggerFn;
12134 trigger.applyTo('my-field');
12137 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12138 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12139 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12140 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12141 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12144 * Create a new TriggerField.
12145 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12146 * to the base TextField)
12148 Roo.bootstrap.TriggerField = function(config){
12149 this.mimicing = false;
12150 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12153 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12155 * @cfg {String} triggerClass A CSS class to apply to the trigger
12158 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12163 * @cfg {Boolean} removable (true|false) special filter default false
12167 /** @cfg {Boolean} grow @hide */
12168 /** @cfg {Number} growMin @hide */
12169 /** @cfg {Number} growMax @hide */
12175 autoSize: Roo.emptyFn,
12179 deferHeight : true,
12182 actionMode : 'wrap',
12187 getAutoCreate : function(){
12189 var align = this.labelAlign || this.parentLabelAlign();
12194 cls: 'form-group' //input-group
12201 type : this.inputType,
12202 cls : 'form-control',
12203 autocomplete: 'new-password',
12204 placeholder : this.placeholder || ''
12208 input.name = this.name;
12211 input.cls += ' input-' + this.size;
12214 if (this.disabled) {
12215 input.disabled=true;
12218 var inputblock = input;
12220 if(this.hasFeedback && !this.allowBlank){
12224 cls: 'glyphicon form-control-feedback'
12227 if(this.removable && !this.editable ){
12229 cls : 'has-feedback',
12235 cls : 'roo-combo-removable-btn close'
12242 cls : 'has-feedback',
12251 if(this.removable && !this.editable ){
12253 cls : 'roo-removable',
12259 cls : 'roo-combo-removable-btn close'
12266 if (this.before || this.after) {
12269 cls : 'input-group',
12273 inputblock.cn.push({
12275 cls : 'input-group-addon input-group-prepend input-group-text',
12280 inputblock.cn.push(input);
12282 if(this.hasFeedback && !this.allowBlank){
12283 inputblock.cls += ' has-feedback';
12284 inputblock.cn.push(feedback);
12288 inputblock.cn.push({
12290 cls : 'input-group-addon input-group-append input-group-text',
12299 var ibwrap = inputblock;
12304 cls: 'roo-select2-choices',
12308 cls: 'roo-select2-search-field',
12320 cls: 'roo-select2-container input-group',
12325 cls: 'form-hidden-field'
12331 if(!this.multiple && this.showToggleBtn){
12337 if (this.caret != false) {
12340 cls: 'fa fa-' + this.caret
12347 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12349 Roo.bootstrap.version == 3 ? caret : '',
12352 cls: 'combobox-clear',
12366 combobox.cls += ' roo-select2-container-multi';
12370 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12371 tooltip : 'This field is required'
12373 if (Roo.bootstrap.version == 4) {
12376 style : 'display:none'
12381 if (align ==='left' && this.fieldLabel.length) {
12383 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12390 cls : 'control-label',
12391 html : this.fieldLabel
12403 var labelCfg = cfg.cn[1];
12404 var contentCfg = cfg.cn[2];
12406 if(this.indicatorpos == 'right'){
12411 cls : 'control-label',
12415 html : this.fieldLabel
12429 labelCfg = cfg.cn[0];
12430 contentCfg = cfg.cn[1];
12433 if(this.labelWidth > 12){
12434 labelCfg.style = "width: " + this.labelWidth + 'px';
12437 if(this.labelWidth < 13 && this.labelmd == 0){
12438 this.labelmd = this.labelWidth;
12441 if(this.labellg > 0){
12442 labelCfg.cls += ' col-lg-' + this.labellg;
12443 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12446 if(this.labelmd > 0){
12447 labelCfg.cls += ' col-md-' + this.labelmd;
12448 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12451 if(this.labelsm > 0){
12452 labelCfg.cls += ' col-sm-' + this.labelsm;
12453 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12456 if(this.labelxs > 0){
12457 labelCfg.cls += ' col-xs-' + this.labelxs;
12458 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12461 } else if ( this.fieldLabel.length) {
12462 // Roo.log(" label");
12467 //cls : 'input-group-addon',
12468 html : this.fieldLabel
12476 if(this.indicatorpos == 'right'){
12484 html : this.fieldLabel
12498 // Roo.log(" no label && no align");
12505 ['xs','sm','md','lg'].map(function(size){
12506 if (settings[size]) {
12507 cfg.cls += ' col-' + size + '-' + settings[size];
12518 onResize : function(w, h){
12519 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12520 // if(typeof w == 'number'){
12521 // var x = w - this.trigger.getWidth();
12522 // this.inputEl().setWidth(this.adjustWidth('input', x));
12523 // this.trigger.setStyle('left', x+'px');
12528 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12531 getResizeEl : function(){
12532 return this.inputEl();
12536 getPositionEl : function(){
12537 return this.inputEl();
12541 alignErrorIcon : function(){
12542 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12546 initEvents : function(){
12550 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12551 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12552 if(!this.multiple && this.showToggleBtn){
12553 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12554 if(this.hideTrigger){
12555 this.trigger.setDisplayed(false);
12557 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12561 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12564 if(this.removable && !this.editable && !this.tickable){
12565 var close = this.closeTriggerEl();
12568 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12569 close.on('click', this.removeBtnClick, this, close);
12573 //this.trigger.addClassOnOver('x-form-trigger-over');
12574 //this.trigger.addClassOnClick('x-form-trigger-click');
12577 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12581 closeTriggerEl : function()
12583 var close = this.el.select('.roo-combo-removable-btn', true).first();
12584 return close ? close : false;
12587 removeBtnClick : function(e, h, el)
12589 e.preventDefault();
12591 if(this.fireEvent("remove", this) !== false){
12593 this.fireEvent("afterremove", this)
12597 createList : function()
12599 this.list = Roo.get(document.body).createChild({
12600 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12601 cls: 'typeahead typeahead-long dropdown-menu shadow',
12602 style: 'display:none'
12605 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12610 initTrigger : function(){
12615 onDestroy : function(){
12617 this.trigger.removeAllListeners();
12618 // this.trigger.remove();
12621 // this.wrap.remove();
12623 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12627 onFocus : function(){
12628 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12630 if(!this.mimicing){
12631 this.wrap.addClass('x-trigger-wrap-focus');
12632 this.mimicing = true;
12633 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12634 if(this.monitorTab){
12635 this.el.on("keydown", this.checkTab, this);
12642 checkTab : function(e){
12643 if(e.getKey() == e.TAB){
12644 this.triggerBlur();
12649 onBlur : function(){
12654 mimicBlur : function(e, t){
12656 if(!this.wrap.contains(t) && this.validateBlur()){
12657 this.triggerBlur();
12663 triggerBlur : function(){
12664 this.mimicing = false;
12665 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12666 if(this.monitorTab){
12667 this.el.un("keydown", this.checkTab, this);
12669 //this.wrap.removeClass('x-trigger-wrap-focus');
12670 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12674 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12675 validateBlur : function(e, t){
12680 onDisable : function(){
12681 this.inputEl().dom.disabled = true;
12682 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12684 // this.wrap.addClass('x-item-disabled');
12689 onEnable : function(){
12690 this.inputEl().dom.disabled = false;
12691 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12693 // this.el.removeClass('x-item-disabled');
12698 onShow : function(){
12699 var ae = this.getActionEl();
12702 ae.dom.style.display = '';
12703 ae.dom.style.visibility = 'visible';
12709 onHide : function(){
12710 var ae = this.getActionEl();
12711 ae.dom.style.display = 'none';
12715 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12716 * by an implementing function.
12718 * @param {EventObject} e
12720 onTriggerClick : Roo.emptyFn
12728 * @class Roo.bootstrap.CardUploader
12729 * @extends Roo.bootstrap.Button
12730 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12731 * @cfg {Number} errorTimeout default 3000
12732 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12733 * @cfg {Array} html The button text.
12737 * Create a new CardUploader
12738 * @param {Object} config The config object
12741 Roo.bootstrap.CardUploader = function(config){
12745 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12748 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12756 * When a image is clicked on - and needs to display a slideshow or similar..
12757 * @param {Roo.bootstrap.Card} this
12758 * @param {Object} The image information data
12764 * When a the download link is clicked
12765 * @param {Roo.bootstrap.Card} this
12766 * @param {Object} The image information data contains
12773 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12776 errorTimeout : 3000,
12780 fileCollection : false,
12783 getAutoCreate : function()
12787 cls :'form-group' ,
12792 //cls : 'input-group-addon',
12793 html : this.fieldLabel
12801 value : this.value,
12802 cls : 'd-none form-control'
12807 multiple : 'multiple',
12809 cls : 'd-none roo-card-upload-selector'
12813 cls : 'roo-card-uploader-button-container w-100 mb-2'
12816 cls : 'card-columns roo-card-uploader-container'
12826 getChildContainer : function() /// what children are added to.
12828 return this.containerEl;
12831 getButtonContainer : function() /// what children are added to.
12833 return this.el.select(".roo-card-uploader-button-container").first();
12836 initEvents : function()
12839 Roo.bootstrap.Input.prototype.initEvents.call(this);
12843 xns: Roo.bootstrap,
12846 container_method : 'getButtonContainer' ,
12847 html : this.html, // fix changable?
12850 'click' : function(btn, e) {
12859 this.urlAPI = (window.createObjectURL && window) ||
12860 (window.URL && URL.revokeObjectURL && URL) ||
12861 (window.webkitURL && webkitURL);
12866 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12868 this.selectorEl.on('change', this.onFileSelected, this);
12871 this.images.forEach(function(img) {
12874 this.images = false;
12876 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12882 onClick : function(e)
12884 e.preventDefault();
12886 this.selectorEl.dom.click();
12890 onFileSelected : function(e)
12892 e.preventDefault();
12894 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12898 Roo.each(this.selectorEl.dom.files, function(file){
12899 this.addFile(file);
12908 addFile : function(file)
12911 if(typeof(file) === 'string'){
12912 throw "Add file by name?"; // should not happen
12916 if(!file || !this.urlAPI){
12926 var url = _this.urlAPI.createObjectURL( file);
12929 id : Roo.bootstrap.CardUploader.ID--,
12930 is_uploaded : false,
12934 mimetype : file.type,
12942 * addCard - add an Attachment to the uploader
12943 * @param data - the data about the image to upload
12947 title : "Title of file",
12948 is_uploaded : false,
12949 src : "http://.....",
12950 srcfile : { the File upload object },
12951 mimetype : file.type,
12954 .. any other data...
12960 addCard : function (data)
12962 // hidden input element?
12963 // if the file is not an image...
12964 //then we need to use something other that and header_image
12969 xns : Roo.bootstrap,
12970 xtype : 'CardFooter',
12973 xns : Roo.bootstrap,
12979 xns : Roo.bootstrap,
12981 html : String.format("<small>{0}</small>", data.title),
12982 cls : 'col-10 text-left',
12987 click : function() {
12989 t.fireEvent( "download", t, data );
12995 xns : Roo.bootstrap,
12997 style: 'max-height: 28px; ',
13003 click : function() {
13004 t.removeCard(data.id)
13016 var cn = this.addxtype(
13019 xns : Roo.bootstrap,
13022 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13023 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13024 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13029 initEvents : function() {
13030 Roo.bootstrap.Card.prototype.initEvents.call(this);
13032 this.imgEl = this.el.select('.card-img-top').first();
13034 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13035 this.imgEl.set({ 'pointer' : 'cursor' });
13038 this.getCardFooter().addClass('p-1');
13045 // dont' really need ot update items.
13046 // this.items.push(cn);
13047 this.fileCollection.add(cn);
13049 if (!data.srcfile) {
13050 this.updateInput();
13055 var reader = new FileReader();
13056 reader.addEventListener("load", function() {
13057 data.srcdata = reader.result;
13060 reader.readAsDataURL(data.srcfile);
13065 removeCard : function(id)
13068 var card = this.fileCollection.get(id);
13069 card.data.is_deleted = 1;
13070 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13071 //this.fileCollection.remove(card);
13072 //this.items = this.items.filter(function(e) { return e != card });
13073 // dont' really need ot update items.
13074 card.el.dom.parentNode.removeChild(card.el.dom);
13075 this.updateInput();
13081 this.fileCollection.each(function(card) {
13082 if (card.el.dom && card.el.dom.parentNode) {
13083 card.el.dom.parentNode.removeChild(card.el.dom);
13086 this.fileCollection.clear();
13087 this.updateInput();
13090 updateInput : function()
13093 this.fileCollection.each(function(e) {
13097 this.inputEl().dom.value = JSON.stringify(data);
13107 Roo.bootstrap.CardUploader.ID = -1;/*
13109 * Ext JS Library 1.1.1
13110 * Copyright(c) 2006-2007, Ext JS, LLC.
13112 * Originally Released Under LGPL - original licence link has changed is not relivant.
13115 * <script type="text/javascript">
13120 * @class Roo.data.SortTypes
13122 * Defines the default sorting (casting?) comparison functions used when sorting data.
13124 Roo.data.SortTypes = {
13126 * Default sort that does nothing
13127 * @param {Mixed} s The value being converted
13128 * @return {Mixed} The comparison value
13130 none : function(s){
13135 * The regular expression used to strip tags
13139 stripTagsRE : /<\/?[^>]+>/gi,
13142 * Strips all HTML tags to sort on text only
13143 * @param {Mixed} s The value being converted
13144 * @return {String} The comparison value
13146 asText : function(s){
13147 return String(s).replace(this.stripTagsRE, "");
13151 * Strips all HTML tags to sort on text only - Case insensitive
13152 * @param {Mixed} s The value being converted
13153 * @return {String} The comparison value
13155 asUCText : function(s){
13156 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13160 * Case insensitive string
13161 * @param {Mixed} s The value being converted
13162 * @return {String} The comparison value
13164 asUCString : function(s) {
13165 return String(s).toUpperCase();
13170 * @param {Mixed} s The value being converted
13171 * @return {Number} The comparison value
13173 asDate : function(s) {
13177 if(s instanceof Date){
13178 return s.getTime();
13180 return Date.parse(String(s));
13185 * @param {Mixed} s The value being converted
13186 * @return {Float} The comparison value
13188 asFloat : function(s) {
13189 var val = parseFloat(String(s).replace(/,/g, ""));
13198 * @param {Mixed} s The value being converted
13199 * @return {Number} The comparison value
13201 asInt : function(s) {
13202 var val = parseInt(String(s).replace(/,/g, ""));
13210 * Ext JS Library 1.1.1
13211 * Copyright(c) 2006-2007, Ext JS, LLC.
13213 * Originally Released Under LGPL - original licence link has changed is not relivant.
13216 * <script type="text/javascript">
13220 * @class Roo.data.Record
13221 * Instances of this class encapsulate both record <em>definition</em> information, and record
13222 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13223 * to access Records cached in an {@link Roo.data.Store} object.<br>
13225 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13226 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13229 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13231 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13232 * {@link #create}. The parameters are the same.
13233 * @param {Array} data An associative Array of data values keyed by the field name.
13234 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13235 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13236 * not specified an integer id is generated.
13238 Roo.data.Record = function(data, id){
13239 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13244 * Generate a constructor for a specific record layout.
13245 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13246 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13247 * Each field definition object may contain the following properties: <ul>
13248 * <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,
13249 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13250 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13251 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13252 * is being used, then this is a string containing the javascript expression to reference the data relative to
13253 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13254 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13255 * this may be omitted.</p></li>
13256 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13257 * <ul><li>auto (Default, implies no conversion)</li>
13262 * <li>date</li></ul></p></li>
13263 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13264 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13265 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13266 * by the Reader into an object that will be stored in the Record. It is passed the
13267 * following parameters:<ul>
13268 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13270 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13272 * <br>usage:<br><pre><code>
13273 var TopicRecord = Roo.data.Record.create(
13274 {name: 'title', mapping: 'topic_title'},
13275 {name: 'author', mapping: 'username'},
13276 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13277 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13278 {name: 'lastPoster', mapping: 'user2'},
13279 {name: 'excerpt', mapping: 'post_text'}
13282 var myNewRecord = new TopicRecord({
13283 title: 'Do my job please',
13286 lastPost: new Date(),
13287 lastPoster: 'Animal',
13288 excerpt: 'No way dude!'
13290 myStore.add(myNewRecord);
13295 Roo.data.Record.create = function(o){
13296 var f = function(){
13297 f.superclass.constructor.apply(this, arguments);
13299 Roo.extend(f, Roo.data.Record);
13300 var p = f.prototype;
13301 p.fields = new Roo.util.MixedCollection(false, function(field){
13304 for(var i = 0, len = o.length; i < len; i++){
13305 p.fields.add(new Roo.data.Field(o[i]));
13307 f.getField = function(name){
13308 return p.fields.get(name);
13313 Roo.data.Record.AUTO_ID = 1000;
13314 Roo.data.Record.EDIT = 'edit';
13315 Roo.data.Record.REJECT = 'reject';
13316 Roo.data.Record.COMMIT = 'commit';
13318 Roo.data.Record.prototype = {
13320 * Readonly flag - true if this record has been modified.
13329 join : function(store){
13330 this.store = store;
13334 * Set the named field to the specified value.
13335 * @param {String} name The name of the field to set.
13336 * @param {Object} value The value to set the field to.
13338 set : function(name, value){
13339 if(this.data[name] == value){
13343 if(!this.modified){
13344 this.modified = {};
13346 if(typeof this.modified[name] == 'undefined'){
13347 this.modified[name] = this.data[name];
13349 this.data[name] = value;
13350 if(!this.editing && this.store){
13351 this.store.afterEdit(this);
13356 * Get the value of the named field.
13357 * @param {String} name The name of the field to get the value of.
13358 * @return {Object} The value of the field.
13360 get : function(name){
13361 return this.data[name];
13365 beginEdit : function(){
13366 this.editing = true;
13367 this.modified = {};
13371 cancelEdit : function(){
13372 this.editing = false;
13373 delete this.modified;
13377 endEdit : function(){
13378 this.editing = false;
13379 if(this.dirty && this.store){
13380 this.store.afterEdit(this);
13385 * Usually called by the {@link Roo.data.Store} which owns the Record.
13386 * Rejects all changes made to the Record since either creation, or the last commit operation.
13387 * Modified fields are reverted to their original values.
13389 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13390 * of reject operations.
13392 reject : function(){
13393 var m = this.modified;
13395 if(typeof m[n] != "function"){
13396 this.data[n] = m[n];
13399 this.dirty = false;
13400 delete this.modified;
13401 this.editing = false;
13403 this.store.afterReject(this);
13408 * Usually called by the {@link Roo.data.Store} which owns the Record.
13409 * Commits all changes made to the Record since either creation, or the last commit operation.
13411 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13412 * of commit operations.
13414 commit : function(){
13415 this.dirty = false;
13416 delete this.modified;
13417 this.editing = false;
13419 this.store.afterCommit(this);
13424 hasError : function(){
13425 return this.error != null;
13429 clearError : function(){
13434 * Creates a copy of this record.
13435 * @param {String} id (optional) A new record id if you don't want to use this record's id
13438 copy : function(newId) {
13439 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13443 * Ext JS Library 1.1.1
13444 * Copyright(c) 2006-2007, Ext JS, LLC.
13446 * Originally Released Under LGPL - original licence link has changed is not relivant.
13449 * <script type="text/javascript">
13455 * @class Roo.data.Store
13456 * @extends Roo.util.Observable
13457 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13458 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13460 * 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
13461 * has no knowledge of the format of the data returned by the Proxy.<br>
13463 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13464 * instances from the data object. These records are cached and made available through accessor functions.
13466 * Creates a new Store.
13467 * @param {Object} config A config object containing the objects needed for the Store to access data,
13468 * and read the data into Records.
13470 Roo.data.Store = function(config){
13471 this.data = new Roo.util.MixedCollection(false);
13472 this.data.getKey = function(o){
13475 this.baseParams = {};
13477 this.paramNames = {
13482 "multisort" : "_multisort"
13485 if(config && config.data){
13486 this.inlineData = config.data;
13487 delete config.data;
13490 Roo.apply(this, config);
13492 if(this.reader){ // reader passed
13493 this.reader = Roo.factory(this.reader, Roo.data);
13494 this.reader.xmodule = this.xmodule || false;
13495 if(!this.recordType){
13496 this.recordType = this.reader.recordType;
13498 if(this.reader.onMetaChange){
13499 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13503 if(this.recordType){
13504 this.fields = this.recordType.prototype.fields;
13506 this.modified = [];
13510 * @event datachanged
13511 * Fires when the data cache has changed, and a widget which is using this Store
13512 * as a Record cache should refresh its view.
13513 * @param {Store} this
13515 datachanged : true,
13517 * @event metachange
13518 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13519 * @param {Store} this
13520 * @param {Object} meta The JSON metadata
13525 * Fires when Records have been added to the Store
13526 * @param {Store} this
13527 * @param {Roo.data.Record[]} records The array of Records added
13528 * @param {Number} index The index at which the record(s) were added
13533 * Fires when a Record has been removed from the Store
13534 * @param {Store} this
13535 * @param {Roo.data.Record} record The Record that was removed
13536 * @param {Number} index The index at which the record was removed
13541 * Fires when a Record has been updated
13542 * @param {Store} this
13543 * @param {Roo.data.Record} record The Record that was updated
13544 * @param {String} operation The update operation being performed. Value may be one of:
13546 Roo.data.Record.EDIT
13547 Roo.data.Record.REJECT
13548 Roo.data.Record.COMMIT
13554 * Fires when the data cache has been cleared.
13555 * @param {Store} this
13559 * @event beforeload
13560 * Fires before a request is made for a new data object. If the beforeload handler returns false
13561 * the load action will be canceled.
13562 * @param {Store} this
13563 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13567 * @event beforeloadadd
13568 * Fires after a new set of Records has been loaded.
13569 * @param {Store} this
13570 * @param {Roo.data.Record[]} records The Records that were loaded
13571 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13573 beforeloadadd : true,
13576 * Fires after a new set of Records has been loaded, before they are added to the store.
13577 * @param {Store} this
13578 * @param {Roo.data.Record[]} records The Records that were loaded
13579 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13580 * @params {Object} return from reader
13584 * @event loadexception
13585 * Fires if an exception occurs in the Proxy during loading.
13586 * Called with the signature of the Proxy's "loadexception" event.
13587 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13590 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13591 * @param {Object} load options
13592 * @param {Object} jsonData from your request (normally this contains the Exception)
13594 loadexception : true
13598 this.proxy = Roo.factory(this.proxy, Roo.data);
13599 this.proxy.xmodule = this.xmodule || false;
13600 this.relayEvents(this.proxy, ["loadexception"]);
13602 this.sortToggle = {};
13603 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13605 Roo.data.Store.superclass.constructor.call(this);
13607 if(this.inlineData){
13608 this.loadData(this.inlineData);
13609 delete this.inlineData;
13613 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13615 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13616 * without a remote query - used by combo/forms at present.
13620 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13623 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13626 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13627 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13630 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13631 * on any HTTP request
13634 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13637 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13641 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13642 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13644 remoteSort : false,
13647 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13648 * loaded or when a record is removed. (defaults to false).
13650 pruneModifiedRecords : false,
13653 lastOptions : null,
13656 * Add Records to the Store and fires the add event.
13657 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13659 add : function(records){
13660 records = [].concat(records);
13661 for(var i = 0, len = records.length; i < len; i++){
13662 records[i].join(this);
13664 var index = this.data.length;
13665 this.data.addAll(records);
13666 this.fireEvent("add", this, records, index);
13670 * Remove a Record from the Store and fires the remove event.
13671 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13673 remove : function(record){
13674 var index = this.data.indexOf(record);
13675 this.data.removeAt(index);
13677 if(this.pruneModifiedRecords){
13678 this.modified.remove(record);
13680 this.fireEvent("remove", this, record, index);
13684 * Remove all Records from the Store and fires the clear event.
13686 removeAll : function(){
13688 if(this.pruneModifiedRecords){
13689 this.modified = [];
13691 this.fireEvent("clear", this);
13695 * Inserts Records to the Store at the given index and fires the add event.
13696 * @param {Number} index The start index at which to insert the passed Records.
13697 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13699 insert : function(index, records){
13700 records = [].concat(records);
13701 for(var i = 0, len = records.length; i < len; i++){
13702 this.data.insert(index, records[i]);
13703 records[i].join(this);
13705 this.fireEvent("add", this, records, index);
13709 * Get the index within the cache of the passed Record.
13710 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13711 * @return {Number} The index of the passed Record. Returns -1 if not found.
13713 indexOf : function(record){
13714 return this.data.indexOf(record);
13718 * Get the index within the cache of the Record with the passed id.
13719 * @param {String} id The id of the Record to find.
13720 * @return {Number} The index of the Record. Returns -1 if not found.
13722 indexOfId : function(id){
13723 return this.data.indexOfKey(id);
13727 * Get the Record with the specified id.
13728 * @param {String} id The id of the Record to find.
13729 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13731 getById : function(id){
13732 return this.data.key(id);
13736 * Get the Record at the specified index.
13737 * @param {Number} index The index of the Record to find.
13738 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13740 getAt : function(index){
13741 return this.data.itemAt(index);
13745 * Returns a range of Records between specified indices.
13746 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13747 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13748 * @return {Roo.data.Record[]} An array of Records
13750 getRange : function(start, end){
13751 return this.data.getRange(start, end);
13755 storeOptions : function(o){
13756 o = Roo.apply({}, o);
13759 this.lastOptions = o;
13763 * Loads the Record cache from the configured Proxy using the configured Reader.
13765 * If using remote paging, then the first load call must specify the <em>start</em>
13766 * and <em>limit</em> properties in the options.params property to establish the initial
13767 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13769 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13770 * and this call will return before the new data has been loaded. Perform any post-processing
13771 * in a callback function, or in a "load" event handler.</strong>
13773 * @param {Object} options An object containing properties which control loading options:<ul>
13774 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13775 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13776 * passed the following arguments:<ul>
13777 * <li>r : Roo.data.Record[]</li>
13778 * <li>options: Options object from the load call</li>
13779 * <li>success: Boolean success indicator</li></ul></li>
13780 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13781 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13784 load : function(options){
13785 options = options || {};
13786 if(this.fireEvent("beforeload", this, options) !== false){
13787 this.storeOptions(options);
13788 var p = Roo.apply(options.params || {}, this.baseParams);
13789 // if meta was not loaded from remote source.. try requesting it.
13790 if (!this.reader.metaFromRemote) {
13791 p._requestMeta = 1;
13793 if(this.sortInfo && this.remoteSort){
13794 var pn = this.paramNames;
13795 p[pn["sort"]] = this.sortInfo.field;
13796 p[pn["dir"]] = this.sortInfo.direction;
13798 if (this.multiSort) {
13799 var pn = this.paramNames;
13800 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13803 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13808 * Reloads the Record cache from the configured Proxy using the configured Reader and
13809 * the options from the last load operation performed.
13810 * @param {Object} options (optional) An object containing properties which may override the options
13811 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13812 * the most recently used options are reused).
13814 reload : function(options){
13815 this.load(Roo.applyIf(options||{}, this.lastOptions));
13819 // Called as a callback by the Reader during a load operation.
13820 loadRecords : function(o, options, success){
13821 if(!o || success === false){
13822 if(success !== false){
13823 this.fireEvent("load", this, [], options, o);
13825 if(options.callback){
13826 options.callback.call(options.scope || this, [], options, false);
13830 // if data returned failure - throw an exception.
13831 if (o.success === false) {
13832 // show a message if no listener is registered.
13833 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13834 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13836 // loadmask wil be hooked into this..
13837 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13840 var r = o.records, t = o.totalRecords || r.length;
13842 this.fireEvent("beforeloadadd", this, r, options, o);
13844 if(!options || options.add !== true){
13845 if(this.pruneModifiedRecords){
13846 this.modified = [];
13848 for(var i = 0, len = r.length; i < len; i++){
13852 this.data = this.snapshot;
13853 delete this.snapshot;
13856 this.data.addAll(r);
13857 this.totalLength = t;
13859 this.fireEvent("datachanged", this);
13861 this.totalLength = Math.max(t, this.data.length+r.length);
13865 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13867 var e = new Roo.data.Record({});
13869 e.set(this.parent.displayField, this.parent.emptyTitle);
13870 e.set(this.parent.valueField, '');
13875 this.fireEvent("load", this, r, options, o);
13876 if(options.callback){
13877 options.callback.call(options.scope || this, r, options, true);
13883 * Loads data from a passed data block. A Reader which understands the format of the data
13884 * must have been configured in the constructor.
13885 * @param {Object} data The data block from which to read the Records. The format of the data expected
13886 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13887 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13889 loadData : function(o, append){
13890 var r = this.reader.readRecords(o);
13891 this.loadRecords(r, {add: append}, true);
13895 * using 'cn' the nested child reader read the child array into it's child stores.
13896 * @param {Object} rec The record with a 'children array
13898 loadDataFromChildren : function(rec)
13900 this.loadData(this.reader.toLoadData(rec));
13905 * Gets the number of cached records.
13907 * <em>If using paging, this may not be the total size of the dataset. If the data object
13908 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13909 * the data set size</em>
13911 getCount : function(){
13912 return this.data.length || 0;
13916 * Gets the total number of records in the dataset as returned by the server.
13918 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13919 * the dataset size</em>
13921 getTotalCount : function(){
13922 return this.totalLength || 0;
13926 * Returns the sort state of the Store as an object with two properties:
13928 field {String} The name of the field by which the Records are sorted
13929 direction {String} The sort order, "ASC" or "DESC"
13932 getSortState : function(){
13933 return this.sortInfo;
13937 applySort : function(){
13938 if(this.sortInfo && !this.remoteSort){
13939 var s = this.sortInfo, f = s.field;
13940 var st = this.fields.get(f).sortType;
13941 var fn = function(r1, r2){
13942 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13943 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13945 this.data.sort(s.direction, fn);
13946 if(this.snapshot && this.snapshot != this.data){
13947 this.snapshot.sort(s.direction, fn);
13953 * Sets the default sort column and order to be used by the next load operation.
13954 * @param {String} fieldName The name of the field to sort by.
13955 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13957 setDefaultSort : function(field, dir){
13958 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13962 * Sort the Records.
13963 * If remote sorting is used, the sort is performed on the server, and the cache is
13964 * reloaded. If local sorting is used, the cache is sorted internally.
13965 * @param {String} fieldName The name of the field to sort by.
13966 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13968 sort : function(fieldName, dir){
13969 var f = this.fields.get(fieldName);
13971 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13973 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13974 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13979 this.sortToggle[f.name] = dir;
13980 this.sortInfo = {field: f.name, direction: dir};
13981 if(!this.remoteSort){
13983 this.fireEvent("datachanged", this);
13985 this.load(this.lastOptions);
13990 * Calls the specified function for each of the Records in the cache.
13991 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13992 * Returning <em>false</em> aborts and exits the iteration.
13993 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13995 each : function(fn, scope){
13996 this.data.each(fn, scope);
14000 * Gets all records modified since the last commit. Modified records are persisted across load operations
14001 * (e.g., during paging).
14002 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14004 getModifiedRecords : function(){
14005 return this.modified;
14009 createFilterFn : function(property, value, anyMatch){
14010 if(!value.exec){ // not a regex
14011 value = String(value);
14012 if(value.length == 0){
14015 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14017 return function(r){
14018 return value.test(r.data[property]);
14023 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14024 * @param {String} property A field on your records
14025 * @param {Number} start The record index to start at (defaults to 0)
14026 * @param {Number} end The last record index to include (defaults to length - 1)
14027 * @return {Number} The sum
14029 sum : function(property, start, end){
14030 var rs = this.data.items, v = 0;
14031 start = start || 0;
14032 end = (end || end === 0) ? end : rs.length-1;
14034 for(var i = start; i <= end; i++){
14035 v += (rs[i].data[property] || 0);
14041 * Filter the records by a specified property.
14042 * @param {String} field A field on your records
14043 * @param {String/RegExp} value Either a string that the field
14044 * should start with or a RegExp to test against the field
14045 * @param {Boolean} anyMatch True to match any part not just the beginning
14047 filter : function(property, value, anyMatch){
14048 var fn = this.createFilterFn(property, value, anyMatch);
14049 return fn ? this.filterBy(fn) : this.clearFilter();
14053 * Filter by a function. The specified function will be called with each
14054 * record in this data source. If the function returns true the record is included,
14055 * otherwise it is filtered.
14056 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14057 * @param {Object} scope (optional) The scope of the function (defaults to this)
14059 filterBy : function(fn, scope){
14060 this.snapshot = this.snapshot || this.data;
14061 this.data = this.queryBy(fn, scope||this);
14062 this.fireEvent("datachanged", this);
14066 * Query the records by a specified property.
14067 * @param {String} field A field on your records
14068 * @param {String/RegExp} value Either a string that the field
14069 * should start with or a RegExp to test against the field
14070 * @param {Boolean} anyMatch True to match any part not just the beginning
14071 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14073 query : function(property, value, anyMatch){
14074 var fn = this.createFilterFn(property, value, anyMatch);
14075 return fn ? this.queryBy(fn) : this.data.clone();
14079 * Query by a function. The specified function will be called with each
14080 * record in this data source. If the function returns true the record is included
14082 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14083 * @param {Object} scope (optional) The scope of the function (defaults to this)
14084 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14086 queryBy : function(fn, scope){
14087 var data = this.snapshot || this.data;
14088 return data.filterBy(fn, scope||this);
14092 * Collects unique values for a particular dataIndex from this store.
14093 * @param {String} dataIndex The property to collect
14094 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14095 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14096 * @return {Array} An array of the unique values
14098 collect : function(dataIndex, allowNull, bypassFilter){
14099 var d = (bypassFilter === true && this.snapshot) ?
14100 this.snapshot.items : this.data.items;
14101 var v, sv, r = [], l = {};
14102 for(var i = 0, len = d.length; i < len; i++){
14103 v = d[i].data[dataIndex];
14105 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14114 * Revert to a view of the Record cache with no filtering applied.
14115 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14117 clearFilter : function(suppressEvent){
14118 if(this.snapshot && this.snapshot != this.data){
14119 this.data = this.snapshot;
14120 delete this.snapshot;
14121 if(suppressEvent !== true){
14122 this.fireEvent("datachanged", this);
14128 afterEdit : function(record){
14129 if(this.modified.indexOf(record) == -1){
14130 this.modified.push(record);
14132 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14136 afterReject : function(record){
14137 this.modified.remove(record);
14138 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14142 afterCommit : function(record){
14143 this.modified.remove(record);
14144 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14148 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14149 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14151 commitChanges : function(){
14152 var m = this.modified.slice(0);
14153 this.modified = [];
14154 for(var i = 0, len = m.length; i < len; i++){
14160 * Cancel outstanding changes on all changed records.
14162 rejectChanges : function(){
14163 var m = this.modified.slice(0);
14164 this.modified = [];
14165 for(var i = 0, len = m.length; i < len; i++){
14170 onMetaChange : function(meta, rtype, o){
14171 this.recordType = rtype;
14172 this.fields = rtype.prototype.fields;
14173 delete this.snapshot;
14174 this.sortInfo = meta.sortInfo || this.sortInfo;
14175 this.modified = [];
14176 this.fireEvent('metachange', this, this.reader.meta);
14179 moveIndex : function(data, type)
14181 var index = this.indexOf(data);
14183 var newIndex = index + type;
14187 this.insert(newIndex, data);
14192 * Ext JS Library 1.1.1
14193 * Copyright(c) 2006-2007, Ext JS, LLC.
14195 * Originally Released Under LGPL - original licence link has changed is not relivant.
14198 * <script type="text/javascript">
14202 * @class Roo.data.SimpleStore
14203 * @extends Roo.data.Store
14204 * Small helper class to make creating Stores from Array data easier.
14205 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14206 * @cfg {Array} fields An array of field definition objects, or field name strings.
14207 * @cfg {Object} an existing reader (eg. copied from another store)
14208 * @cfg {Array} data The multi-dimensional array of data
14210 * @param {Object} config
14212 Roo.data.SimpleStore = function(config)
14214 Roo.data.SimpleStore.superclass.constructor.call(this, {
14216 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14219 Roo.data.Record.create(config.fields)
14221 proxy : new Roo.data.MemoryProxy(config.data)
14225 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14227 * Ext JS Library 1.1.1
14228 * Copyright(c) 2006-2007, Ext JS, LLC.
14230 * Originally Released Under LGPL - original licence link has changed is not relivant.
14233 * <script type="text/javascript">
14238 * @extends Roo.data.Store
14239 * @class Roo.data.JsonStore
14240 * Small helper class to make creating Stores for JSON data easier. <br/>
14242 var store = new Roo.data.JsonStore({
14243 url: 'get-images.php',
14245 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14248 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14249 * JsonReader and HttpProxy (unless inline data is provided).</b>
14250 * @cfg {Array} fields An array of field definition objects, or field name strings.
14252 * @param {Object} config
14254 Roo.data.JsonStore = function(c){
14255 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14256 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14257 reader: new Roo.data.JsonReader(c, c.fields)
14260 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14262 * Ext JS Library 1.1.1
14263 * Copyright(c) 2006-2007, Ext JS, LLC.
14265 * Originally Released Under LGPL - original licence link has changed is not relivant.
14268 * <script type="text/javascript">
14272 Roo.data.Field = function(config){
14273 if(typeof config == "string"){
14274 config = {name: config};
14276 Roo.apply(this, config);
14279 this.type = "auto";
14282 var st = Roo.data.SortTypes;
14283 // named sortTypes are supported, here we look them up
14284 if(typeof this.sortType == "string"){
14285 this.sortType = st[this.sortType];
14288 // set default sortType for strings and dates
14289 if(!this.sortType){
14292 this.sortType = st.asUCString;
14295 this.sortType = st.asDate;
14298 this.sortType = st.none;
14303 var stripRe = /[\$,%]/g;
14305 // prebuilt conversion function for this field, instead of
14306 // switching every time we're reading a value
14308 var cv, dateFormat = this.dateFormat;
14313 cv = function(v){ return v; };
14316 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14320 return v !== undefined && v !== null && v !== '' ?
14321 parseInt(String(v).replace(stripRe, ""), 10) : '';
14326 return v !== undefined && v !== null && v !== '' ?
14327 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14332 cv = function(v){ return v === true || v === "true" || v == 1; };
14339 if(v instanceof Date){
14343 if(dateFormat == "timestamp"){
14344 return new Date(v*1000);
14346 return Date.parseDate(v, dateFormat);
14348 var parsed = Date.parse(v);
14349 return parsed ? new Date(parsed) : null;
14358 Roo.data.Field.prototype = {
14366 * Ext JS Library 1.1.1
14367 * Copyright(c) 2006-2007, Ext JS, LLC.
14369 * Originally Released Under LGPL - original licence link has changed is not relivant.
14372 * <script type="text/javascript">
14375 // Base class for reading structured data from a data source. This class is intended to be
14376 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14379 * @class Roo.data.DataReader
14380 * Base class for reading structured data from a data source. This class is intended to be
14381 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14384 Roo.data.DataReader = function(meta, recordType){
14388 this.recordType = recordType instanceof Array ?
14389 Roo.data.Record.create(recordType) : recordType;
14392 Roo.data.DataReader.prototype = {
14395 readerType : 'Data',
14397 * Create an empty record
14398 * @param {Object} data (optional) - overlay some values
14399 * @return {Roo.data.Record} record created.
14401 newRow : function(d) {
14403 this.recordType.prototype.fields.each(function(c) {
14405 case 'int' : da[c.name] = 0; break;
14406 case 'date' : da[c.name] = new Date(); break;
14407 case 'float' : da[c.name] = 0.0; break;
14408 case 'boolean' : da[c.name] = false; break;
14409 default : da[c.name] = ""; break;
14413 return new this.recordType(Roo.apply(da, d));
14419 * Ext JS Library 1.1.1
14420 * Copyright(c) 2006-2007, Ext JS, LLC.
14422 * Originally Released Under LGPL - original licence link has changed is not relivant.
14425 * <script type="text/javascript">
14429 * @class Roo.data.DataProxy
14430 * @extends Roo.data.Observable
14431 * This class is an abstract base class for implementations which provide retrieval of
14432 * unformatted data objects.<br>
14434 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14435 * (of the appropriate type which knows how to parse the data object) to provide a block of
14436 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14438 * Custom implementations must implement the load method as described in
14439 * {@link Roo.data.HttpProxy#load}.
14441 Roo.data.DataProxy = function(){
14444 * @event beforeload
14445 * Fires before a network request is made to retrieve a data object.
14446 * @param {Object} This DataProxy object.
14447 * @param {Object} params The params parameter to the load function.
14452 * Fires before the load method's callback is called.
14453 * @param {Object} This DataProxy object.
14454 * @param {Object} o The data object.
14455 * @param {Object} arg The callback argument object passed to the load function.
14459 * @event loadexception
14460 * Fires if an Exception occurs during data retrieval.
14461 * @param {Object} This DataProxy object.
14462 * @param {Object} o The data object.
14463 * @param {Object} arg The callback argument object passed to the load function.
14464 * @param {Object} e The Exception.
14466 loadexception : true
14468 Roo.data.DataProxy.superclass.constructor.call(this);
14471 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14474 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14478 * Ext JS Library 1.1.1
14479 * Copyright(c) 2006-2007, Ext JS, LLC.
14481 * Originally Released Under LGPL - original licence link has changed is not relivant.
14484 * <script type="text/javascript">
14487 * @class Roo.data.MemoryProxy
14488 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14489 * to the Reader when its load method is called.
14491 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14493 Roo.data.MemoryProxy = function(data){
14497 Roo.data.MemoryProxy.superclass.constructor.call(this);
14501 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14504 * Load data from the requested source (in this case an in-memory
14505 * data object passed to the constructor), read the data object into
14506 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14507 * process that block using the passed callback.
14508 * @param {Object} params This parameter is not used by the MemoryProxy class.
14509 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14510 * object into a block of Roo.data.Records.
14511 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14512 * The function must be passed <ul>
14513 * <li>The Record block object</li>
14514 * <li>The "arg" argument from the load function</li>
14515 * <li>A boolean success indicator</li>
14517 * @param {Object} scope The scope in which to call the callback
14518 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14520 load : function(params, reader, callback, scope, arg){
14521 params = params || {};
14524 result = reader.readRecords(params.data ? params.data :this.data);
14526 this.fireEvent("loadexception", this, arg, null, e);
14527 callback.call(scope, null, arg, false);
14530 callback.call(scope, result, arg, true);
14534 update : function(params, records){
14539 * Ext JS Library 1.1.1
14540 * Copyright(c) 2006-2007, Ext JS, LLC.
14542 * Originally Released Under LGPL - original licence link has changed is not relivant.
14545 * <script type="text/javascript">
14548 * @class Roo.data.HttpProxy
14549 * @extends Roo.data.DataProxy
14550 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14551 * configured to reference a certain URL.<br><br>
14553 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14554 * from which the running page was served.<br><br>
14556 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14558 * Be aware that to enable the browser to parse an XML document, the server must set
14559 * the Content-Type header in the HTTP response to "text/xml".
14561 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14562 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14563 * will be used to make the request.
14565 Roo.data.HttpProxy = function(conn){
14566 Roo.data.HttpProxy.superclass.constructor.call(this);
14567 // is conn a conn config or a real conn?
14569 this.useAjax = !conn || !conn.events;
14573 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14574 // thse are take from connection...
14577 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14580 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14581 * extra parameters to each request made by this object. (defaults to undefined)
14584 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14585 * to each request made by this object. (defaults to undefined)
14588 * @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)
14591 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14594 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14600 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14604 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14605 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14606 * a finer-grained basis than the DataProxy events.
14608 getConnection : function(){
14609 return this.useAjax ? Roo.Ajax : this.conn;
14613 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14614 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14615 * process that block using the passed callback.
14616 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14617 * for the request to the remote server.
14618 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14619 * object into a block of Roo.data.Records.
14620 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14621 * The function must be passed <ul>
14622 * <li>The Record block object</li>
14623 * <li>The "arg" argument from the load function</li>
14624 * <li>A boolean success indicator</li>
14626 * @param {Object} scope The scope in which to call the callback
14627 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14629 load : function(params, reader, callback, scope, arg){
14630 if(this.fireEvent("beforeload", this, params) !== false){
14632 params : params || {},
14634 callback : callback,
14639 callback : this.loadResponse,
14643 Roo.applyIf(o, this.conn);
14644 if(this.activeRequest){
14645 Roo.Ajax.abort(this.activeRequest);
14647 this.activeRequest = Roo.Ajax.request(o);
14649 this.conn.request(o);
14652 callback.call(scope||this, null, arg, false);
14657 loadResponse : function(o, success, response){
14658 delete this.activeRequest;
14660 this.fireEvent("loadexception", this, o, response);
14661 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14666 result = o.reader.read(response);
14668 this.fireEvent("loadexception", this, o, response, e);
14669 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14673 this.fireEvent("load", this, o, o.request.arg);
14674 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14678 update : function(dataSet){
14683 updateResponse : function(dataSet){
14688 * Ext JS Library 1.1.1
14689 * Copyright(c) 2006-2007, Ext JS, LLC.
14691 * Originally Released Under LGPL - original licence link has changed is not relivant.
14694 * <script type="text/javascript">
14698 * @class Roo.data.ScriptTagProxy
14699 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14700 * other than the originating domain of the running page.<br><br>
14702 * <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
14703 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14705 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14706 * source code that is used as the source inside a <script> tag.<br><br>
14708 * In order for the browser to process the returned data, the server must wrap the data object
14709 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14710 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14711 * depending on whether the callback name was passed:
14714 boolean scriptTag = false;
14715 String cb = request.getParameter("callback");
14718 response.setContentType("text/javascript");
14720 response.setContentType("application/x-json");
14722 Writer out = response.getWriter();
14724 out.write(cb + "(");
14726 out.print(dataBlock.toJsonString());
14733 * @param {Object} config A configuration object.
14735 Roo.data.ScriptTagProxy = function(config){
14736 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14737 Roo.apply(this, config);
14738 this.head = document.getElementsByTagName("head")[0];
14741 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14743 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14745 * @cfg {String} url The URL from which to request the data object.
14748 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14752 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14753 * the server the name of the callback function set up by the load call to process the returned data object.
14754 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14755 * javascript output which calls this named function passing the data object as its only parameter.
14757 callbackParam : "callback",
14759 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14760 * name to the request.
14765 * Load data from the configured URL, read the data object into
14766 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14767 * process that block using the passed callback.
14768 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14769 * for the request to the remote server.
14770 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14771 * object into a block of Roo.data.Records.
14772 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14773 * The function must be passed <ul>
14774 * <li>The Record block object</li>
14775 * <li>The "arg" argument from the load function</li>
14776 * <li>A boolean success indicator</li>
14778 * @param {Object} scope The scope in which to call the callback
14779 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14781 load : function(params, reader, callback, scope, arg){
14782 if(this.fireEvent("beforeload", this, params) !== false){
14784 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14786 var url = this.url;
14787 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14789 url += "&_dc=" + (new Date().getTime());
14791 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14794 cb : "stcCallback"+transId,
14795 scriptId : "stcScript"+transId,
14799 callback : callback,
14805 window[trans.cb] = function(o){
14806 conn.handleResponse(o, trans);
14809 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14811 if(this.autoAbort !== false){
14815 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14817 var script = document.createElement("script");
14818 script.setAttribute("src", url);
14819 script.setAttribute("type", "text/javascript");
14820 script.setAttribute("id", trans.scriptId);
14821 this.head.appendChild(script);
14823 this.trans = trans;
14825 callback.call(scope||this, null, arg, false);
14830 isLoading : function(){
14831 return this.trans ? true : false;
14835 * Abort the current server request.
14837 abort : function(){
14838 if(this.isLoading()){
14839 this.destroyTrans(this.trans);
14844 destroyTrans : function(trans, isLoaded){
14845 this.head.removeChild(document.getElementById(trans.scriptId));
14846 clearTimeout(trans.timeoutId);
14848 window[trans.cb] = undefined;
14850 delete window[trans.cb];
14853 // if hasn't been loaded, wait for load to remove it to prevent script error
14854 window[trans.cb] = function(){
14855 window[trans.cb] = undefined;
14857 delete window[trans.cb];
14864 handleResponse : function(o, trans){
14865 this.trans = false;
14866 this.destroyTrans(trans, true);
14869 result = trans.reader.readRecords(o);
14871 this.fireEvent("loadexception", this, o, trans.arg, e);
14872 trans.callback.call(trans.scope||window, null, trans.arg, false);
14875 this.fireEvent("load", this, o, trans.arg);
14876 trans.callback.call(trans.scope||window, result, trans.arg, true);
14880 handleFailure : function(trans){
14881 this.trans = false;
14882 this.destroyTrans(trans, false);
14883 this.fireEvent("loadexception", this, null, trans.arg);
14884 trans.callback.call(trans.scope||window, null, trans.arg, false);
14888 * Ext JS Library 1.1.1
14889 * Copyright(c) 2006-2007, Ext JS, LLC.
14891 * Originally Released Under LGPL - original licence link has changed is not relivant.
14894 * <script type="text/javascript">
14898 * @class Roo.data.JsonReader
14899 * @extends Roo.data.DataReader
14900 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14901 * based on mappings in a provided Roo.data.Record constructor.
14903 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14904 * in the reply previously.
14909 var RecordDef = Roo.data.Record.create([
14910 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14911 {name: 'occupation'} // This field will use "occupation" as the mapping.
14913 var myReader = new Roo.data.JsonReader({
14914 totalProperty: "results", // The property which contains the total dataset size (optional)
14915 root: "rows", // The property which contains an Array of row objects
14916 id: "id" // The property within each row object that provides an ID for the record (optional)
14920 * This would consume a JSON file like this:
14922 { 'results': 2, 'rows': [
14923 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14924 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14927 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14928 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14929 * paged from the remote server.
14930 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14931 * @cfg {String} root name of the property which contains the Array of row objects.
14932 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14933 * @cfg {Array} fields Array of field definition objects
14935 * Create a new JsonReader
14936 * @param {Object} meta Metadata configuration options
14937 * @param {Object} recordType Either an Array of field definition objects,
14938 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14940 Roo.data.JsonReader = function(meta, recordType){
14943 // set some defaults:
14944 Roo.applyIf(meta, {
14945 totalProperty: 'total',
14946 successProperty : 'success',
14951 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14953 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14955 readerType : 'Json',
14958 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14959 * Used by Store query builder to append _requestMeta to params.
14962 metaFromRemote : false,
14964 * This method is only used by a DataProxy which has retrieved data from a remote server.
14965 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14966 * @return {Object} data A data block which is used by an Roo.data.Store object as
14967 * a cache of Roo.data.Records.
14969 read : function(response){
14970 var json = response.responseText;
14972 var o = /* eval:var:o */ eval("("+json+")");
14974 throw {message: "JsonReader.read: Json object not found"};
14980 this.metaFromRemote = true;
14981 this.meta = o.metaData;
14982 this.recordType = Roo.data.Record.create(o.metaData.fields);
14983 this.onMetaChange(this.meta, this.recordType, o);
14985 return this.readRecords(o);
14988 // private function a store will implement
14989 onMetaChange : function(meta, recordType, o){
14996 simpleAccess: function(obj, subsc) {
15003 getJsonAccessor: function(){
15005 return function(expr) {
15007 return(re.test(expr))
15008 ? new Function("obj", "return obj." + expr)
15013 return Roo.emptyFn;
15018 * Create a data block containing Roo.data.Records from an XML document.
15019 * @param {Object} o An object which contains an Array of row objects in the property specified
15020 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15021 * which contains the total size of the dataset.
15022 * @return {Object} data A data block which is used by an Roo.data.Store object as
15023 * a cache of Roo.data.Records.
15025 readRecords : function(o){
15027 * After any data loads, the raw JSON data is available for further custom processing.
15031 var s = this.meta, Record = this.recordType,
15032 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15034 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15036 if(s.totalProperty) {
15037 this.getTotal = this.getJsonAccessor(s.totalProperty);
15039 if(s.successProperty) {
15040 this.getSuccess = this.getJsonAccessor(s.successProperty);
15042 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15044 var g = this.getJsonAccessor(s.id);
15045 this.getId = function(rec) {
15047 return (r === undefined || r === "") ? null : r;
15050 this.getId = function(){return null;};
15053 for(var jj = 0; jj < fl; jj++){
15055 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15056 this.ef[jj] = this.getJsonAccessor(map);
15060 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15061 if(s.totalProperty){
15062 var vt = parseInt(this.getTotal(o), 10);
15067 if(s.successProperty){
15068 var vs = this.getSuccess(o);
15069 if(vs === false || vs === 'false'){
15074 for(var i = 0; i < c; i++){
15077 var id = this.getId(n);
15078 for(var j = 0; j < fl; j++){
15080 var v = this.ef[j](n);
15082 Roo.log('missing convert for ' + f.name);
15086 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15088 var record = new Record(values, id);
15090 records[i] = record;
15096 totalRecords : totalRecords
15099 // used when loading children.. @see loadDataFromChildren
15100 toLoadData: function(rec)
15102 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15103 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15104 return { data : data, total : data.length };
15109 * Ext JS Library 1.1.1
15110 * Copyright(c) 2006-2007, Ext JS, LLC.
15112 * Originally Released Under LGPL - original licence link has changed is not relivant.
15115 * <script type="text/javascript">
15119 * @class Roo.data.ArrayReader
15120 * @extends Roo.data.DataReader
15121 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15122 * Each element of that Array represents a row of data fields. The
15123 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15124 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15128 var RecordDef = Roo.data.Record.create([
15129 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15130 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15132 var myReader = new Roo.data.ArrayReader({
15133 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15137 * This would consume an Array like this:
15139 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15143 * Create a new JsonReader
15144 * @param {Object} meta Metadata configuration options.
15145 * @param {Object|Array} recordType Either an Array of field definition objects
15147 * @cfg {Array} fields Array of field definition objects
15148 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15149 * as specified to {@link Roo.data.Record#create},
15150 * or an {@link Roo.data.Record} object
15153 * created using {@link Roo.data.Record#create}.
15155 Roo.data.ArrayReader = function(meta, recordType)
15157 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15160 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15163 * Create a data block containing Roo.data.Records from an XML document.
15164 * @param {Object} o An Array of row objects which represents the dataset.
15165 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15166 * a cache of Roo.data.Records.
15168 readRecords : function(o)
15170 var sid = this.meta ? this.meta.id : null;
15171 var recordType = this.recordType, fields = recordType.prototype.fields;
15174 for(var i = 0; i < root.length; i++){
15177 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15178 for(var j = 0, jlen = fields.length; j < jlen; j++){
15179 var f = fields.items[j];
15180 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15181 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15183 values[f.name] = v;
15185 var record = new recordType(values, id);
15187 records[records.length] = record;
15191 totalRecords : records.length
15194 // used when loading children.. @see loadDataFromChildren
15195 toLoadData: function(rec)
15197 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15198 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15209 * @class Roo.bootstrap.ComboBox
15210 * @extends Roo.bootstrap.TriggerField
15211 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15212 * @cfg {Boolean} append (true|false) default false
15213 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15214 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15215 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15216 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15217 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15218 * @cfg {Boolean} animate default true
15219 * @cfg {Boolean} emptyResultText only for touch device
15220 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15221 * @cfg {String} emptyTitle default ''
15222 * @cfg {Number} width fixed with? experimental
15224 * Create a new ComboBox.
15225 * @param {Object} config Configuration options
15227 Roo.bootstrap.ComboBox = function(config){
15228 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15232 * Fires when the dropdown list is expanded
15233 * @param {Roo.bootstrap.ComboBox} combo This combo box
15238 * Fires when the dropdown list is collapsed
15239 * @param {Roo.bootstrap.ComboBox} combo This combo box
15243 * @event beforeselect
15244 * Fires before a list item is selected. Return false to cancel the selection.
15245 * @param {Roo.bootstrap.ComboBox} combo This combo box
15246 * @param {Roo.data.Record} record The data record returned from the underlying store
15247 * @param {Number} index The index of the selected item in the dropdown list
15249 'beforeselect' : true,
15252 * Fires when a list item is selected
15253 * @param {Roo.bootstrap.ComboBox} combo This combo box
15254 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15255 * @param {Number} index The index of the selected item in the dropdown list
15259 * @event beforequery
15260 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15261 * The event object passed has these properties:
15262 * @param {Roo.bootstrap.ComboBox} combo This combo box
15263 * @param {String} query The query
15264 * @param {Boolean} forceAll true to force "all" query
15265 * @param {Boolean} cancel true to cancel the query
15266 * @param {Object} e The query event object
15268 'beforequery': true,
15271 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15272 * @param {Roo.bootstrap.ComboBox} combo This combo box
15277 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15278 * @param {Roo.bootstrap.ComboBox} combo This combo box
15279 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15284 * Fires when the remove value from the combobox array
15285 * @param {Roo.bootstrap.ComboBox} combo This combo box
15289 * @event afterremove
15290 * Fires when the remove value from the combobox array
15291 * @param {Roo.bootstrap.ComboBox} combo This combo box
15293 'afterremove' : true,
15295 * @event specialfilter
15296 * Fires when specialfilter
15297 * @param {Roo.bootstrap.ComboBox} combo This combo box
15299 'specialfilter' : true,
15302 * Fires when tick the element
15303 * @param {Roo.bootstrap.ComboBox} combo This combo box
15307 * @event touchviewdisplay
15308 * Fires when touch view require special display (default is using displayField)
15309 * @param {Roo.bootstrap.ComboBox} combo This combo box
15310 * @param {Object} cfg set html .
15312 'touchviewdisplay' : true
15317 this.tickItems = [];
15319 this.selectedIndex = -1;
15320 if(this.mode == 'local'){
15321 if(config.queryDelay === undefined){
15322 this.queryDelay = 10;
15324 if(config.minChars === undefined){
15330 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15333 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15334 * rendering into an Roo.Editor, defaults to false)
15337 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15338 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15341 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15344 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15345 * the dropdown list (defaults to undefined, with no header element)
15349 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15353 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15355 listWidth: undefined,
15357 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15358 * mode = 'remote' or 'text' if mode = 'local')
15360 displayField: undefined,
15363 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15364 * mode = 'remote' or 'value' if mode = 'local').
15365 * Note: use of a valueField requires the user make a selection
15366 * in order for a value to be mapped.
15368 valueField: undefined,
15370 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15375 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15376 * field's data value (defaults to the underlying DOM element's name)
15378 hiddenName: undefined,
15380 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15384 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15386 selectedClass: 'active',
15389 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15393 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15394 * anchor positions (defaults to 'tl-bl')
15396 listAlign: 'tl-bl?',
15398 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15402 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15403 * query specified by the allQuery config option (defaults to 'query')
15405 triggerAction: 'query',
15407 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15408 * (defaults to 4, does not apply if editable = false)
15412 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15413 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15417 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15418 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15422 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15423 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15427 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15428 * when editable = true (defaults to false)
15430 selectOnFocus:false,
15432 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15434 queryParam: 'query',
15436 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15437 * when mode = 'remote' (defaults to 'Loading...')
15439 loadingText: 'Loading...',
15441 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15445 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15449 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15450 * traditional select (defaults to true)
15454 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15458 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15462 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15463 * listWidth has a higher value)
15467 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15468 * allow the user to set arbitrary text into the field (defaults to false)
15470 forceSelection:false,
15472 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15473 * if typeAhead = true (defaults to 250)
15475 typeAheadDelay : 250,
15477 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15478 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15480 valueNotFoundText : undefined,
15482 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15484 blockFocus : false,
15487 * @cfg {Boolean} disableClear Disable showing of clear button.
15489 disableClear : false,
15491 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15493 alwaysQuery : false,
15496 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15501 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15503 invalidClass : "has-warning",
15506 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15508 validClass : "has-success",
15511 * @cfg {Boolean} specialFilter (true|false) special filter default false
15513 specialFilter : false,
15516 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15518 mobileTouchView : true,
15521 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15523 useNativeIOS : false,
15526 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15528 mobile_restrict_height : false,
15530 ios_options : false,
15542 btnPosition : 'right',
15543 triggerList : true,
15544 showToggleBtn : true,
15546 emptyResultText: 'Empty',
15547 triggerText : 'Select',
15551 // element that contains real text value.. (when hidden is used..)
15553 getAutoCreate : function()
15558 * Render classic select for iso
15561 if(Roo.isIOS && this.useNativeIOS){
15562 cfg = this.getAutoCreateNativeIOS();
15570 if(Roo.isTouch && this.mobileTouchView){
15571 cfg = this.getAutoCreateTouchView();
15578 if(!this.tickable){
15579 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15584 * ComboBox with tickable selections
15587 var align = this.labelAlign || this.parentLabelAlign();
15590 cls : 'form-group roo-combobox-tickable' //input-group
15593 var btn_text_select = '';
15594 var btn_text_done = '';
15595 var btn_text_cancel = '';
15597 if (this.btn_text_show) {
15598 btn_text_select = 'Select';
15599 btn_text_done = 'Done';
15600 btn_text_cancel = 'Cancel';
15605 cls : 'tickable-buttons',
15610 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15611 //html : this.triggerText
15612 html: btn_text_select
15618 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15620 html: btn_text_done
15626 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15628 html: btn_text_cancel
15634 buttons.cn.unshift({
15636 cls: 'roo-select2-search-field-input'
15642 Roo.each(buttons.cn, function(c){
15644 c.cls += ' btn-' + _this.size;
15647 if (_this.disabled) {
15654 style : 'display: contents',
15659 cls: 'form-hidden-field'
15663 cls: 'roo-select2-choices',
15667 cls: 'roo-select2-search-field',
15678 cls: 'roo-select2-container input-group roo-select2-container-multi',
15684 // cls: 'typeahead typeahead-long dropdown-menu',
15685 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15690 if(this.hasFeedback && !this.allowBlank){
15694 cls: 'glyphicon form-control-feedback'
15697 combobox.cn.push(feedback);
15704 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15705 tooltip : 'This field is required'
15707 if (Roo.bootstrap.version == 4) {
15710 style : 'display:none'
15713 if (align ==='left' && this.fieldLabel.length) {
15715 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15722 cls : 'control-label col-form-label',
15723 html : this.fieldLabel
15735 var labelCfg = cfg.cn[1];
15736 var contentCfg = cfg.cn[2];
15739 if(this.indicatorpos == 'right'){
15745 cls : 'control-label col-form-label',
15749 html : this.fieldLabel
15765 labelCfg = cfg.cn[0];
15766 contentCfg = cfg.cn[1];
15770 if(this.labelWidth > 12){
15771 labelCfg.style = "width: " + this.labelWidth + 'px';
15773 if(this.width * 1 > 0){
15774 contentCfg.style = "width: " + this.width + 'px';
15776 if(this.labelWidth < 13 && this.labelmd == 0){
15777 this.labelmd = this.labelWidth;
15780 if(this.labellg > 0){
15781 labelCfg.cls += ' col-lg-' + this.labellg;
15782 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15785 if(this.labelmd > 0){
15786 labelCfg.cls += ' col-md-' + this.labelmd;
15787 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15790 if(this.labelsm > 0){
15791 labelCfg.cls += ' col-sm-' + this.labelsm;
15792 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15795 if(this.labelxs > 0){
15796 labelCfg.cls += ' col-xs-' + this.labelxs;
15797 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15801 } else if ( this.fieldLabel.length) {
15802 // Roo.log(" label");
15807 //cls : 'input-group-addon',
15808 html : this.fieldLabel
15813 if(this.indicatorpos == 'right'){
15817 //cls : 'input-group-addon',
15818 html : this.fieldLabel
15828 // Roo.log(" no label && no align");
15835 ['xs','sm','md','lg'].map(function(size){
15836 if (settings[size]) {
15837 cfg.cls += ' col-' + size + '-' + settings[size];
15845 _initEventsCalled : false,
15848 initEvents: function()
15850 if (this._initEventsCalled) { // as we call render... prevent looping...
15853 this._initEventsCalled = true;
15856 throw "can not find store for combo";
15859 this.indicator = this.indicatorEl();
15861 this.store = Roo.factory(this.store, Roo.data);
15862 this.store.parent = this;
15864 // if we are building from html. then this element is so complex, that we can not really
15865 // use the rendered HTML.
15866 // so we have to trash and replace the previous code.
15867 if (Roo.XComponent.build_from_html) {
15868 // remove this element....
15869 var e = this.el.dom, k=0;
15870 while (e ) { e = e.previousSibling; ++k;}
15875 this.rendered = false;
15877 this.render(this.parent().getChildContainer(true), k);
15880 if(Roo.isIOS && this.useNativeIOS){
15881 this.initIOSView();
15889 if(Roo.isTouch && this.mobileTouchView){
15890 this.initTouchView();
15895 this.initTickableEvents();
15899 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15901 if(this.hiddenName){
15903 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15905 this.hiddenField.dom.value =
15906 this.hiddenValue !== undefined ? this.hiddenValue :
15907 this.value !== undefined ? this.value : '';
15909 // prevent input submission
15910 this.el.dom.removeAttribute('name');
15911 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15916 // this.el.dom.setAttribute('autocomplete', 'off');
15919 var cls = 'x-combo-list';
15921 //this.list = new Roo.Layer({
15922 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15928 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15929 _this.list.setWidth(lw);
15932 this.list.on('mouseover', this.onViewOver, this);
15933 this.list.on('mousemove', this.onViewMove, this);
15934 this.list.on('scroll', this.onViewScroll, this);
15937 this.list.swallowEvent('mousewheel');
15938 this.assetHeight = 0;
15941 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15942 this.assetHeight += this.header.getHeight();
15945 this.innerList = this.list.createChild({cls:cls+'-inner'});
15946 this.innerList.on('mouseover', this.onViewOver, this);
15947 this.innerList.on('mousemove', this.onViewMove, this);
15948 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15950 if(this.allowBlank && !this.pageSize && !this.disableClear){
15951 this.footer = this.list.createChild({cls:cls+'-ft'});
15952 this.pageTb = new Roo.Toolbar(this.footer);
15956 this.footer = this.list.createChild({cls:cls+'-ft'});
15957 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15958 {pageSize: this.pageSize});
15962 if (this.pageTb && this.allowBlank && !this.disableClear) {
15964 this.pageTb.add(new Roo.Toolbar.Fill(), {
15965 cls: 'x-btn-icon x-btn-clear',
15967 handler: function()
15970 _this.clearValue();
15971 _this.onSelect(false, -1);
15976 this.assetHeight += this.footer.getHeight();
15981 this.tpl = Roo.bootstrap.version == 4 ?
15982 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15983 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15986 this.view = new Roo.View(this.list, this.tpl, {
15987 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15989 //this.view.wrapEl.setDisplayed(false);
15990 this.view.on('click', this.onViewClick, this);
15993 this.store.on('beforeload', this.onBeforeLoad, this);
15994 this.store.on('load', this.onLoad, this);
15995 this.store.on('loadexception', this.onLoadException, this);
15997 if(this.resizable){
15998 this.resizer = new Roo.Resizable(this.list, {
15999 pinned:true, handles:'se'
16001 this.resizer.on('resize', function(r, w, h){
16002 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16003 this.listWidth = w;
16004 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16005 this.restrictHeight();
16007 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16010 if(!this.editable){
16011 this.editable = true;
16012 this.setEditable(false);
16017 if (typeof(this.events.add.listeners) != 'undefined') {
16019 this.addicon = this.wrap.createChild(
16020 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16022 this.addicon.on('click', function(e) {
16023 this.fireEvent('add', this);
16026 if (typeof(this.events.edit.listeners) != 'undefined') {
16028 this.editicon = this.wrap.createChild(
16029 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16030 if (this.addicon) {
16031 this.editicon.setStyle('margin-left', '40px');
16033 this.editicon.on('click', function(e) {
16035 // we fire even if inothing is selected..
16036 this.fireEvent('edit', this, this.lastData );
16042 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16043 "up" : function(e){
16044 this.inKeyMode = true;
16048 "down" : function(e){
16049 if(!this.isExpanded()){
16050 this.onTriggerClick();
16052 this.inKeyMode = true;
16057 "enter" : function(e){
16058 // this.onViewClick();
16062 if(this.fireEvent("specialkey", this, e)){
16063 this.onViewClick(false);
16069 "esc" : function(e){
16073 "tab" : function(e){
16076 if(this.fireEvent("specialkey", this, e)){
16077 this.onViewClick(false);
16085 doRelay : function(foo, bar, hname){
16086 if(hname == 'down' || this.scope.isExpanded()){
16087 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16096 this.queryDelay = Math.max(this.queryDelay || 10,
16097 this.mode == 'local' ? 10 : 250);
16100 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16102 if(this.typeAhead){
16103 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16105 if(this.editable !== false){
16106 this.inputEl().on("keyup", this.onKeyUp, this);
16108 if(this.forceSelection){
16109 this.inputEl().on('blur', this.doForce, this);
16113 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16114 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16118 initTickableEvents: function()
16122 if(this.hiddenName){
16124 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16126 this.hiddenField.dom.value =
16127 this.hiddenValue !== undefined ? this.hiddenValue :
16128 this.value !== undefined ? this.value : '';
16130 // prevent input submission
16131 this.el.dom.removeAttribute('name');
16132 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16137 // this.list = this.el.select('ul.dropdown-menu',true).first();
16139 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16140 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16141 if(this.triggerList){
16142 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16145 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16146 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16148 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16149 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16151 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16152 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16154 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16155 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16156 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16159 this.cancelBtn.hide();
16164 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16165 _this.list.setWidth(lw);
16168 this.list.on('mouseover', this.onViewOver, this);
16169 this.list.on('mousemove', this.onViewMove, this);
16171 this.list.on('scroll', this.onViewScroll, this);
16174 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16175 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16178 this.view = new Roo.View(this.list, this.tpl, {
16183 selectedClass: this.selectedClass
16186 //this.view.wrapEl.setDisplayed(false);
16187 this.view.on('click', this.onViewClick, this);
16191 this.store.on('beforeload', this.onBeforeLoad, this);
16192 this.store.on('load', this.onLoad, this);
16193 this.store.on('loadexception', this.onLoadException, this);
16196 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16197 "up" : function(e){
16198 this.inKeyMode = true;
16202 "down" : function(e){
16203 this.inKeyMode = true;
16207 "enter" : function(e){
16208 if(this.fireEvent("specialkey", this, e)){
16209 this.onViewClick(false);
16215 "esc" : function(e){
16216 this.onTickableFooterButtonClick(e, false, false);
16219 "tab" : function(e){
16220 this.fireEvent("specialkey", this, e);
16222 this.onTickableFooterButtonClick(e, false, false);
16229 doRelay : function(e, fn, key){
16230 if(this.scope.isExpanded()){
16231 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16240 this.queryDelay = Math.max(this.queryDelay || 10,
16241 this.mode == 'local' ? 10 : 250);
16244 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16246 if(this.typeAhead){
16247 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16250 if(this.editable !== false){
16251 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16254 this.indicator = this.indicatorEl();
16256 if(this.indicator){
16257 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16258 this.indicator.hide();
16263 onDestroy : function(){
16265 this.view.setStore(null);
16266 this.view.el.removeAllListeners();
16267 this.view.el.remove();
16268 this.view.purgeListeners();
16271 this.list.dom.innerHTML = '';
16275 this.store.un('beforeload', this.onBeforeLoad, this);
16276 this.store.un('load', this.onLoad, this);
16277 this.store.un('loadexception', this.onLoadException, this);
16279 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16283 fireKey : function(e){
16284 if(e.isNavKeyPress() && !this.list.isVisible()){
16285 this.fireEvent("specialkey", this, e);
16290 onResize: function(w, h)
16294 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16296 // if(typeof w != 'number'){
16297 // // we do not handle it!?!?
16300 // var tw = this.trigger.getWidth();
16301 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16302 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16304 // this.inputEl().setWidth( this.adjustWidth('input', x));
16306 // //this.trigger.setStyle('left', x+'px');
16308 // if(this.list && this.listWidth === undefined){
16309 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16310 // this.list.setWidth(lw);
16311 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16319 * Allow or prevent the user from directly editing the field text. If false is passed,
16320 * the user will only be able to select from the items defined in the dropdown list. This method
16321 * is the runtime equivalent of setting the 'editable' config option at config time.
16322 * @param {Boolean} value True to allow the user to directly edit the field text
16324 setEditable : function(value){
16325 if(value == this.editable){
16328 this.editable = value;
16330 this.inputEl().dom.setAttribute('readOnly', true);
16331 this.inputEl().on('mousedown', this.onTriggerClick, this);
16332 this.inputEl().addClass('x-combo-noedit');
16334 this.inputEl().dom.setAttribute('readOnly', false);
16335 this.inputEl().un('mousedown', this.onTriggerClick, this);
16336 this.inputEl().removeClass('x-combo-noedit');
16342 onBeforeLoad : function(combo,opts){
16343 if(!this.hasFocus){
16347 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16349 this.restrictHeight();
16350 this.selectedIndex = -1;
16354 onLoad : function(){
16356 this.hasQuery = false;
16358 if(!this.hasFocus){
16362 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16363 this.loading.hide();
16366 if(this.store.getCount() > 0){
16369 this.restrictHeight();
16370 if(this.lastQuery == this.allQuery){
16371 if(this.editable && !this.tickable){
16372 this.inputEl().dom.select();
16376 !this.selectByValue(this.value, true) &&
16379 !this.store.lastOptions ||
16380 typeof(this.store.lastOptions.add) == 'undefined' ||
16381 this.store.lastOptions.add != true
16384 this.select(0, true);
16387 if(this.autoFocus){
16390 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16391 this.taTask.delay(this.typeAheadDelay);
16395 this.onEmptyResults();
16401 onLoadException : function()
16403 this.hasQuery = false;
16405 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16406 this.loading.hide();
16409 if(this.tickable && this.editable){
16414 // only causes errors at present
16415 //Roo.log(this.store.reader.jsonData);
16416 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16418 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16424 onTypeAhead : function(){
16425 if(this.store.getCount() > 0){
16426 var r = this.store.getAt(0);
16427 var newValue = r.data[this.displayField];
16428 var len = newValue.length;
16429 var selStart = this.getRawValue().length;
16431 if(selStart != len){
16432 this.setRawValue(newValue);
16433 this.selectText(selStart, newValue.length);
16439 onSelect : function(record, index){
16441 if(this.fireEvent('beforeselect', this, record, index) !== false){
16443 this.setFromData(index > -1 ? record.data : false);
16446 this.fireEvent('select', this, record, index);
16451 * Returns the currently selected field value or empty string if no value is set.
16452 * @return {String} value The selected value
16454 getValue : function()
16456 if(Roo.isIOS && this.useNativeIOS){
16457 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16461 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16464 if(this.valueField){
16465 return typeof this.value != 'undefined' ? this.value : '';
16467 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16471 getRawValue : function()
16473 if(Roo.isIOS && this.useNativeIOS){
16474 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16477 var v = this.inputEl().getValue();
16483 * Clears any text/value currently set in the field
16485 clearValue : function(){
16487 if(this.hiddenField){
16488 this.hiddenField.dom.value = '';
16491 this.setRawValue('');
16492 this.lastSelectionText = '';
16493 this.lastData = false;
16495 var close = this.closeTriggerEl();
16506 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16507 * will be displayed in the field. If the value does not match the data value of an existing item,
16508 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16509 * Otherwise the field will be blank (although the value will still be set).
16510 * @param {String} value The value to match
16512 setValue : function(v)
16514 if(Roo.isIOS && this.useNativeIOS){
16515 this.setIOSValue(v);
16525 if(this.valueField){
16526 var r = this.findRecord(this.valueField, v);
16528 text = r.data[this.displayField];
16529 }else if(this.valueNotFoundText !== undefined){
16530 text = this.valueNotFoundText;
16533 this.lastSelectionText = text;
16534 if(this.hiddenField){
16535 this.hiddenField.dom.value = v;
16537 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16540 var close = this.closeTriggerEl();
16543 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16549 * @property {Object} the last set data for the element
16554 * Sets the value of the field based on a object which is related to the record format for the store.
16555 * @param {Object} value the value to set as. or false on reset?
16557 setFromData : function(o){
16564 var dv = ''; // display value
16565 var vv = ''; // value value..
16567 if (this.displayField) {
16568 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16570 // this is an error condition!!!
16571 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16574 if(this.valueField){
16575 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16578 var close = this.closeTriggerEl();
16581 if(dv.length || vv * 1 > 0){
16583 this.blockFocus=true;
16589 if(this.hiddenField){
16590 this.hiddenField.dom.value = vv;
16592 this.lastSelectionText = dv;
16593 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16597 // no hidden field.. - we store the value in 'value', but still display
16598 // display field!!!!
16599 this.lastSelectionText = dv;
16600 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16607 reset : function(){
16608 // overridden so that last data is reset..
16615 this.setValue(this.originalValue);
16616 //this.clearInvalid();
16617 this.lastData = false;
16619 this.view.clearSelections();
16625 findRecord : function(prop, value){
16627 if(this.store.getCount() > 0){
16628 this.store.each(function(r){
16629 if(r.data[prop] == value){
16639 getName: function()
16641 // returns hidden if it's set..
16642 if (!this.rendered) {return ''};
16643 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16647 onViewMove : function(e, t){
16648 this.inKeyMode = false;
16652 onViewOver : function(e, t){
16653 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16656 var item = this.view.findItemFromChild(t);
16659 var index = this.view.indexOf(item);
16660 this.select(index, false);
16665 onViewClick : function(view, doFocus, el, e)
16667 var index = this.view.getSelectedIndexes()[0];
16669 var r = this.store.getAt(index);
16673 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16680 Roo.each(this.tickItems, function(v,k){
16682 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16684 _this.tickItems.splice(k, 1);
16686 if(typeof(e) == 'undefined' && view == false){
16687 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16699 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16700 this.tickItems.push(r.data);
16703 if(typeof(e) == 'undefined' && view == false){
16704 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16711 this.onSelect(r, index);
16713 if(doFocus !== false && !this.blockFocus){
16714 this.inputEl().focus();
16719 restrictHeight : function(){
16720 //this.innerList.dom.style.height = '';
16721 //var inner = this.innerList.dom;
16722 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16723 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16724 //this.list.beginUpdate();
16725 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16726 this.list.alignTo(this.inputEl(), this.listAlign);
16727 this.list.alignTo(this.inputEl(), this.listAlign);
16728 //this.list.endUpdate();
16732 onEmptyResults : function(){
16734 if(this.tickable && this.editable){
16735 this.hasFocus = false;
16736 this.restrictHeight();
16744 * Returns true if the dropdown list is expanded, else false.
16746 isExpanded : function(){
16747 return this.list.isVisible();
16751 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16752 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16753 * @param {String} value The data value of the item to select
16754 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16755 * selected item if it is not currently in view (defaults to true)
16756 * @return {Boolean} True if the value matched an item in the list, else false
16758 selectByValue : function(v, scrollIntoView){
16759 if(v !== undefined && v !== null){
16760 var r = this.findRecord(this.valueField || this.displayField, v);
16762 this.select(this.store.indexOf(r), scrollIntoView);
16770 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16771 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16772 * @param {Number} index The zero-based index of the list item to select
16773 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16774 * selected item if it is not currently in view (defaults to true)
16776 select : function(index, scrollIntoView){
16777 this.selectedIndex = index;
16778 this.view.select(index);
16779 if(scrollIntoView !== false){
16780 var el = this.view.getNode(index);
16782 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16785 this.list.scrollChildIntoView(el, false);
16791 selectNext : function(){
16792 var ct = this.store.getCount();
16794 if(this.selectedIndex == -1){
16796 }else if(this.selectedIndex < ct-1){
16797 this.select(this.selectedIndex+1);
16803 selectPrev : function(){
16804 var ct = this.store.getCount();
16806 if(this.selectedIndex == -1){
16808 }else if(this.selectedIndex != 0){
16809 this.select(this.selectedIndex-1);
16815 onKeyUp : function(e){
16816 if(this.editable !== false && !e.isSpecialKey()){
16817 this.lastKey = e.getKey();
16818 this.dqTask.delay(this.queryDelay);
16823 validateBlur : function(){
16824 return !this.list || !this.list.isVisible();
16828 initQuery : function(){
16830 var v = this.getRawValue();
16832 if(this.tickable && this.editable){
16833 v = this.tickableInputEl().getValue();
16840 doForce : function(){
16841 if(this.inputEl().dom.value.length > 0){
16842 this.inputEl().dom.value =
16843 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16849 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16850 * query allowing the query action to be canceled if needed.
16851 * @param {String} query The SQL query to execute
16852 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16853 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16854 * saved in the current store (defaults to false)
16856 doQuery : function(q, forceAll){
16858 if(q === undefined || q === null){
16863 forceAll: forceAll,
16867 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16872 forceAll = qe.forceAll;
16873 if(forceAll === true || (q.length >= this.minChars)){
16875 this.hasQuery = true;
16877 if(this.lastQuery != q || this.alwaysQuery){
16878 this.lastQuery = q;
16879 if(this.mode == 'local'){
16880 this.selectedIndex = -1;
16882 this.store.clearFilter();
16885 if(this.specialFilter){
16886 this.fireEvent('specialfilter', this);
16891 this.store.filter(this.displayField, q);
16894 this.store.fireEvent("datachanged", this.store);
16901 this.store.baseParams[this.queryParam] = q;
16903 var options = {params : this.getParams(q)};
16906 options.add = true;
16907 options.params.start = this.page * this.pageSize;
16910 this.store.load(options);
16913 * this code will make the page width larger, at the beginning, the list not align correctly,
16914 * we should expand the list on onLoad
16915 * so command out it
16920 this.selectedIndex = -1;
16925 this.loadNext = false;
16929 getParams : function(q){
16931 //p[this.queryParam] = q;
16935 p.limit = this.pageSize;
16941 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16943 collapse : function(){
16944 if(!this.isExpanded()){
16950 this.hasFocus = false;
16954 this.cancelBtn.hide();
16955 this.trigger.show();
16958 this.tickableInputEl().dom.value = '';
16959 this.tickableInputEl().blur();
16964 Roo.get(document).un('mousedown', this.collapseIf, this);
16965 Roo.get(document).un('mousewheel', this.collapseIf, this);
16966 if (!this.editable) {
16967 Roo.get(document).un('keydown', this.listKeyPress, this);
16969 this.fireEvent('collapse', this);
16975 collapseIf : function(e){
16976 var in_combo = e.within(this.el);
16977 var in_list = e.within(this.list);
16978 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16980 if (in_combo || in_list || is_list) {
16981 //e.stopPropagation();
16986 this.onTickableFooterButtonClick(e, false, false);
16994 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16996 expand : function(){
16998 if(this.isExpanded() || !this.hasFocus){
17002 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17003 this.list.setWidth(lw);
17009 this.restrictHeight();
17013 this.tickItems = Roo.apply([], this.item);
17016 this.cancelBtn.show();
17017 this.trigger.hide();
17020 this.tickableInputEl().focus();
17025 Roo.get(document).on('mousedown', this.collapseIf, this);
17026 Roo.get(document).on('mousewheel', this.collapseIf, this);
17027 if (!this.editable) {
17028 Roo.get(document).on('keydown', this.listKeyPress, this);
17031 this.fireEvent('expand', this);
17035 // Implements the default empty TriggerField.onTriggerClick function
17036 onTriggerClick : function(e)
17038 Roo.log('trigger click');
17040 if(this.disabled || !this.triggerList){
17045 this.loadNext = false;
17047 if(this.isExpanded()){
17049 if (!this.blockFocus) {
17050 this.inputEl().focus();
17054 this.hasFocus = true;
17055 if(this.triggerAction == 'all') {
17056 this.doQuery(this.allQuery, true);
17058 this.doQuery(this.getRawValue());
17060 if (!this.blockFocus) {
17061 this.inputEl().focus();
17066 onTickableTriggerClick : function(e)
17073 this.loadNext = false;
17074 this.hasFocus = true;
17076 if(this.triggerAction == 'all') {
17077 this.doQuery(this.allQuery, true);
17079 this.doQuery(this.getRawValue());
17083 onSearchFieldClick : function(e)
17085 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17086 this.onTickableFooterButtonClick(e, false, false);
17090 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17095 this.loadNext = false;
17096 this.hasFocus = true;
17098 if(this.triggerAction == 'all') {
17099 this.doQuery(this.allQuery, true);
17101 this.doQuery(this.getRawValue());
17105 listKeyPress : function(e)
17107 //Roo.log('listkeypress');
17108 // scroll to first matching element based on key pres..
17109 if (e.isSpecialKey()) {
17112 var k = String.fromCharCode(e.getKey()).toUpperCase();
17115 var csel = this.view.getSelectedNodes();
17116 var cselitem = false;
17118 var ix = this.view.indexOf(csel[0]);
17119 cselitem = this.store.getAt(ix);
17120 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17126 this.store.each(function(v) {
17128 // start at existing selection.
17129 if (cselitem.id == v.id) {
17135 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17136 match = this.store.indexOf(v);
17142 if (match === false) {
17143 return true; // no more action?
17146 this.view.select(match);
17147 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17148 sn.scrollIntoView(sn.dom.parentNode, false);
17151 onViewScroll : function(e, t){
17153 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){
17157 this.hasQuery = true;
17159 this.loading = this.list.select('.loading', true).first();
17161 if(this.loading === null){
17162 this.list.createChild({
17164 cls: 'loading roo-select2-more-results roo-select2-active',
17165 html: 'Loading more results...'
17168 this.loading = this.list.select('.loading', true).first();
17170 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17172 this.loading.hide();
17175 this.loading.show();
17180 this.loadNext = true;
17182 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17187 addItem : function(o)
17189 var dv = ''; // display value
17191 if (this.displayField) {
17192 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17194 // this is an error condition!!!
17195 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17202 var choice = this.choices.createChild({
17204 cls: 'roo-select2-search-choice',
17213 cls: 'roo-select2-search-choice-close fa fa-times',
17218 }, this.searchField);
17220 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17222 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17230 this.inputEl().dom.value = '';
17235 onRemoveItem : function(e, _self, o)
17237 e.preventDefault();
17239 this.lastItem = Roo.apply([], this.item);
17241 var index = this.item.indexOf(o.data) * 1;
17244 Roo.log('not this item?!');
17248 this.item.splice(index, 1);
17253 this.fireEvent('remove', this, e);
17259 syncValue : function()
17261 if(!this.item.length){
17268 Roo.each(this.item, function(i){
17269 if(_this.valueField){
17270 value.push(i[_this.valueField]);
17277 this.value = value.join(',');
17279 if(this.hiddenField){
17280 this.hiddenField.dom.value = this.value;
17283 this.store.fireEvent("datachanged", this.store);
17288 clearItem : function()
17290 if(!this.multiple){
17296 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17304 if(this.tickable && !Roo.isTouch){
17305 this.view.refresh();
17309 inputEl: function ()
17311 if(Roo.isIOS && this.useNativeIOS){
17312 return this.el.select('select.roo-ios-select', true).first();
17315 if(Roo.isTouch && this.mobileTouchView){
17316 return this.el.select('input.form-control',true).first();
17320 return this.searchField;
17323 return this.el.select('input.form-control',true).first();
17326 onTickableFooterButtonClick : function(e, btn, el)
17328 e.preventDefault();
17330 this.lastItem = Roo.apply([], this.item);
17332 if(btn && btn.name == 'cancel'){
17333 this.tickItems = Roo.apply([], this.item);
17342 Roo.each(this.tickItems, function(o){
17350 validate : function()
17352 if(this.getVisibilityEl().hasClass('hidden')){
17356 var v = this.getRawValue();
17359 v = this.getValue();
17362 if(this.disabled || this.allowBlank || v.length){
17367 this.markInvalid();
17371 tickableInputEl : function()
17373 if(!this.tickable || !this.editable){
17374 return this.inputEl();
17377 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17381 getAutoCreateTouchView : function()
17386 cls: 'form-group' //input-group
17392 type : this.inputType,
17393 cls : 'form-control x-combo-noedit',
17394 autocomplete: 'new-password',
17395 placeholder : this.placeholder || '',
17400 input.name = this.name;
17404 input.cls += ' input-' + this.size;
17407 if (this.disabled) {
17408 input.disabled = true;
17412 cls : 'roo-combobox-wrap',
17419 inputblock.cls += ' input-group';
17421 inputblock.cn.unshift({
17423 cls : 'input-group-addon input-group-prepend input-group-text',
17428 if(this.removable && !this.multiple){
17429 inputblock.cls += ' roo-removable';
17431 inputblock.cn.push({
17434 cls : 'roo-combo-removable-btn close'
17438 if(this.hasFeedback && !this.allowBlank){
17440 inputblock.cls += ' has-feedback';
17442 inputblock.cn.push({
17444 cls: 'glyphicon form-control-feedback'
17451 inputblock.cls += (this.before) ? '' : ' input-group';
17453 inputblock.cn.push({
17455 cls : 'input-group-addon input-group-append input-group-text',
17461 var ibwrap = inputblock;
17466 cls: 'roo-select2-choices',
17470 cls: 'roo-select2-search-field',
17483 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17488 cls: 'form-hidden-field'
17494 if(!this.multiple && this.showToggleBtn){
17500 if (this.caret != false) {
17503 cls: 'fa fa-' + this.caret
17510 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17512 Roo.bootstrap.version == 3 ? caret : '',
17515 cls: 'combobox-clear',
17529 combobox.cls += ' roo-select2-container-multi';
17532 var align = this.labelAlign || this.parentLabelAlign();
17534 if (align ==='left' && this.fieldLabel.length) {
17539 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17540 tooltip : 'This field is required'
17544 cls : 'control-label col-form-label',
17545 html : this.fieldLabel
17549 cls : 'roo-combobox-wrap ',
17556 var labelCfg = cfg.cn[1];
17557 var contentCfg = cfg.cn[2];
17560 if(this.indicatorpos == 'right'){
17565 cls : 'control-label col-form-label',
17569 html : this.fieldLabel
17573 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17574 tooltip : 'This field is required'
17579 cls : "roo-combobox-wrap ",
17587 labelCfg = cfg.cn[0];
17588 contentCfg = cfg.cn[1];
17593 if(this.labelWidth > 12){
17594 labelCfg.style = "width: " + this.labelWidth + 'px';
17597 if(this.labelWidth < 13 && this.labelmd == 0){
17598 this.labelmd = this.labelWidth;
17601 if(this.labellg > 0){
17602 labelCfg.cls += ' col-lg-' + this.labellg;
17603 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17606 if(this.labelmd > 0){
17607 labelCfg.cls += ' col-md-' + this.labelmd;
17608 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17611 if(this.labelsm > 0){
17612 labelCfg.cls += ' col-sm-' + this.labelsm;
17613 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17616 if(this.labelxs > 0){
17617 labelCfg.cls += ' col-xs-' + this.labelxs;
17618 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17622 } else if ( this.fieldLabel.length) {
17626 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17627 tooltip : 'This field is required'
17631 cls : 'control-label',
17632 html : this.fieldLabel
17643 if(this.indicatorpos == 'right'){
17647 cls : 'control-label',
17648 html : this.fieldLabel,
17652 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17653 tooltip : 'This field is required'
17670 var settings = this;
17672 ['xs','sm','md','lg'].map(function(size){
17673 if (settings[size]) {
17674 cfg.cls += ' col-' + size + '-' + settings[size];
17681 initTouchView : function()
17683 this.renderTouchView();
17685 this.touchViewEl.on('scroll', function(){
17686 this.el.dom.scrollTop = 0;
17689 this.originalValue = this.getValue();
17691 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17693 this.inputEl().on("click", this.showTouchView, this);
17694 if (this.triggerEl) {
17695 this.triggerEl.on("click", this.showTouchView, this);
17699 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17700 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17702 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17704 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17705 this.store.on('load', this.onTouchViewLoad, this);
17706 this.store.on('loadexception', this.onTouchViewLoadException, this);
17708 if(this.hiddenName){
17710 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17712 this.hiddenField.dom.value =
17713 this.hiddenValue !== undefined ? this.hiddenValue :
17714 this.value !== undefined ? this.value : '';
17716 this.el.dom.removeAttribute('name');
17717 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17721 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17722 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17725 if(this.removable && !this.multiple){
17726 var close = this.closeTriggerEl();
17728 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17729 close.on('click', this.removeBtnClick, this, close);
17733 * fix the bug in Safari iOS8
17735 this.inputEl().on("focus", function(e){
17736 document.activeElement.blur();
17739 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17746 renderTouchView : function()
17748 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17749 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17751 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17752 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17754 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17755 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17756 this.touchViewBodyEl.setStyle('overflow', 'auto');
17758 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17759 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17761 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17762 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17766 showTouchView : function()
17772 this.touchViewHeaderEl.hide();
17774 if(this.modalTitle.length){
17775 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17776 this.touchViewHeaderEl.show();
17779 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17780 this.touchViewEl.show();
17782 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17784 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17785 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17787 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17789 if(this.modalTitle.length){
17790 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17793 this.touchViewBodyEl.setHeight(bodyHeight);
17797 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17799 this.touchViewEl.addClass(['in','show']);
17802 if(this._touchViewMask){
17803 Roo.get(document.body).addClass("x-body-masked");
17804 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17805 this._touchViewMask.setStyle('z-index', 10000);
17806 this._touchViewMask.addClass('show');
17809 this.doTouchViewQuery();
17813 hideTouchView : function()
17815 this.touchViewEl.removeClass(['in','show']);
17819 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17821 this.touchViewEl.setStyle('display', 'none');
17824 if(this._touchViewMask){
17825 this._touchViewMask.removeClass('show');
17826 Roo.get(document.body).removeClass("x-body-masked");
17830 setTouchViewValue : function()
17837 Roo.each(this.tickItems, function(o){
17842 this.hideTouchView();
17845 doTouchViewQuery : function()
17854 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17858 if(!this.alwaysQuery || this.mode == 'local'){
17859 this.onTouchViewLoad();
17866 onTouchViewBeforeLoad : function(combo,opts)
17872 onTouchViewLoad : function()
17874 if(this.store.getCount() < 1){
17875 this.onTouchViewEmptyResults();
17879 this.clearTouchView();
17881 var rawValue = this.getRawValue();
17883 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17885 this.tickItems = [];
17887 this.store.data.each(function(d, rowIndex){
17888 var row = this.touchViewListGroup.createChild(template);
17890 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17891 row.addClass(d.data.cls);
17894 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17897 html : d.data[this.displayField]
17900 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17901 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17904 row.removeClass('selected');
17905 if(!this.multiple && this.valueField &&
17906 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17909 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17910 row.addClass('selected');
17913 if(this.multiple && this.valueField &&
17914 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17918 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17919 this.tickItems.push(d.data);
17922 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17926 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17928 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17930 if(this.modalTitle.length){
17931 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17934 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17936 if(this.mobile_restrict_height && listHeight < bodyHeight){
17937 this.touchViewBodyEl.setHeight(listHeight);
17942 if(firstChecked && listHeight > bodyHeight){
17943 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17948 onTouchViewLoadException : function()
17950 this.hideTouchView();
17953 onTouchViewEmptyResults : function()
17955 this.clearTouchView();
17957 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17959 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17963 clearTouchView : function()
17965 this.touchViewListGroup.dom.innerHTML = '';
17968 onTouchViewClick : function(e, el, o)
17970 e.preventDefault();
17973 var rowIndex = o.rowIndex;
17975 var r = this.store.getAt(rowIndex);
17977 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17979 if(!this.multiple){
17980 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17981 c.dom.removeAttribute('checked');
17984 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17986 this.setFromData(r.data);
17988 var close = this.closeTriggerEl();
17994 this.hideTouchView();
17996 this.fireEvent('select', this, r, rowIndex);
18001 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18002 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18003 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18007 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18008 this.addItem(r.data);
18009 this.tickItems.push(r.data);
18013 getAutoCreateNativeIOS : function()
18016 cls: 'form-group' //input-group,
18021 cls : 'roo-ios-select'
18025 combobox.name = this.name;
18028 if (this.disabled) {
18029 combobox.disabled = true;
18032 var settings = this;
18034 ['xs','sm','md','lg'].map(function(size){
18035 if (settings[size]) {
18036 cfg.cls += ' col-' + size + '-' + settings[size];
18046 initIOSView : function()
18048 this.store.on('load', this.onIOSViewLoad, this);
18053 onIOSViewLoad : function()
18055 if(this.store.getCount() < 1){
18059 this.clearIOSView();
18061 if(this.allowBlank) {
18063 var default_text = '-- SELECT --';
18065 if(this.placeholder.length){
18066 default_text = this.placeholder;
18069 if(this.emptyTitle.length){
18070 default_text += ' - ' + this.emptyTitle + ' -';
18073 var opt = this.inputEl().createChild({
18076 html : default_text
18080 o[this.valueField] = 0;
18081 o[this.displayField] = default_text;
18083 this.ios_options.push({
18090 this.store.data.each(function(d, rowIndex){
18094 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18095 html = d.data[this.displayField];
18100 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18101 value = d.data[this.valueField];
18110 if(this.value == d.data[this.valueField]){
18111 option['selected'] = true;
18114 var opt = this.inputEl().createChild(option);
18116 this.ios_options.push({
18123 this.inputEl().on('change', function(){
18124 this.fireEvent('select', this);
18129 clearIOSView: function()
18131 this.inputEl().dom.innerHTML = '';
18133 this.ios_options = [];
18136 setIOSValue: function(v)
18140 if(!this.ios_options){
18144 Roo.each(this.ios_options, function(opts){
18146 opts.el.dom.removeAttribute('selected');
18148 if(opts.data[this.valueField] != v){
18152 opts.el.dom.setAttribute('selected', true);
18158 * @cfg {Boolean} grow
18162 * @cfg {Number} growMin
18166 * @cfg {Number} growMax
18175 Roo.apply(Roo.bootstrap.ComboBox, {
18179 cls: 'modal-header',
18201 cls: 'list-group-item',
18205 cls: 'roo-combobox-list-group-item-value'
18209 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18223 listItemCheckbox : {
18225 cls: 'list-group-item',
18229 cls: 'roo-combobox-list-group-item-value'
18233 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18249 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18254 cls: 'modal-footer',
18262 cls: 'col-xs-6 text-left',
18265 cls: 'btn btn-danger roo-touch-view-cancel',
18271 cls: 'col-xs-6 text-right',
18274 cls: 'btn btn-success roo-touch-view-ok',
18285 Roo.apply(Roo.bootstrap.ComboBox, {
18287 touchViewTemplate : {
18289 cls: 'modal fade roo-combobox-touch-view',
18293 cls: 'modal-dialog',
18294 style : 'position:fixed', // we have to fix position....
18298 cls: 'modal-content',
18300 Roo.bootstrap.ComboBox.header,
18301 Roo.bootstrap.ComboBox.body,
18302 Roo.bootstrap.ComboBox.footer
18311 * Ext JS Library 1.1.1
18312 * Copyright(c) 2006-2007, Ext JS, LLC.
18314 * Originally Released Under LGPL - original licence link has changed is not relivant.
18317 * <script type="text/javascript">
18322 * @extends Roo.util.Observable
18323 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18324 * This class also supports single and multi selection modes. <br>
18325 * Create a data model bound view:
18327 var store = new Roo.data.Store(...);
18329 var view = new Roo.View({
18331 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18333 singleSelect: true,
18334 selectedClass: "ydataview-selected",
18338 // listen for node click?
18339 view.on("click", function(vw, index, node, e){
18340 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18344 dataModel.load("foobar.xml");
18346 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18348 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18349 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18351 * Note: old style constructor is still suported (container, template, config)
18354 * Create a new View
18355 * @param {Object} config The config object
18358 Roo.View = function(config, depreciated_tpl, depreciated_config){
18360 this.parent = false;
18362 if (typeof(depreciated_tpl) == 'undefined') {
18363 // new way.. - universal constructor.
18364 Roo.apply(this, config);
18365 this.el = Roo.get(this.el);
18368 this.el = Roo.get(config);
18369 this.tpl = depreciated_tpl;
18370 Roo.apply(this, depreciated_config);
18372 this.wrapEl = this.el.wrap().wrap();
18373 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18376 if(typeof(this.tpl) == "string"){
18377 this.tpl = new Roo.Template(this.tpl);
18379 // support xtype ctors..
18380 this.tpl = new Roo.factory(this.tpl, Roo);
18384 this.tpl.compile();
18389 * @event beforeclick
18390 * Fires before a click is processed. Returns false to cancel the default action.
18391 * @param {Roo.View} this
18392 * @param {Number} index The index of the target node
18393 * @param {HTMLElement} node The target node
18394 * @param {Roo.EventObject} e The raw event object
18396 "beforeclick" : true,
18399 * Fires when a template node is clicked.
18400 * @param {Roo.View} this
18401 * @param {Number} index The index of the target node
18402 * @param {HTMLElement} node The target node
18403 * @param {Roo.EventObject} e The raw event object
18408 * Fires when a template node is double clicked.
18409 * @param {Roo.View} this
18410 * @param {Number} index The index of the target node
18411 * @param {HTMLElement} node The target node
18412 * @param {Roo.EventObject} e The raw event object
18416 * @event contextmenu
18417 * Fires when a template node is right clicked.
18418 * @param {Roo.View} this
18419 * @param {Number} index The index of the target node
18420 * @param {HTMLElement} node The target node
18421 * @param {Roo.EventObject} e The raw event object
18423 "contextmenu" : true,
18425 * @event selectionchange
18426 * Fires when the selected nodes change.
18427 * @param {Roo.View} this
18428 * @param {Array} selections Array of the selected nodes
18430 "selectionchange" : true,
18433 * @event beforeselect
18434 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18435 * @param {Roo.View} this
18436 * @param {HTMLElement} node The node to be selected
18437 * @param {Array} selections Array of currently selected nodes
18439 "beforeselect" : true,
18441 * @event preparedata
18442 * Fires on every row to render, to allow you to change the data.
18443 * @param {Roo.View} this
18444 * @param {Object} data to be rendered (change this)
18446 "preparedata" : true
18454 "click": this.onClick,
18455 "dblclick": this.onDblClick,
18456 "contextmenu": this.onContextMenu,
18460 this.selections = [];
18462 this.cmp = new Roo.CompositeElementLite([]);
18464 this.store = Roo.factory(this.store, Roo.data);
18465 this.setStore(this.store, true);
18468 if ( this.footer && this.footer.xtype) {
18470 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18472 this.footer.dataSource = this.store;
18473 this.footer.container = fctr;
18474 this.footer = Roo.factory(this.footer, Roo);
18475 fctr.insertFirst(this.el);
18477 // this is a bit insane - as the paging toolbar seems to detach the el..
18478 // dom.parentNode.parentNode.parentNode
18479 // they get detached?
18483 Roo.View.superclass.constructor.call(this);
18488 Roo.extend(Roo.View, Roo.util.Observable, {
18491 * @cfg {Roo.data.Store} store Data store to load data from.
18496 * @cfg {String|Roo.Element} el The container element.
18501 * @cfg {String|Roo.Template} tpl The template used by this View
18505 * @cfg {String} dataName the named area of the template to use as the data area
18506 * Works with domtemplates roo-name="name"
18510 * @cfg {String} selectedClass The css class to add to selected nodes
18512 selectedClass : "x-view-selected",
18514 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18519 * @cfg {String} text to display on mask (default Loading)
18523 * @cfg {Boolean} multiSelect Allow multiple selection
18525 multiSelect : false,
18527 * @cfg {Boolean} singleSelect Allow single selection
18529 singleSelect: false,
18532 * @cfg {Boolean} toggleSelect - selecting
18534 toggleSelect : false,
18537 * @cfg {Boolean} tickable - selecting
18542 * Returns the element this view is bound to.
18543 * @return {Roo.Element}
18545 getEl : function(){
18546 return this.wrapEl;
18552 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18554 refresh : function(){
18555 //Roo.log('refresh');
18558 // if we are using something like 'domtemplate', then
18559 // the what gets used is:
18560 // t.applySubtemplate(NAME, data, wrapping data..)
18561 // the outer template then get' applied with
18562 // the store 'extra data'
18563 // and the body get's added to the
18564 // roo-name="data" node?
18565 // <span class='roo-tpl-{name}'></span> ?????
18569 this.clearSelections();
18570 this.el.update("");
18572 var records = this.store.getRange();
18573 if(records.length < 1) {
18575 // is this valid?? = should it render a template??
18577 this.el.update(this.emptyText);
18581 if (this.dataName) {
18582 this.el.update(t.apply(this.store.meta)); //????
18583 el = this.el.child('.roo-tpl-' + this.dataName);
18586 for(var i = 0, len = records.length; i < len; i++){
18587 var data = this.prepareData(records[i].data, i, records[i]);
18588 this.fireEvent("preparedata", this, data, i, records[i]);
18590 var d = Roo.apply({}, data);
18593 Roo.apply(d, {'roo-id' : Roo.id()});
18597 Roo.each(this.parent.item, function(item){
18598 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18601 Roo.apply(d, {'roo-data-checked' : 'checked'});
18605 html[html.length] = Roo.util.Format.trim(
18607 t.applySubtemplate(this.dataName, d, this.store.meta) :
18614 el.update(html.join(""));
18615 this.nodes = el.dom.childNodes;
18616 this.updateIndexes(0);
18621 * Function to override to reformat the data that is sent to
18622 * the template for each node.
18623 * DEPRICATED - use the preparedata event handler.
18624 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18625 * a JSON object for an UpdateManager bound view).
18627 prepareData : function(data, index, record)
18629 this.fireEvent("preparedata", this, data, index, record);
18633 onUpdate : function(ds, record){
18634 // Roo.log('on update');
18635 this.clearSelections();
18636 var index = this.store.indexOf(record);
18637 var n = this.nodes[index];
18638 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18639 n.parentNode.removeChild(n);
18640 this.updateIndexes(index, index);
18646 onAdd : function(ds, records, index)
18648 //Roo.log(['on Add', ds, records, index] );
18649 this.clearSelections();
18650 if(this.nodes.length == 0){
18654 var n = this.nodes[index];
18655 for(var i = 0, len = records.length; i < len; i++){
18656 var d = this.prepareData(records[i].data, i, records[i]);
18658 this.tpl.insertBefore(n, d);
18661 this.tpl.append(this.el, d);
18664 this.updateIndexes(index);
18667 onRemove : function(ds, record, index){
18668 // Roo.log('onRemove');
18669 this.clearSelections();
18670 var el = this.dataName ?
18671 this.el.child('.roo-tpl-' + this.dataName) :
18674 el.dom.removeChild(this.nodes[index]);
18675 this.updateIndexes(index);
18679 * Refresh an individual node.
18680 * @param {Number} index
18682 refreshNode : function(index){
18683 this.onUpdate(this.store, this.store.getAt(index));
18686 updateIndexes : function(startIndex, endIndex){
18687 var ns = this.nodes;
18688 startIndex = startIndex || 0;
18689 endIndex = endIndex || ns.length - 1;
18690 for(var i = startIndex; i <= endIndex; i++){
18691 ns[i].nodeIndex = i;
18696 * Changes the data store this view uses and refresh the view.
18697 * @param {Store} store
18699 setStore : function(store, initial){
18700 if(!initial && this.store){
18701 this.store.un("datachanged", this.refresh);
18702 this.store.un("add", this.onAdd);
18703 this.store.un("remove", this.onRemove);
18704 this.store.un("update", this.onUpdate);
18705 this.store.un("clear", this.refresh);
18706 this.store.un("beforeload", this.onBeforeLoad);
18707 this.store.un("load", this.onLoad);
18708 this.store.un("loadexception", this.onLoad);
18712 store.on("datachanged", this.refresh, this);
18713 store.on("add", this.onAdd, this);
18714 store.on("remove", this.onRemove, this);
18715 store.on("update", this.onUpdate, this);
18716 store.on("clear", this.refresh, this);
18717 store.on("beforeload", this.onBeforeLoad, this);
18718 store.on("load", this.onLoad, this);
18719 store.on("loadexception", this.onLoad, this);
18727 * onbeforeLoad - masks the loading area.
18730 onBeforeLoad : function(store,opts)
18732 //Roo.log('onBeforeLoad');
18734 this.el.update("");
18736 this.el.mask(this.mask ? this.mask : "Loading" );
18738 onLoad : function ()
18745 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18746 * @param {HTMLElement} node
18747 * @return {HTMLElement} The template node
18749 findItemFromChild : function(node){
18750 var el = this.dataName ?
18751 this.el.child('.roo-tpl-' + this.dataName,true) :
18754 if(!node || node.parentNode == el){
18757 var p = node.parentNode;
18758 while(p && p != el){
18759 if(p.parentNode == el){
18768 onClick : function(e){
18769 var item = this.findItemFromChild(e.getTarget());
18771 var index = this.indexOf(item);
18772 if(this.onItemClick(item, index, e) !== false){
18773 this.fireEvent("click", this, index, item, e);
18776 this.clearSelections();
18781 onContextMenu : function(e){
18782 var item = this.findItemFromChild(e.getTarget());
18784 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18789 onDblClick : function(e){
18790 var item = this.findItemFromChild(e.getTarget());
18792 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18796 onItemClick : function(item, index, e)
18798 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18801 if (this.toggleSelect) {
18802 var m = this.isSelected(item) ? 'unselect' : 'select';
18805 _t[m](item, true, false);
18808 if(this.multiSelect || this.singleSelect){
18809 if(this.multiSelect && e.shiftKey && this.lastSelection){
18810 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18812 this.select(item, this.multiSelect && e.ctrlKey);
18813 this.lastSelection = item;
18816 if(!this.tickable){
18817 e.preventDefault();
18825 * Get the number of selected nodes.
18828 getSelectionCount : function(){
18829 return this.selections.length;
18833 * Get the currently selected nodes.
18834 * @return {Array} An array of HTMLElements
18836 getSelectedNodes : function(){
18837 return this.selections;
18841 * Get the indexes of the selected nodes.
18844 getSelectedIndexes : function(){
18845 var indexes = [], s = this.selections;
18846 for(var i = 0, len = s.length; i < len; i++){
18847 indexes.push(s[i].nodeIndex);
18853 * Clear all selections
18854 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18856 clearSelections : function(suppressEvent){
18857 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18858 this.cmp.elements = this.selections;
18859 this.cmp.removeClass(this.selectedClass);
18860 this.selections = [];
18861 if(!suppressEvent){
18862 this.fireEvent("selectionchange", this, this.selections);
18868 * Returns true if the passed node is selected
18869 * @param {HTMLElement/Number} node The node or node index
18870 * @return {Boolean}
18872 isSelected : function(node){
18873 var s = this.selections;
18877 node = this.getNode(node);
18878 return s.indexOf(node) !== -1;
18883 * @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
18884 * @param {Boolean} keepExisting (optional) true to keep existing selections
18885 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18887 select : function(nodeInfo, keepExisting, suppressEvent){
18888 if(nodeInfo instanceof Array){
18890 this.clearSelections(true);
18892 for(var i = 0, len = nodeInfo.length; i < len; i++){
18893 this.select(nodeInfo[i], true, true);
18897 var node = this.getNode(nodeInfo);
18898 if(!node || this.isSelected(node)){
18899 return; // already selected.
18902 this.clearSelections(true);
18905 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18906 Roo.fly(node).addClass(this.selectedClass);
18907 this.selections.push(node);
18908 if(!suppressEvent){
18909 this.fireEvent("selectionchange", this, this.selections);
18917 * @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
18918 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18919 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18921 unselect : function(nodeInfo, keepExisting, suppressEvent)
18923 if(nodeInfo instanceof Array){
18924 Roo.each(this.selections, function(s) {
18925 this.unselect(s, nodeInfo);
18929 var node = this.getNode(nodeInfo);
18930 if(!node || !this.isSelected(node)){
18931 //Roo.log("not selected");
18932 return; // not selected.
18936 Roo.each(this.selections, function(s) {
18938 Roo.fly(node).removeClass(this.selectedClass);
18945 this.selections= ns;
18946 this.fireEvent("selectionchange", this, this.selections);
18950 * Gets a template node.
18951 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18952 * @return {HTMLElement} The node or null if it wasn't found
18954 getNode : function(nodeInfo){
18955 if(typeof nodeInfo == "string"){
18956 return document.getElementById(nodeInfo);
18957 }else if(typeof nodeInfo == "number"){
18958 return this.nodes[nodeInfo];
18964 * Gets a range template nodes.
18965 * @param {Number} startIndex
18966 * @param {Number} endIndex
18967 * @return {Array} An array of nodes
18969 getNodes : function(start, end){
18970 var ns = this.nodes;
18971 start = start || 0;
18972 end = typeof end == "undefined" ? ns.length - 1 : end;
18975 for(var i = start; i <= end; i++){
18979 for(var i = start; i >= end; i--){
18987 * Finds the index of the passed node
18988 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18989 * @return {Number} The index of the node or -1
18991 indexOf : function(node){
18992 node = this.getNode(node);
18993 if(typeof node.nodeIndex == "number"){
18994 return node.nodeIndex;
18996 var ns = this.nodes;
18997 for(var i = 0, len = ns.length; i < len; i++){
19008 * based on jquery fullcalendar
19012 Roo.bootstrap = Roo.bootstrap || {};
19014 * @class Roo.bootstrap.Calendar
19015 * @extends Roo.bootstrap.Component
19016 * Bootstrap Calendar class
19017 * @cfg {Boolean} loadMask (true|false) default false
19018 * @cfg {Object} header generate the user specific header of the calendar, default false
19021 * Create a new Container
19022 * @param {Object} config The config object
19027 Roo.bootstrap.Calendar = function(config){
19028 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19032 * Fires when a date is selected
19033 * @param {DatePicker} this
19034 * @param {Date} date The selected date
19038 * @event monthchange
19039 * Fires when the displayed month changes
19040 * @param {DatePicker} this
19041 * @param {Date} date The selected month
19043 'monthchange': true,
19045 * @event evententer
19046 * Fires when mouse over an event
19047 * @param {Calendar} this
19048 * @param {event} Event
19050 'evententer': true,
19052 * @event eventleave
19053 * Fires when the mouse leaves an
19054 * @param {Calendar} this
19057 'eventleave': true,
19059 * @event eventclick
19060 * Fires when the mouse click an
19061 * @param {Calendar} this
19070 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19073 * @cfg {Number} startDay
19074 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19082 getAutoCreate : function(){
19085 var fc_button = function(name, corner, style, content ) {
19086 return Roo.apply({},{
19088 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19090 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19093 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19104 style : 'width:100%',
19111 cls : 'fc-header-left',
19113 fc_button('prev', 'left', 'arrow', '‹' ),
19114 fc_button('next', 'right', 'arrow', '›' ),
19115 { tag: 'span', cls: 'fc-header-space' },
19116 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19124 cls : 'fc-header-center',
19128 cls: 'fc-header-title',
19131 html : 'month / year'
19139 cls : 'fc-header-right',
19141 /* fc_button('month', 'left', '', 'month' ),
19142 fc_button('week', '', '', 'week' ),
19143 fc_button('day', 'right', '', 'day' )
19155 header = this.header;
19158 var cal_heads = function() {
19160 // fixme - handle this.
19162 for (var i =0; i < Date.dayNames.length; i++) {
19163 var d = Date.dayNames[i];
19166 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19167 html : d.substring(0,3)
19171 ret[0].cls += ' fc-first';
19172 ret[6].cls += ' fc-last';
19175 var cal_cell = function(n) {
19178 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19183 cls: 'fc-day-number',
19187 cls: 'fc-day-content',
19191 style: 'position: relative;' // height: 17px;
19203 var cal_rows = function() {
19206 for (var r = 0; r < 6; r++) {
19213 for (var i =0; i < Date.dayNames.length; i++) {
19214 var d = Date.dayNames[i];
19215 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19218 row.cn[0].cls+=' fc-first';
19219 row.cn[0].cn[0].style = 'min-height:90px';
19220 row.cn[6].cls+=' fc-last';
19224 ret[0].cls += ' fc-first';
19225 ret[4].cls += ' fc-prev-last';
19226 ret[5].cls += ' fc-last';
19233 cls: 'fc-border-separate',
19234 style : 'width:100%',
19242 cls : 'fc-first fc-last',
19260 cls : 'fc-content',
19261 style : "position: relative;",
19264 cls : 'fc-view fc-view-month fc-grid',
19265 style : 'position: relative',
19266 unselectable : 'on',
19269 cls : 'fc-event-container',
19270 style : 'position:absolute;z-index:8;top:0;left:0;'
19288 initEvents : function()
19291 throw "can not find store for calendar";
19297 style: "text-align:center",
19301 style: "background-color:white;width:50%;margin:250 auto",
19305 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19316 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19318 var size = this.el.select('.fc-content', true).first().getSize();
19319 this.maskEl.setSize(size.width, size.height);
19320 this.maskEl.enableDisplayMode("block");
19321 if(!this.loadMask){
19322 this.maskEl.hide();
19325 this.store = Roo.factory(this.store, Roo.data);
19326 this.store.on('load', this.onLoad, this);
19327 this.store.on('beforeload', this.onBeforeLoad, this);
19331 this.cells = this.el.select('.fc-day',true);
19332 //Roo.log(this.cells);
19333 this.textNodes = this.el.query('.fc-day-number');
19334 this.cells.addClassOnOver('fc-state-hover');
19336 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19337 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19338 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19339 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19341 this.on('monthchange', this.onMonthChange, this);
19343 this.update(new Date().clearTime());
19346 resize : function() {
19347 var sz = this.el.getSize();
19349 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19350 this.el.select('.fc-day-content div',true).setHeight(34);
19355 showPrevMonth : function(e){
19356 this.update(this.activeDate.add("mo", -1));
19358 showToday : function(e){
19359 this.update(new Date().clearTime());
19362 showNextMonth : function(e){
19363 this.update(this.activeDate.add("mo", 1));
19367 showPrevYear : function(){
19368 this.update(this.activeDate.add("y", -1));
19372 showNextYear : function(){
19373 this.update(this.activeDate.add("y", 1));
19378 update : function(date)
19380 var vd = this.activeDate;
19381 this.activeDate = date;
19382 // if(vd && this.el){
19383 // var t = date.getTime();
19384 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19385 // Roo.log('using add remove');
19387 // this.fireEvent('monthchange', this, date);
19389 // this.cells.removeClass("fc-state-highlight");
19390 // this.cells.each(function(c){
19391 // if(c.dateValue == t){
19392 // c.addClass("fc-state-highlight");
19393 // setTimeout(function(){
19394 // try{c.dom.firstChild.focus();}catch(e){}
19404 var days = date.getDaysInMonth();
19406 var firstOfMonth = date.getFirstDateOfMonth();
19407 var startingPos = firstOfMonth.getDay()-this.startDay;
19409 if(startingPos < this.startDay){
19413 var pm = date.add(Date.MONTH, -1);
19414 var prevStart = pm.getDaysInMonth()-startingPos;
19416 this.cells = this.el.select('.fc-day',true);
19417 this.textNodes = this.el.query('.fc-day-number');
19418 this.cells.addClassOnOver('fc-state-hover');
19420 var cells = this.cells.elements;
19421 var textEls = this.textNodes;
19423 Roo.each(cells, function(cell){
19424 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19427 days += startingPos;
19429 // convert everything to numbers so it's fast
19430 var day = 86400000;
19431 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19434 //Roo.log(prevStart);
19436 var today = new Date().clearTime().getTime();
19437 var sel = date.clearTime().getTime();
19438 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19439 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19440 var ddMatch = this.disabledDatesRE;
19441 var ddText = this.disabledDatesText;
19442 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19443 var ddaysText = this.disabledDaysText;
19444 var format = this.format;
19446 var setCellClass = function(cal, cell){
19450 //Roo.log('set Cell Class');
19452 var t = d.getTime();
19456 cell.dateValue = t;
19458 cell.className += " fc-today";
19459 cell.className += " fc-state-highlight";
19460 cell.title = cal.todayText;
19463 // disable highlight in other month..
19464 //cell.className += " fc-state-highlight";
19469 cell.className = " fc-state-disabled";
19470 cell.title = cal.minText;
19474 cell.className = " fc-state-disabled";
19475 cell.title = cal.maxText;
19479 if(ddays.indexOf(d.getDay()) != -1){
19480 cell.title = ddaysText;
19481 cell.className = " fc-state-disabled";
19484 if(ddMatch && format){
19485 var fvalue = d.dateFormat(format);
19486 if(ddMatch.test(fvalue)){
19487 cell.title = ddText.replace("%0", fvalue);
19488 cell.className = " fc-state-disabled";
19492 if (!cell.initialClassName) {
19493 cell.initialClassName = cell.dom.className;
19496 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19501 for(; i < startingPos; i++) {
19502 textEls[i].innerHTML = (++prevStart);
19503 d.setDate(d.getDate()+1);
19505 cells[i].className = "fc-past fc-other-month";
19506 setCellClass(this, cells[i]);
19511 for(; i < days; i++){
19512 intDay = i - startingPos + 1;
19513 textEls[i].innerHTML = (intDay);
19514 d.setDate(d.getDate()+1);
19516 cells[i].className = ''; // "x-date-active";
19517 setCellClass(this, cells[i]);
19521 for(; i < 42; i++) {
19522 textEls[i].innerHTML = (++extraDays);
19523 d.setDate(d.getDate()+1);
19525 cells[i].className = "fc-future fc-other-month";
19526 setCellClass(this, cells[i]);
19529 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19531 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19533 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19534 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19536 if(totalRows != 6){
19537 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19538 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19541 this.fireEvent('monthchange', this, date);
19545 if(!this.internalRender){
19546 var main = this.el.dom.firstChild;
19547 var w = main.offsetWidth;
19548 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19549 Roo.fly(main).setWidth(w);
19550 this.internalRender = true;
19551 // opera does not respect the auto grow header center column
19552 // then, after it gets a width opera refuses to recalculate
19553 // without a second pass
19554 if(Roo.isOpera && !this.secondPass){
19555 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19556 this.secondPass = true;
19557 this.update.defer(10, this, [date]);
19564 findCell : function(dt) {
19565 dt = dt.clearTime().getTime();
19567 this.cells.each(function(c){
19568 //Roo.log("check " +c.dateValue + '?=' + dt);
19569 if(c.dateValue == dt){
19579 findCells : function(ev) {
19580 var s = ev.start.clone().clearTime().getTime();
19582 var e= ev.end.clone().clearTime().getTime();
19585 this.cells.each(function(c){
19586 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19588 if(c.dateValue > e){
19591 if(c.dateValue < s){
19600 // findBestRow: function(cells)
19604 // for (var i =0 ; i < cells.length;i++) {
19605 // ret = Math.max(cells[i].rows || 0,ret);
19612 addItem : function(ev)
19614 // look for vertical location slot in
19615 var cells = this.findCells(ev);
19617 // ev.row = this.findBestRow(cells);
19619 // work out the location.
19623 for(var i =0; i < cells.length; i++) {
19625 cells[i].row = cells[0].row;
19628 cells[i].row = cells[i].row + 1;
19638 if (crow.start.getY() == cells[i].getY()) {
19640 crow.end = cells[i];
19657 cells[0].events.push(ev);
19659 this.calevents.push(ev);
19662 clearEvents: function() {
19664 if(!this.calevents){
19668 Roo.each(this.cells.elements, function(c){
19674 Roo.each(this.calevents, function(e) {
19675 Roo.each(e.els, function(el) {
19676 el.un('mouseenter' ,this.onEventEnter, this);
19677 el.un('mouseleave' ,this.onEventLeave, this);
19682 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19688 renderEvents: function()
19692 this.cells.each(function(c) {
19701 if(c.row != c.events.length){
19702 r = 4 - (4 - (c.row - c.events.length));
19705 c.events = ev.slice(0, r);
19706 c.more = ev.slice(r);
19708 if(c.more.length && c.more.length == 1){
19709 c.events.push(c.more.pop());
19712 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19716 this.cells.each(function(c) {
19718 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19721 for (var e = 0; e < c.events.length; e++){
19722 var ev = c.events[e];
19723 var rows = ev.rows;
19725 for(var i = 0; i < rows.length; i++) {
19727 // how many rows should it span..
19730 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19731 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19733 unselectable : "on",
19736 cls: 'fc-event-inner',
19740 // cls: 'fc-event-time',
19741 // html : cells.length > 1 ? '' : ev.time
19745 cls: 'fc-event-title',
19746 html : String.format('{0}', ev.title)
19753 cls: 'ui-resizable-handle ui-resizable-e',
19754 html : '  '
19761 cfg.cls += ' fc-event-start';
19763 if ((i+1) == rows.length) {
19764 cfg.cls += ' fc-event-end';
19767 var ctr = _this.el.select('.fc-event-container',true).first();
19768 var cg = ctr.createChild(cfg);
19770 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19771 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19773 var r = (c.more.length) ? 1 : 0;
19774 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19775 cg.setWidth(ebox.right - sbox.x -2);
19777 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19778 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19779 cg.on('click', _this.onEventClick, _this, ev);
19790 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19791 style : 'position: absolute',
19792 unselectable : "on",
19795 cls: 'fc-event-inner',
19799 cls: 'fc-event-title',
19807 cls: 'ui-resizable-handle ui-resizable-e',
19808 html : '  '
19814 var ctr = _this.el.select('.fc-event-container',true).first();
19815 var cg = ctr.createChild(cfg);
19817 var sbox = c.select('.fc-day-content',true).first().getBox();
19818 var ebox = c.select('.fc-day-content',true).first().getBox();
19820 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19821 cg.setWidth(ebox.right - sbox.x -2);
19823 cg.on('click', _this.onMoreEventClick, _this, c.more);
19833 onEventEnter: function (e, el,event,d) {
19834 this.fireEvent('evententer', this, el, event);
19837 onEventLeave: function (e, el,event,d) {
19838 this.fireEvent('eventleave', this, el, event);
19841 onEventClick: function (e, el,event,d) {
19842 this.fireEvent('eventclick', this, el, event);
19845 onMonthChange: function () {
19849 onMoreEventClick: function(e, el, more)
19853 this.calpopover.placement = 'right';
19854 this.calpopover.setTitle('More');
19856 this.calpopover.setContent('');
19858 var ctr = this.calpopover.el.select('.popover-content', true).first();
19860 Roo.each(more, function(m){
19862 cls : 'fc-event-hori fc-event-draggable',
19865 var cg = ctr.createChild(cfg);
19867 cg.on('click', _this.onEventClick, _this, m);
19870 this.calpopover.show(el);
19875 onLoad: function ()
19877 this.calevents = [];
19880 if(this.store.getCount() > 0){
19881 this.store.data.each(function(d){
19884 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19885 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19886 time : d.data.start_time,
19887 title : d.data.title,
19888 description : d.data.description,
19889 venue : d.data.venue
19894 this.renderEvents();
19896 if(this.calevents.length && this.loadMask){
19897 this.maskEl.hide();
19901 onBeforeLoad: function()
19903 this.clearEvents();
19905 this.maskEl.show();
19919 * @class Roo.bootstrap.Popover
19920 * @extends Roo.bootstrap.Component
19921 * Bootstrap Popover class
19922 * @cfg {String} html contents of the popover (or false to use children..)
19923 * @cfg {String} title of popover (or false to hide)
19924 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19925 * @cfg {String} trigger click || hover (or false to trigger manually)
19926 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19927 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19928 * - if false and it has a 'parent' then it will be automatically added to that element
19929 * - if string - Roo.get will be called
19930 * @cfg {Number} delay - delay before showing
19933 * Create a new Popover
19934 * @param {Object} config The config object
19937 Roo.bootstrap.Popover = function(config){
19938 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19944 * After the popover show
19946 * @param {Roo.bootstrap.Popover} this
19951 * After the popover hide
19953 * @param {Roo.bootstrap.Popover} this
19959 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19964 placement : 'right',
19965 trigger : 'hover', // hover
19971 can_build_overlaid : false,
19973 maskEl : false, // the mask element
19976 alignEl : false, // when show is called with an element - this get's stored.
19978 getChildContainer : function()
19980 return this.contentEl;
19983 getPopoverHeader : function()
19985 this.title = true; // flag not to hide it..
19986 this.headerEl.addClass('p-0');
19987 return this.headerEl
19991 getAutoCreate : function(){
19994 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19995 style: 'display:block',
20001 cls : 'popover-inner ',
20005 cls: 'popover-title popover-header',
20006 html : this.title === false ? '' : this.title
20009 cls : 'popover-content popover-body ' + (this.cls || ''),
20010 html : this.html || ''
20021 * @param {string} the title
20023 setTitle: function(str)
20027 this.headerEl.dom.innerHTML = str;
20032 * @param {string} the body content
20034 setContent: function(str)
20037 if (this.contentEl) {
20038 this.contentEl.dom.innerHTML = str;
20042 // as it get's added to the bottom of the page.
20043 onRender : function(ct, position)
20045 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20050 var cfg = Roo.apply({}, this.getAutoCreate());
20054 cfg.cls += ' ' + this.cls;
20057 cfg.style = this.style;
20059 //Roo.log("adding to ");
20060 this.el = Roo.get(document.body).createChild(cfg, position);
20061 // Roo.log(this.el);
20064 this.contentEl = this.el.select('.popover-content',true).first();
20065 this.headerEl = this.el.select('.popover-title',true).first();
20068 if(typeof(this.items) != 'undefined'){
20069 var items = this.items;
20072 for(var i =0;i < items.length;i++) {
20073 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20077 this.items = nitems;
20079 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20080 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20087 resizeMask : function()
20089 this.maskEl.setSize(
20090 Roo.lib.Dom.getViewWidth(true),
20091 Roo.lib.Dom.getViewHeight(true)
20095 initEvents : function()
20099 Roo.bootstrap.Popover.register(this);
20102 this.arrowEl = this.el.select('.arrow',true).first();
20103 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20104 this.el.enableDisplayMode('block');
20108 if (this.over === false && !this.parent()) {
20111 if (this.triggers === false) {
20116 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20117 var triggers = this.trigger ? this.trigger.split(' ') : [];
20118 Roo.each(triggers, function(trigger) {
20120 if (trigger == 'click') {
20121 on_el.on('click', this.toggle, this);
20122 } else if (trigger != 'manual') {
20123 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20124 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20126 on_el.on(eventIn ,this.enter, this);
20127 on_el.on(eventOut, this.leave, this);
20137 toggle : function () {
20138 this.hoverState == 'in' ? this.leave() : this.enter();
20141 enter : function () {
20143 clearTimeout(this.timeout);
20145 this.hoverState = 'in';
20147 if (!this.delay || !this.delay.show) {
20152 this.timeout = setTimeout(function () {
20153 if (_t.hoverState == 'in') {
20156 }, this.delay.show)
20159 leave : function() {
20160 clearTimeout(this.timeout);
20162 this.hoverState = 'out';
20164 if (!this.delay || !this.delay.hide) {
20169 this.timeout = setTimeout(function () {
20170 if (_t.hoverState == 'out') {
20173 }, this.delay.hide)
20177 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20178 * @param {string} (left|right|top|bottom) position
20180 show : function (on_el, placement)
20182 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20183 on_el = on_el || false; // default to false
20186 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20187 on_el = this.parent().el;
20188 } else if (this.over) {
20189 Roo.get(this.over);
20194 this.alignEl = Roo.get( on_el );
20197 this.render(document.body);
20203 if (this.title === false) {
20204 this.headerEl.hide();
20209 this.el.dom.style.display = 'block';
20212 if (this.alignEl) {
20213 this.updatePosition(this.placement, true);
20216 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20217 var es = this.el.getSize();
20218 var x = Roo.lib.Dom.getViewWidth()/2;
20219 var y = Roo.lib.Dom.getViewHeight()/2;
20220 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20225 //var arrow = this.el.select('.arrow',true).first();
20226 //arrow.set(align[2],
20228 this.el.addClass('in');
20232 this.hoverState = 'in';
20235 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20236 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20237 this.maskEl.dom.style.display = 'block';
20238 this.maskEl.addClass('show');
20240 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20242 this.fireEvent('show', this);
20246 * fire this manually after loading a grid in the table for example
20247 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20248 * @param {Boolean} try and move it if we cant get right position.
20250 updatePosition : function(placement, try_move)
20252 // allow for calling with no parameters
20253 placement = placement ? placement : this.placement;
20254 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20256 this.el.removeClass([
20257 'fade','top','bottom', 'left', 'right','in',
20258 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20260 this.el.addClass(placement + ' bs-popover-' + placement);
20262 if (!this.alignEl ) {
20266 switch (placement) {
20268 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20269 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20270 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20271 //normal display... or moved up/down.
20272 this.el.setXY(offset);
20273 var xy = this.alignEl.getAnchorXY('tr', false);
20275 this.arrowEl.setXY(xy);
20278 // continue through...
20279 return this.updatePosition('left', false);
20283 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20284 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20285 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20286 //normal display... or moved up/down.
20287 this.el.setXY(offset);
20288 var xy = this.alignEl.getAnchorXY('tl', false);
20289 xy[0]-=10;xy[1]+=5; // << fix me
20290 this.arrowEl.setXY(xy);
20294 return this.updatePosition('right', false);
20297 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20298 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20299 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20300 //normal display... or moved up/down.
20301 this.el.setXY(offset);
20302 var xy = this.alignEl.getAnchorXY('t', false);
20303 xy[1]-=10; // << fix me
20304 this.arrowEl.setXY(xy);
20308 return this.updatePosition('bottom', false);
20311 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20312 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20313 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20314 //normal display... or moved up/down.
20315 this.el.setXY(offset);
20316 var xy = this.alignEl.getAnchorXY('b', false);
20317 xy[1]+=2; // << fix me
20318 this.arrowEl.setXY(xy);
20322 return this.updatePosition('top', false);
20333 this.el.setXY([0,0]);
20334 this.el.removeClass('in');
20336 this.hoverState = null;
20337 this.maskEl.hide(); // always..
20338 this.fireEvent('hide', this);
20344 Roo.apply(Roo.bootstrap.Popover, {
20347 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20348 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20349 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20350 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20355 clickHander : false,
20358 onMouseDown : function(e)
20360 if (!e.getTarget(".roo-popover")) {
20368 register : function(popup)
20370 if (!Roo.bootstrap.Popover.clickHandler) {
20371 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20373 // hide other popups.
20375 this.popups.push(popup);
20377 hideAll : function()
20379 this.popups.forEach(function(p) {
20387 * Card header - holder for the card header elements.
20392 * @class Roo.bootstrap.PopoverNav
20393 * @extends Roo.bootstrap.NavGroup
20394 * Bootstrap Popover header navigation class
20396 * Create a new Popover Header Navigation
20397 * @param {Object} config The config object
20400 Roo.bootstrap.PopoverNav = function(config){
20401 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20404 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20407 container_method : 'getPopoverHeader'
20425 * @class Roo.bootstrap.Progress
20426 * @extends Roo.bootstrap.Component
20427 * Bootstrap Progress class
20428 * @cfg {Boolean} striped striped of the progress bar
20429 * @cfg {Boolean} active animated of the progress bar
20433 * Create a new Progress
20434 * @param {Object} config The config object
20437 Roo.bootstrap.Progress = function(config){
20438 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20441 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20446 getAutoCreate : function(){
20454 cfg.cls += ' progress-striped';
20458 cfg.cls += ' active';
20477 * @class Roo.bootstrap.ProgressBar
20478 * @extends Roo.bootstrap.Component
20479 * Bootstrap ProgressBar class
20480 * @cfg {Number} aria_valuenow aria-value now
20481 * @cfg {Number} aria_valuemin aria-value min
20482 * @cfg {Number} aria_valuemax aria-value max
20483 * @cfg {String} label label for the progress bar
20484 * @cfg {String} panel (success | info | warning | danger )
20485 * @cfg {String} role role of the progress bar
20486 * @cfg {String} sr_only text
20490 * Create a new ProgressBar
20491 * @param {Object} config The config object
20494 Roo.bootstrap.ProgressBar = function(config){
20495 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20498 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20502 aria_valuemax : 100,
20508 getAutoCreate : function()
20513 cls: 'progress-bar',
20514 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20526 cfg.role = this.role;
20529 if(this.aria_valuenow){
20530 cfg['aria-valuenow'] = this.aria_valuenow;
20533 if(this.aria_valuemin){
20534 cfg['aria-valuemin'] = this.aria_valuemin;
20537 if(this.aria_valuemax){
20538 cfg['aria-valuemax'] = this.aria_valuemax;
20541 if(this.label && !this.sr_only){
20542 cfg.html = this.label;
20546 cfg.cls += ' progress-bar-' + this.panel;
20552 update : function(aria_valuenow)
20554 this.aria_valuenow = aria_valuenow;
20556 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20571 * @class Roo.bootstrap.TabGroup
20572 * @extends Roo.bootstrap.Column
20573 * Bootstrap Column class
20574 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20575 * @cfg {Boolean} carousel true to make the group behave like a carousel
20576 * @cfg {Boolean} bullets show bullets for the panels
20577 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20578 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20579 * @cfg {Boolean} showarrow (true|false) show arrow default true
20582 * Create a new TabGroup
20583 * @param {Object} config The config object
20586 Roo.bootstrap.TabGroup = function(config){
20587 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20589 this.navId = Roo.id();
20592 Roo.bootstrap.TabGroup.register(this);
20596 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20599 transition : false,
20604 slideOnTouch : false,
20607 getAutoCreate : function()
20609 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20611 cfg.cls += ' tab-content';
20613 if (this.carousel) {
20614 cfg.cls += ' carousel slide';
20617 cls : 'carousel-inner',
20621 if(this.bullets && !Roo.isTouch){
20624 cls : 'carousel-bullets',
20628 if(this.bullets_cls){
20629 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20636 cfg.cn[0].cn.push(bullets);
20639 if(this.showarrow){
20640 cfg.cn[0].cn.push({
20642 class : 'carousel-arrow',
20646 class : 'carousel-prev',
20650 class : 'fa fa-chevron-left'
20656 class : 'carousel-next',
20660 class : 'fa fa-chevron-right'
20673 initEvents: function()
20675 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20676 // this.el.on("touchstart", this.onTouchStart, this);
20679 if(this.autoslide){
20682 this.slideFn = window.setInterval(function() {
20683 _this.showPanelNext();
20687 if(this.showarrow){
20688 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20689 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20695 // onTouchStart : function(e, el, o)
20697 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20701 // this.showPanelNext();
20705 getChildContainer : function()
20707 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20711 * register a Navigation item
20712 * @param {Roo.bootstrap.NavItem} the navitem to add
20714 register : function(item)
20716 this.tabs.push( item);
20717 item.navId = this.navId; // not really needed..
20722 getActivePanel : function()
20725 Roo.each(this.tabs, function(t) {
20735 getPanelByName : function(n)
20738 Roo.each(this.tabs, function(t) {
20739 if (t.tabId == n) {
20747 indexOfPanel : function(p)
20750 Roo.each(this.tabs, function(t,i) {
20751 if (t.tabId == p.tabId) {
20760 * show a specific panel
20761 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20762 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20764 showPanel : function (pan)
20766 if(this.transition || typeof(pan) == 'undefined'){
20767 Roo.log("waiting for the transitionend");
20771 if (typeof(pan) == 'number') {
20772 pan = this.tabs[pan];
20775 if (typeof(pan) == 'string') {
20776 pan = this.getPanelByName(pan);
20779 var cur = this.getActivePanel();
20782 Roo.log('pan or acitve pan is undefined');
20786 if (pan.tabId == this.getActivePanel().tabId) {
20790 if (false === cur.fireEvent('beforedeactivate')) {
20794 if(this.bullets > 0 && !Roo.isTouch){
20795 this.setActiveBullet(this.indexOfPanel(pan));
20798 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20800 //class="carousel-item carousel-item-next carousel-item-left"
20802 this.transition = true;
20803 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20804 var lr = dir == 'next' ? 'left' : 'right';
20805 pan.el.addClass(dir); // or prev
20806 pan.el.addClass('carousel-item-' + dir); // or prev
20807 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20808 cur.el.addClass(lr); // or right
20809 pan.el.addClass(lr);
20810 cur.el.addClass('carousel-item-' +lr); // or right
20811 pan.el.addClass('carousel-item-' +lr);
20815 cur.el.on('transitionend', function() {
20816 Roo.log("trans end?");
20818 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20819 pan.setActive(true);
20821 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20822 cur.setActive(false);
20824 _this.transition = false;
20826 }, this, { single: true } );
20831 cur.setActive(false);
20832 pan.setActive(true);
20837 showPanelNext : function()
20839 var i = this.indexOfPanel(this.getActivePanel());
20841 if (i >= this.tabs.length - 1 && !this.autoslide) {
20845 if (i >= this.tabs.length - 1 && this.autoslide) {
20849 this.showPanel(this.tabs[i+1]);
20852 showPanelPrev : function()
20854 var i = this.indexOfPanel(this.getActivePanel());
20856 if (i < 1 && !this.autoslide) {
20860 if (i < 1 && this.autoslide) {
20861 i = this.tabs.length;
20864 this.showPanel(this.tabs[i-1]);
20868 addBullet: function()
20870 if(!this.bullets || Roo.isTouch){
20873 var ctr = this.el.select('.carousel-bullets',true).first();
20874 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20875 var bullet = ctr.createChild({
20876 cls : 'bullet bullet-' + i
20877 },ctr.dom.lastChild);
20882 bullet.on('click', (function(e, el, o, ii, t){
20884 e.preventDefault();
20886 this.showPanel(ii);
20888 if(this.autoslide && this.slideFn){
20889 clearInterval(this.slideFn);
20890 this.slideFn = window.setInterval(function() {
20891 _this.showPanelNext();
20895 }).createDelegate(this, [i, bullet], true));
20900 setActiveBullet : function(i)
20906 Roo.each(this.el.select('.bullet', true).elements, function(el){
20907 el.removeClass('selected');
20910 var bullet = this.el.select('.bullet-' + i, true).first();
20916 bullet.addClass('selected');
20927 Roo.apply(Roo.bootstrap.TabGroup, {
20931 * register a Navigation Group
20932 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20934 register : function(navgrp)
20936 this.groups[navgrp.navId] = navgrp;
20940 * fetch a Navigation Group based on the navigation ID
20941 * if one does not exist , it will get created.
20942 * @param {string} the navgroup to add
20943 * @returns {Roo.bootstrap.NavGroup} the navgroup
20945 get: function(navId) {
20946 if (typeof(this.groups[navId]) == 'undefined') {
20947 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20949 return this.groups[navId] ;
20964 * @class Roo.bootstrap.TabPanel
20965 * @extends Roo.bootstrap.Component
20966 * Bootstrap TabPanel class
20967 * @cfg {Boolean} active panel active
20968 * @cfg {String} html panel content
20969 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20970 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20971 * @cfg {String} href click to link..
20972 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20976 * Create a new TabPanel
20977 * @param {Object} config The config object
20980 Roo.bootstrap.TabPanel = function(config){
20981 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20985 * Fires when the active status changes
20986 * @param {Roo.bootstrap.TabPanel} this
20987 * @param {Boolean} state the new state
20992 * @event beforedeactivate
20993 * Fires before a tab is de-activated - can be used to do validation on a form.
20994 * @param {Roo.bootstrap.TabPanel} this
20995 * @return {Boolean} false if there is an error
20998 'beforedeactivate': true
21001 this.tabId = this.tabId || Roo.id();
21005 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21012 touchSlide : false,
21013 getAutoCreate : function(){
21018 // item is needed for carousel - not sure if it has any effect otherwise
21019 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21020 html: this.html || ''
21024 cfg.cls += ' active';
21028 cfg.tabId = this.tabId;
21036 initEvents: function()
21038 var p = this.parent();
21040 this.navId = this.navId || p.navId;
21042 if (typeof(this.navId) != 'undefined') {
21043 // not really needed.. but just in case.. parent should be a NavGroup.
21044 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21048 var i = tg.tabs.length - 1;
21050 if(this.active && tg.bullets > 0 && i < tg.bullets){
21051 tg.setActiveBullet(i);
21055 this.el.on('click', this.onClick, this);
21057 if(Roo.isTouch && this.touchSlide){
21058 this.el.on("touchstart", this.onTouchStart, this);
21059 this.el.on("touchmove", this.onTouchMove, this);
21060 this.el.on("touchend", this.onTouchEnd, this);
21065 onRender : function(ct, position)
21067 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21070 setActive : function(state)
21072 Roo.log("panel - set active " + this.tabId + "=" + state);
21074 this.active = state;
21076 this.el.removeClass('active');
21078 } else if (!this.el.hasClass('active')) {
21079 this.el.addClass('active');
21082 this.fireEvent('changed', this, state);
21085 onClick : function(e)
21087 e.preventDefault();
21089 if(!this.href.length){
21093 window.location.href = this.href;
21102 onTouchStart : function(e)
21104 this.swiping = false;
21106 this.startX = e.browserEvent.touches[0].clientX;
21107 this.startY = e.browserEvent.touches[0].clientY;
21110 onTouchMove : function(e)
21112 this.swiping = true;
21114 this.endX = e.browserEvent.touches[0].clientX;
21115 this.endY = e.browserEvent.touches[0].clientY;
21118 onTouchEnd : function(e)
21125 var tabGroup = this.parent();
21127 if(this.endX > this.startX){ // swiping right
21128 tabGroup.showPanelPrev();
21132 if(this.startX > this.endX){ // swiping left
21133 tabGroup.showPanelNext();
21152 * @class Roo.bootstrap.DateField
21153 * @extends Roo.bootstrap.Input
21154 * Bootstrap DateField class
21155 * @cfg {Number} weekStart default 0
21156 * @cfg {String} viewMode default empty, (months|years)
21157 * @cfg {String} minViewMode default empty, (months|years)
21158 * @cfg {Number} startDate default -Infinity
21159 * @cfg {Number} endDate default Infinity
21160 * @cfg {Boolean} todayHighlight default false
21161 * @cfg {Boolean} todayBtn default false
21162 * @cfg {Boolean} calendarWeeks default false
21163 * @cfg {Object} daysOfWeekDisabled default empty
21164 * @cfg {Boolean} singleMode default false (true | false)
21166 * @cfg {Boolean} keyboardNavigation default true
21167 * @cfg {String} language default en
21170 * Create a new DateField
21171 * @param {Object} config The config object
21174 Roo.bootstrap.DateField = function(config){
21175 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21179 * Fires when this field show.
21180 * @param {Roo.bootstrap.DateField} this
21181 * @param {Mixed} date The date value
21186 * Fires when this field hide.
21187 * @param {Roo.bootstrap.DateField} this
21188 * @param {Mixed} date The date value
21193 * Fires when select a date.
21194 * @param {Roo.bootstrap.DateField} this
21195 * @param {Mixed} date The date value
21199 * @event beforeselect
21200 * Fires when before select a date.
21201 * @param {Roo.bootstrap.DateField} this
21202 * @param {Mixed} date The date value
21204 beforeselect : true
21208 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21211 * @cfg {String} format
21212 * The default date format string which can be overriden for localization support. The format must be
21213 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21217 * @cfg {String} altFormats
21218 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21219 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21221 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21229 todayHighlight : false,
21235 keyboardNavigation: true,
21237 calendarWeeks: false,
21239 startDate: -Infinity,
21243 daysOfWeekDisabled: [],
21247 singleMode : false,
21249 UTCDate: function()
21251 return new Date(Date.UTC.apply(Date, arguments));
21254 UTCToday: function()
21256 var today = new Date();
21257 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21260 getDate: function() {
21261 var d = this.getUTCDate();
21262 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21265 getUTCDate: function() {
21269 setDate: function(d) {
21270 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21273 setUTCDate: function(d) {
21275 this.setValue(this.formatDate(this.date));
21278 onRender: function(ct, position)
21281 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21283 this.language = this.language || 'en';
21284 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21285 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21287 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21288 this.format = this.format || 'm/d/y';
21289 this.isInline = false;
21290 this.isInput = true;
21291 this.component = this.el.select('.add-on', true).first() || false;
21292 this.component = (this.component && this.component.length === 0) ? false : this.component;
21293 this.hasInput = this.component && this.inputEl().length;
21295 if (typeof(this.minViewMode === 'string')) {
21296 switch (this.minViewMode) {
21298 this.minViewMode = 1;
21301 this.minViewMode = 2;
21304 this.minViewMode = 0;
21309 if (typeof(this.viewMode === 'string')) {
21310 switch (this.viewMode) {
21323 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21325 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21327 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21329 this.picker().on('mousedown', this.onMousedown, this);
21330 this.picker().on('click', this.onClick, this);
21332 this.picker().addClass('datepicker-dropdown');
21334 this.startViewMode = this.viewMode;
21336 if(this.singleMode){
21337 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21338 v.setVisibilityMode(Roo.Element.DISPLAY);
21342 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21343 v.setStyle('width', '189px');
21347 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21348 if(!this.calendarWeeks){
21353 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21354 v.attr('colspan', function(i, val){
21355 return parseInt(val) + 1;
21360 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21362 this.setStartDate(this.startDate);
21363 this.setEndDate(this.endDate);
21365 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21372 if(this.isInline) {
21377 picker : function()
21379 return this.pickerEl;
21380 // return this.el.select('.datepicker', true).first();
21383 fillDow: function()
21385 var dowCnt = this.weekStart;
21394 if(this.calendarWeeks){
21402 while (dowCnt < this.weekStart + 7) {
21406 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21410 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21413 fillMonths: function()
21416 var months = this.picker().select('>.datepicker-months td', true).first();
21418 months.dom.innerHTML = '';
21424 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21427 months.createChild(month);
21434 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;
21436 if (this.date < this.startDate) {
21437 this.viewDate = new Date(this.startDate);
21438 } else if (this.date > this.endDate) {
21439 this.viewDate = new Date(this.endDate);
21441 this.viewDate = new Date(this.date);
21449 var d = new Date(this.viewDate),
21450 year = d.getUTCFullYear(),
21451 month = d.getUTCMonth(),
21452 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21453 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21454 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21455 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21456 currentDate = this.date && this.date.valueOf(),
21457 today = this.UTCToday();
21459 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21461 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21463 // this.picker.select('>tfoot th.today').
21464 // .text(dates[this.language].today)
21465 // .toggle(this.todayBtn !== false);
21467 this.updateNavArrows();
21470 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21472 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21474 prevMonth.setUTCDate(day);
21476 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21478 var nextMonth = new Date(prevMonth);
21480 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21482 nextMonth = nextMonth.valueOf();
21484 var fillMonths = false;
21486 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21488 while(prevMonth.valueOf() <= nextMonth) {
21491 if (prevMonth.getUTCDay() === this.weekStart) {
21493 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21501 if(this.calendarWeeks){
21502 // ISO 8601: First week contains first thursday.
21503 // ISO also states week starts on Monday, but we can be more abstract here.
21505 // Start of current week: based on weekstart/current date
21506 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21507 // Thursday of this week
21508 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21509 // First Thursday of year, year from thursday
21510 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21511 // Calendar week: ms between thursdays, div ms per day, div 7 days
21512 calWeek = (th - yth) / 864e5 / 7 + 1;
21514 fillMonths.cn.push({
21522 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21524 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21527 if (this.todayHighlight &&
21528 prevMonth.getUTCFullYear() == today.getFullYear() &&
21529 prevMonth.getUTCMonth() == today.getMonth() &&
21530 prevMonth.getUTCDate() == today.getDate()) {
21531 clsName += ' today';
21534 if (currentDate && prevMonth.valueOf() === currentDate) {
21535 clsName += ' active';
21538 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21539 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21540 clsName += ' disabled';
21543 fillMonths.cn.push({
21545 cls: 'day ' + clsName,
21546 html: prevMonth.getDate()
21549 prevMonth.setDate(prevMonth.getDate()+1);
21552 var currentYear = this.date && this.date.getUTCFullYear();
21553 var currentMonth = this.date && this.date.getUTCMonth();
21555 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21557 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21558 v.removeClass('active');
21560 if(currentYear === year && k === currentMonth){
21561 v.addClass('active');
21564 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21565 v.addClass('disabled');
21571 year = parseInt(year/10, 10) * 10;
21573 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21575 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21578 for (var i = -1; i < 11; i++) {
21579 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21581 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21589 showMode: function(dir)
21592 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21595 Roo.each(this.picker().select('>div',true).elements, function(v){
21596 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21599 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21604 if(this.isInline) {
21608 this.picker().removeClass(['bottom', 'top']);
21610 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21612 * place to the top of element!
21616 this.picker().addClass('top');
21617 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21622 this.picker().addClass('bottom');
21624 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21627 parseDate : function(value)
21629 if(!value || value instanceof Date){
21632 var v = Date.parseDate(value, this.format);
21633 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21634 v = Date.parseDate(value, 'Y-m-d');
21636 if(!v && this.altFormats){
21637 if(!this.altFormatsArray){
21638 this.altFormatsArray = this.altFormats.split("|");
21640 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21641 v = Date.parseDate(value, this.altFormatsArray[i]);
21647 formatDate : function(date, fmt)
21649 return (!date || !(date instanceof Date)) ?
21650 date : date.dateFormat(fmt || this.format);
21653 onFocus : function()
21655 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21659 onBlur : function()
21661 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21663 var d = this.inputEl().getValue();
21670 showPopup : function()
21672 this.picker().show();
21676 this.fireEvent('showpopup', this, this.date);
21679 hidePopup : function()
21681 if(this.isInline) {
21684 this.picker().hide();
21685 this.viewMode = this.startViewMode;
21688 this.fireEvent('hidepopup', this, this.date);
21692 onMousedown: function(e)
21694 e.stopPropagation();
21695 e.preventDefault();
21700 Roo.bootstrap.DateField.superclass.keyup.call(this);
21704 setValue: function(v)
21706 if(this.fireEvent('beforeselect', this, v) !== false){
21707 var d = new Date(this.parseDate(v) ).clearTime();
21709 if(isNaN(d.getTime())){
21710 this.date = this.viewDate = '';
21711 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21715 v = this.formatDate(d);
21717 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21719 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21723 this.fireEvent('select', this, this.date);
21727 getValue: function()
21729 return this.formatDate(this.date);
21732 fireKey: function(e)
21734 if (!this.picker().isVisible()){
21735 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21741 var dateChanged = false,
21743 newDate, newViewDate;
21748 e.preventDefault();
21752 if (!this.keyboardNavigation) {
21755 dir = e.keyCode == 37 ? -1 : 1;
21758 newDate = this.moveYear(this.date, dir);
21759 newViewDate = this.moveYear(this.viewDate, dir);
21760 } else if (e.shiftKey){
21761 newDate = this.moveMonth(this.date, dir);
21762 newViewDate = this.moveMonth(this.viewDate, dir);
21764 newDate = new Date(this.date);
21765 newDate.setUTCDate(this.date.getUTCDate() + dir);
21766 newViewDate = new Date(this.viewDate);
21767 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21769 if (this.dateWithinRange(newDate)){
21770 this.date = newDate;
21771 this.viewDate = newViewDate;
21772 this.setValue(this.formatDate(this.date));
21774 e.preventDefault();
21775 dateChanged = true;
21780 if (!this.keyboardNavigation) {
21783 dir = e.keyCode == 38 ? -1 : 1;
21785 newDate = this.moveYear(this.date, dir);
21786 newViewDate = this.moveYear(this.viewDate, dir);
21787 } else if (e.shiftKey){
21788 newDate = this.moveMonth(this.date, dir);
21789 newViewDate = this.moveMonth(this.viewDate, dir);
21791 newDate = new Date(this.date);
21792 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21793 newViewDate = new Date(this.viewDate);
21794 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21796 if (this.dateWithinRange(newDate)){
21797 this.date = newDate;
21798 this.viewDate = newViewDate;
21799 this.setValue(this.formatDate(this.date));
21801 e.preventDefault();
21802 dateChanged = true;
21806 this.setValue(this.formatDate(this.date));
21808 e.preventDefault();
21811 this.setValue(this.formatDate(this.date));
21825 onClick: function(e)
21827 e.stopPropagation();
21828 e.preventDefault();
21830 var target = e.getTarget();
21832 if(target.nodeName.toLowerCase() === 'i'){
21833 target = Roo.get(target).dom.parentNode;
21836 var nodeName = target.nodeName;
21837 var className = target.className;
21838 var html = target.innerHTML;
21839 //Roo.log(nodeName);
21841 switch(nodeName.toLowerCase()) {
21843 switch(className) {
21849 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21850 switch(this.viewMode){
21852 this.viewDate = this.moveMonth(this.viewDate, dir);
21856 this.viewDate = this.moveYear(this.viewDate, dir);
21862 var date = new Date();
21863 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21865 this.setValue(this.formatDate(this.date));
21872 if (className.indexOf('disabled') < 0) {
21873 this.viewDate.setUTCDate(1);
21874 if (className.indexOf('month') > -1) {
21875 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21877 var year = parseInt(html, 10) || 0;
21878 this.viewDate.setUTCFullYear(year);
21882 if(this.singleMode){
21883 this.setValue(this.formatDate(this.viewDate));
21894 //Roo.log(className);
21895 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21896 var day = parseInt(html, 10) || 1;
21897 var year = (this.viewDate || new Date()).getUTCFullYear(),
21898 month = (this.viewDate || new Date()).getUTCMonth();
21900 if (className.indexOf('old') > -1) {
21907 } else if (className.indexOf('new') > -1) {
21915 //Roo.log([year,month,day]);
21916 this.date = this.UTCDate(year, month, day,0,0,0,0);
21917 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21919 //Roo.log(this.formatDate(this.date));
21920 this.setValue(this.formatDate(this.date));
21927 setStartDate: function(startDate)
21929 this.startDate = startDate || -Infinity;
21930 if (this.startDate !== -Infinity) {
21931 this.startDate = this.parseDate(this.startDate);
21934 this.updateNavArrows();
21937 setEndDate: function(endDate)
21939 this.endDate = endDate || Infinity;
21940 if (this.endDate !== Infinity) {
21941 this.endDate = this.parseDate(this.endDate);
21944 this.updateNavArrows();
21947 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21949 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21950 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21951 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21953 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21954 return parseInt(d, 10);
21957 this.updateNavArrows();
21960 updateNavArrows: function()
21962 if(this.singleMode){
21966 var d = new Date(this.viewDate),
21967 year = d.getUTCFullYear(),
21968 month = d.getUTCMonth();
21970 Roo.each(this.picker().select('.prev', true).elements, function(v){
21972 switch (this.viewMode) {
21975 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21981 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21988 Roo.each(this.picker().select('.next', true).elements, function(v){
21990 switch (this.viewMode) {
21993 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21999 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22007 moveMonth: function(date, dir)
22012 var new_date = new Date(date.valueOf()),
22013 day = new_date.getUTCDate(),
22014 month = new_date.getUTCMonth(),
22015 mag = Math.abs(dir),
22017 dir = dir > 0 ? 1 : -1;
22020 // If going back one month, make sure month is not current month
22021 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22023 return new_date.getUTCMonth() == month;
22025 // If going forward one month, make sure month is as expected
22026 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22028 return new_date.getUTCMonth() != new_month;
22030 new_month = month + dir;
22031 new_date.setUTCMonth(new_month);
22032 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22033 if (new_month < 0 || new_month > 11) {
22034 new_month = (new_month + 12) % 12;
22037 // For magnitudes >1, move one month at a time...
22038 for (var i=0; i<mag; i++) {
22039 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22040 new_date = this.moveMonth(new_date, dir);
22042 // ...then reset the day, keeping it in the new month
22043 new_month = new_date.getUTCMonth();
22044 new_date.setUTCDate(day);
22046 return new_month != new_date.getUTCMonth();
22049 // Common date-resetting loop -- if date is beyond end of month, make it
22052 new_date.setUTCDate(--day);
22053 new_date.setUTCMonth(new_month);
22058 moveYear: function(date, dir)
22060 return this.moveMonth(date, dir*12);
22063 dateWithinRange: function(date)
22065 return date >= this.startDate && date <= this.endDate;
22071 this.picker().remove();
22074 validateValue : function(value)
22076 if(this.getVisibilityEl().hasClass('hidden')){
22080 if(value.length < 1) {
22081 if(this.allowBlank){
22087 if(value.length < this.minLength){
22090 if(value.length > this.maxLength){
22094 var vt = Roo.form.VTypes;
22095 if(!vt[this.vtype](value, this)){
22099 if(typeof this.validator == "function"){
22100 var msg = this.validator(value);
22106 if(this.regex && !this.regex.test(value)){
22110 if(typeof(this.parseDate(value)) == 'undefined'){
22114 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22118 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22128 this.date = this.viewDate = '';
22130 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22135 Roo.apply(Roo.bootstrap.DateField, {
22146 html: '<i class="fa fa-arrow-left"/>'
22156 html: '<i class="fa fa-arrow-right"/>'
22198 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22199 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22200 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22201 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22202 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22215 navFnc: 'FullYear',
22220 navFnc: 'FullYear',
22225 Roo.apply(Roo.bootstrap.DateField, {
22229 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22233 cls: 'datepicker-days',
22237 cls: 'table-condensed',
22239 Roo.bootstrap.DateField.head,
22243 Roo.bootstrap.DateField.footer
22250 cls: 'datepicker-months',
22254 cls: 'table-condensed',
22256 Roo.bootstrap.DateField.head,
22257 Roo.bootstrap.DateField.content,
22258 Roo.bootstrap.DateField.footer
22265 cls: 'datepicker-years',
22269 cls: 'table-condensed',
22271 Roo.bootstrap.DateField.head,
22272 Roo.bootstrap.DateField.content,
22273 Roo.bootstrap.DateField.footer
22292 * @class Roo.bootstrap.TimeField
22293 * @extends Roo.bootstrap.Input
22294 * Bootstrap DateField class
22298 * Create a new TimeField
22299 * @param {Object} config The config object
22302 Roo.bootstrap.TimeField = function(config){
22303 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22307 * Fires when this field show.
22308 * @param {Roo.bootstrap.DateField} thisthis
22309 * @param {Mixed} date The date value
22314 * Fires when this field hide.
22315 * @param {Roo.bootstrap.DateField} this
22316 * @param {Mixed} date The date value
22321 * Fires when select a date.
22322 * @param {Roo.bootstrap.DateField} this
22323 * @param {Mixed} date The date value
22329 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22332 * @cfg {String} format
22333 * The default time format string which can be overriden for localization support. The format must be
22334 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22338 getAutoCreate : function()
22340 this.after = '<i class="fa far fa-clock"></i>';
22341 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22345 onRender: function(ct, position)
22348 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22350 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22352 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22354 this.pop = this.picker().select('>.datepicker-time',true).first();
22355 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22357 this.picker().on('mousedown', this.onMousedown, this);
22358 this.picker().on('click', this.onClick, this);
22360 this.picker().addClass('datepicker-dropdown');
22365 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22366 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22367 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22368 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22369 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22370 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22374 fireKey: function(e){
22375 if (!this.picker().isVisible()){
22376 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22382 e.preventDefault();
22390 this.onTogglePeriod();
22393 this.onIncrementMinutes();
22396 this.onDecrementMinutes();
22405 onClick: function(e) {
22406 e.stopPropagation();
22407 e.preventDefault();
22410 picker : function()
22412 return this.pickerEl;
22415 fillTime: function()
22417 var time = this.pop.select('tbody', true).first();
22419 time.dom.innerHTML = '';
22434 cls: 'hours-up fa fas fa-chevron-up'
22454 cls: 'minutes-up fa fas fa-chevron-up'
22475 cls: 'timepicker-hour',
22490 cls: 'timepicker-minute',
22505 cls: 'btn btn-primary period',
22527 cls: 'hours-down fa fas fa-chevron-down'
22547 cls: 'minutes-down fa fas fa-chevron-down'
22565 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22572 var hours = this.time.getHours();
22573 var minutes = this.time.getMinutes();
22586 hours = hours - 12;
22590 hours = '0' + hours;
22594 minutes = '0' + minutes;
22597 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22598 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22599 this.pop.select('button', true).first().dom.innerHTML = period;
22605 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22607 var cls = ['bottom'];
22609 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22616 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22620 //this.picker().setXY(20000,20000);
22621 this.picker().addClass(cls.join('-'));
22625 Roo.each(cls, function(c){
22630 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22631 //_this.picker().setTop(_this.inputEl().getHeight());
22635 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22637 //_this.picker().setTop(0 - _this.picker().getHeight());
22642 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22646 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22654 onFocus : function()
22656 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22660 onBlur : function()
22662 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22668 this.picker().show();
22673 this.fireEvent('show', this, this.date);
22678 this.picker().hide();
22681 this.fireEvent('hide', this, this.date);
22684 setTime : function()
22687 this.setValue(this.time.format(this.format));
22689 this.fireEvent('select', this, this.date);
22694 onMousedown: function(e){
22695 e.stopPropagation();
22696 e.preventDefault();
22699 onIncrementHours: function()
22701 Roo.log('onIncrementHours');
22702 this.time = this.time.add(Date.HOUR, 1);
22707 onDecrementHours: function()
22709 Roo.log('onDecrementHours');
22710 this.time = this.time.add(Date.HOUR, -1);
22714 onIncrementMinutes: function()
22716 Roo.log('onIncrementMinutes');
22717 this.time = this.time.add(Date.MINUTE, 1);
22721 onDecrementMinutes: function()
22723 Roo.log('onDecrementMinutes');
22724 this.time = this.time.add(Date.MINUTE, -1);
22728 onTogglePeriod: function()
22730 Roo.log('onTogglePeriod');
22731 this.time = this.time.add(Date.HOUR, 12);
22739 Roo.apply(Roo.bootstrap.TimeField, {
22743 cls: 'datepicker dropdown-menu',
22747 cls: 'datepicker-time',
22751 cls: 'table-condensed',
22780 cls: 'btn btn-info ok',
22808 * @class Roo.bootstrap.MonthField
22809 * @extends Roo.bootstrap.Input
22810 * Bootstrap MonthField class
22812 * @cfg {String} language default en
22815 * Create a new MonthField
22816 * @param {Object} config The config object
22819 Roo.bootstrap.MonthField = function(config){
22820 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22825 * Fires when this field show.
22826 * @param {Roo.bootstrap.MonthField} this
22827 * @param {Mixed} date The date value
22832 * Fires when this field hide.
22833 * @param {Roo.bootstrap.MonthField} this
22834 * @param {Mixed} date The date value
22839 * Fires when select a date.
22840 * @param {Roo.bootstrap.MonthField} this
22841 * @param {String} oldvalue The old value
22842 * @param {String} newvalue The new value
22848 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22850 onRender: function(ct, position)
22853 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22855 this.language = this.language || 'en';
22856 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22857 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22859 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22860 this.isInline = false;
22861 this.isInput = true;
22862 this.component = this.el.select('.add-on', true).first() || false;
22863 this.component = (this.component && this.component.length === 0) ? false : this.component;
22864 this.hasInput = this.component && this.inputEL().length;
22866 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22868 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22870 this.picker().on('mousedown', this.onMousedown, this);
22871 this.picker().on('click', this.onClick, this);
22873 this.picker().addClass('datepicker-dropdown');
22875 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22876 v.setStyle('width', '189px');
22883 if(this.isInline) {
22889 setValue: function(v, suppressEvent)
22891 var o = this.getValue();
22893 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22897 if(suppressEvent !== true){
22898 this.fireEvent('select', this, o, v);
22903 getValue: function()
22908 onClick: function(e)
22910 e.stopPropagation();
22911 e.preventDefault();
22913 var target = e.getTarget();
22915 if(target.nodeName.toLowerCase() === 'i'){
22916 target = Roo.get(target).dom.parentNode;
22919 var nodeName = target.nodeName;
22920 var className = target.className;
22921 var html = target.innerHTML;
22923 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22927 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22929 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22935 picker : function()
22937 return this.pickerEl;
22940 fillMonths: function()
22943 var months = this.picker().select('>.datepicker-months td', true).first();
22945 months.dom.innerHTML = '';
22951 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22954 months.createChild(month);
22963 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22964 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22967 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22968 e.removeClass('active');
22970 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22971 e.addClass('active');
22978 if(this.isInline) {
22982 this.picker().removeClass(['bottom', 'top']);
22984 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22986 * place to the top of element!
22990 this.picker().addClass('top');
22991 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22996 this.picker().addClass('bottom');
22998 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23001 onFocus : function()
23003 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23007 onBlur : function()
23009 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23011 var d = this.inputEl().getValue();
23020 this.picker().show();
23021 this.picker().select('>.datepicker-months', true).first().show();
23025 this.fireEvent('show', this, this.date);
23030 if(this.isInline) {
23033 this.picker().hide();
23034 this.fireEvent('hide', this, this.date);
23038 onMousedown: function(e)
23040 e.stopPropagation();
23041 e.preventDefault();
23046 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23050 fireKey: function(e)
23052 if (!this.picker().isVisible()){
23053 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23064 e.preventDefault();
23068 dir = e.keyCode == 37 ? -1 : 1;
23070 this.vIndex = this.vIndex + dir;
23072 if(this.vIndex < 0){
23076 if(this.vIndex > 11){
23080 if(isNaN(this.vIndex)){
23084 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23090 dir = e.keyCode == 38 ? -1 : 1;
23092 this.vIndex = this.vIndex + dir * 4;
23094 if(this.vIndex < 0){
23098 if(this.vIndex > 11){
23102 if(isNaN(this.vIndex)){
23106 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23111 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23112 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23116 e.preventDefault();
23119 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23120 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23136 this.picker().remove();
23141 Roo.apply(Roo.bootstrap.MonthField, {
23160 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23161 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23166 Roo.apply(Roo.bootstrap.MonthField, {
23170 cls: 'datepicker dropdown-menu roo-dynamic',
23174 cls: 'datepicker-months',
23178 cls: 'table-condensed',
23180 Roo.bootstrap.DateField.content
23200 * @class Roo.bootstrap.CheckBox
23201 * @extends Roo.bootstrap.Input
23202 * Bootstrap CheckBox class
23204 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23205 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23206 * @cfg {String} boxLabel The text that appears beside the checkbox
23207 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23208 * @cfg {Boolean} checked initnal the element
23209 * @cfg {Boolean} inline inline the element (default false)
23210 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23211 * @cfg {String} tooltip label tooltip
23214 * Create a new CheckBox
23215 * @param {Object} config The config object
23218 Roo.bootstrap.CheckBox = function(config){
23219 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23224 * Fires when the element is checked or unchecked.
23225 * @param {Roo.bootstrap.CheckBox} this This input
23226 * @param {Boolean} checked The new checked value
23231 * Fires when the element is click.
23232 * @param {Roo.bootstrap.CheckBox} this This input
23239 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23241 inputType: 'checkbox',
23250 // checkbox success does not make any sense really..
23255 getAutoCreate : function()
23257 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23263 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23266 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23272 type : this.inputType,
23273 value : this.inputValue,
23274 cls : 'roo-' + this.inputType, //'form-box',
23275 placeholder : this.placeholder || ''
23279 if(this.inputType != 'radio'){
23283 cls : 'roo-hidden-value',
23284 value : this.checked ? this.inputValue : this.valueOff
23289 if (this.weight) { // Validity check?
23290 cfg.cls += " " + this.inputType + "-" + this.weight;
23293 if (this.disabled) {
23294 input.disabled=true;
23298 input.checked = this.checked;
23303 input.name = this.name;
23305 if(this.inputType != 'radio'){
23306 hidden.name = this.name;
23307 input.name = '_hidden_' + this.name;
23312 input.cls += ' input-' + this.size;
23317 ['xs','sm','md','lg'].map(function(size){
23318 if (settings[size]) {
23319 cfg.cls += ' col-' + size + '-' + settings[size];
23323 var inputblock = input;
23325 if (this.before || this.after) {
23328 cls : 'input-group',
23333 inputblock.cn.push({
23335 cls : 'input-group-addon',
23340 inputblock.cn.push(input);
23342 if(this.inputType != 'radio'){
23343 inputblock.cn.push(hidden);
23347 inputblock.cn.push({
23349 cls : 'input-group-addon',
23355 var boxLabelCfg = false;
23361 //'for': id, // box label is handled by onclick - so no for...
23363 html: this.boxLabel
23366 boxLabelCfg.tooltip = this.tooltip;
23372 if (align ==='left' && this.fieldLabel.length) {
23373 // Roo.log("left and has label");
23378 cls : 'control-label',
23379 html : this.fieldLabel
23390 cfg.cn[1].cn.push(boxLabelCfg);
23393 if(this.labelWidth > 12){
23394 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23397 if(this.labelWidth < 13 && this.labelmd == 0){
23398 this.labelmd = this.labelWidth;
23401 if(this.labellg > 0){
23402 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23403 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23406 if(this.labelmd > 0){
23407 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23408 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23411 if(this.labelsm > 0){
23412 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23413 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23416 if(this.labelxs > 0){
23417 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23418 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23421 } else if ( this.fieldLabel.length) {
23422 // Roo.log(" label");
23426 tag: this.boxLabel ? 'span' : 'label',
23428 cls: 'control-label box-input-label',
23429 //cls : 'input-group-addon',
23430 html : this.fieldLabel
23437 cfg.cn.push(boxLabelCfg);
23442 // Roo.log(" no label && no align");
23443 cfg.cn = [ inputblock ] ;
23445 cfg.cn.push(boxLabelCfg);
23453 if(this.inputType != 'radio'){
23454 cfg.cn.push(hidden);
23462 * return the real input element.
23464 inputEl: function ()
23466 return this.el.select('input.roo-' + this.inputType,true).first();
23468 hiddenEl: function ()
23470 return this.el.select('input.roo-hidden-value',true).first();
23473 labelEl: function()
23475 return this.el.select('label.control-label',true).first();
23477 /* depricated... */
23481 return this.labelEl();
23484 boxLabelEl: function()
23486 return this.el.select('label.box-label',true).first();
23489 initEvents : function()
23491 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23493 this.inputEl().on('click', this.onClick, this);
23495 if (this.boxLabel) {
23496 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23499 this.startValue = this.getValue();
23502 Roo.bootstrap.CheckBox.register(this);
23506 onClick : function(e)
23508 if(this.fireEvent('click', this, e) !== false){
23509 this.setChecked(!this.checked);
23514 setChecked : function(state,suppressEvent)
23516 this.startValue = this.getValue();
23518 if(this.inputType == 'radio'){
23520 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23521 e.dom.checked = false;
23524 this.inputEl().dom.checked = true;
23526 this.inputEl().dom.value = this.inputValue;
23528 if(suppressEvent !== true){
23529 this.fireEvent('check', this, true);
23537 this.checked = state;
23539 this.inputEl().dom.checked = state;
23542 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23544 if(suppressEvent !== true){
23545 this.fireEvent('check', this, state);
23551 getValue : function()
23553 if(this.inputType == 'radio'){
23554 return this.getGroupValue();
23557 return this.hiddenEl().dom.value;
23561 getGroupValue : function()
23563 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23567 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23570 setValue : function(v,suppressEvent)
23572 if(this.inputType == 'radio'){
23573 this.setGroupValue(v, suppressEvent);
23577 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23582 setGroupValue : function(v, suppressEvent)
23584 this.startValue = this.getValue();
23586 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23587 e.dom.checked = false;
23589 if(e.dom.value == v){
23590 e.dom.checked = true;
23594 if(suppressEvent !== true){
23595 this.fireEvent('check', this, true);
23603 validate : function()
23605 if(this.getVisibilityEl().hasClass('hidden')){
23611 (this.inputType == 'radio' && this.validateRadio()) ||
23612 (this.inputType == 'checkbox' && this.validateCheckbox())
23618 this.markInvalid();
23622 validateRadio : function()
23624 if(this.getVisibilityEl().hasClass('hidden')){
23628 if(this.allowBlank){
23634 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23635 if(!e.dom.checked){
23647 validateCheckbox : function()
23650 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23651 //return (this.getValue() == this.inputValue) ? true : false;
23654 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23662 for(var i in group){
23663 if(group[i].el.isVisible(true)){
23671 for(var i in group){
23676 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23683 * Mark this field as valid
23685 markValid : function()
23689 this.fireEvent('valid', this);
23691 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23694 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23701 if(this.inputType == 'radio'){
23702 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23703 var fg = e.findParent('.form-group', false, true);
23704 if (Roo.bootstrap.version == 3) {
23705 fg.removeClass([_this.invalidClass, _this.validClass]);
23706 fg.addClass(_this.validClass);
23708 fg.removeClass(['is-valid', 'is-invalid']);
23709 fg.addClass('is-valid');
23717 var fg = this.el.findParent('.form-group', false, true);
23718 if (Roo.bootstrap.version == 3) {
23719 fg.removeClass([this.invalidClass, this.validClass]);
23720 fg.addClass(this.validClass);
23722 fg.removeClass(['is-valid', 'is-invalid']);
23723 fg.addClass('is-valid');
23728 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23734 for(var i in group){
23735 var fg = group[i].el.findParent('.form-group', false, true);
23736 if (Roo.bootstrap.version == 3) {
23737 fg.removeClass([this.invalidClass, this.validClass]);
23738 fg.addClass(this.validClass);
23740 fg.removeClass(['is-valid', 'is-invalid']);
23741 fg.addClass('is-valid');
23747 * Mark this field as invalid
23748 * @param {String} msg The validation message
23750 markInvalid : function(msg)
23752 if(this.allowBlank){
23758 this.fireEvent('invalid', this, msg);
23760 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23763 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23767 label.markInvalid();
23770 if(this.inputType == 'radio'){
23772 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23773 var fg = e.findParent('.form-group', false, true);
23774 if (Roo.bootstrap.version == 3) {
23775 fg.removeClass([_this.invalidClass, _this.validClass]);
23776 fg.addClass(_this.invalidClass);
23778 fg.removeClass(['is-invalid', 'is-valid']);
23779 fg.addClass('is-invalid');
23787 var fg = this.el.findParent('.form-group', false, true);
23788 if (Roo.bootstrap.version == 3) {
23789 fg.removeClass([_this.invalidClass, _this.validClass]);
23790 fg.addClass(_this.invalidClass);
23792 fg.removeClass(['is-invalid', 'is-valid']);
23793 fg.addClass('is-invalid');
23798 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23804 for(var i in group){
23805 var fg = group[i].el.findParent('.form-group', false, true);
23806 if (Roo.bootstrap.version == 3) {
23807 fg.removeClass([_this.invalidClass, _this.validClass]);
23808 fg.addClass(_this.invalidClass);
23810 fg.removeClass(['is-invalid', 'is-valid']);
23811 fg.addClass('is-invalid');
23817 clearInvalid : function()
23819 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23821 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23823 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23825 if (label && label.iconEl) {
23826 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23827 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23831 disable : function()
23833 if(this.inputType != 'radio'){
23834 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23841 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23842 _this.getActionEl().addClass(this.disabledClass);
23843 e.dom.disabled = true;
23847 this.disabled = true;
23848 this.fireEvent("disable", this);
23852 enable : function()
23854 if(this.inputType != 'radio'){
23855 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23862 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23863 _this.getActionEl().removeClass(this.disabledClass);
23864 e.dom.disabled = false;
23868 this.disabled = false;
23869 this.fireEvent("enable", this);
23873 setBoxLabel : function(v)
23878 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23884 Roo.apply(Roo.bootstrap.CheckBox, {
23889 * register a CheckBox Group
23890 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23892 register : function(checkbox)
23894 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23895 this.groups[checkbox.groupId] = {};
23898 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23902 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23906 * fetch a CheckBox Group based on the group ID
23907 * @param {string} the group ID
23908 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23910 get: function(groupId) {
23911 if (typeof(this.groups[groupId]) == 'undefined') {
23915 return this.groups[groupId] ;
23928 * @class Roo.bootstrap.Radio
23929 * @extends Roo.bootstrap.Component
23930 * Bootstrap Radio class
23931 * @cfg {String} boxLabel - the label associated
23932 * @cfg {String} value - the value of radio
23935 * Create a new Radio
23936 * @param {Object} config The config object
23938 Roo.bootstrap.Radio = function(config){
23939 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23943 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23949 getAutoCreate : function()
23953 cls : 'form-group radio',
23958 html : this.boxLabel
23966 initEvents : function()
23968 this.parent().register(this);
23970 this.el.on('click', this.onClick, this);
23974 onClick : function(e)
23976 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23977 this.setChecked(true);
23981 setChecked : function(state, suppressEvent)
23983 this.parent().setValue(this.value, suppressEvent);
23987 setBoxLabel : function(v)
23992 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24007 * @class Roo.bootstrap.SecurePass
24008 * @extends Roo.bootstrap.Input
24009 * Bootstrap SecurePass class
24013 * Create a new SecurePass
24014 * @param {Object} config The config object
24017 Roo.bootstrap.SecurePass = function (config) {
24018 // these go here, so the translation tool can replace them..
24020 PwdEmpty: "Please type a password, and then retype it to confirm.",
24021 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24022 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24023 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24024 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24025 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24026 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24027 TooWeak: "Your password is Too Weak."
24029 this.meterLabel = "Password strength:";
24030 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24031 this.meterClass = [
24032 "roo-password-meter-tooweak",
24033 "roo-password-meter-weak",
24034 "roo-password-meter-medium",
24035 "roo-password-meter-strong",
24036 "roo-password-meter-grey"
24041 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24044 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24046 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24048 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24049 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24050 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24051 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24052 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24053 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24054 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24064 * @cfg {String/Object} Label for the strength meter (defaults to
24065 * 'Password strength:')
24070 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24071 * ['Weak', 'Medium', 'Strong'])
24074 pwdStrengths: false,
24087 initEvents: function ()
24089 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24091 if (this.el.is('input[type=password]') && Roo.isSafari) {
24092 this.el.on('keydown', this.SafariOnKeyDown, this);
24095 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24098 onRender: function (ct, position)
24100 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24101 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24102 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24104 this.trigger.createChild({
24109 cls: 'roo-password-meter-grey col-xs-12',
24112 //width: this.meterWidth + 'px'
24116 cls: 'roo-password-meter-text'
24122 if (this.hideTrigger) {
24123 this.trigger.setDisplayed(false);
24125 this.setSize(this.width || '', this.height || '');
24128 onDestroy: function ()
24130 if (this.trigger) {
24131 this.trigger.removeAllListeners();
24132 this.trigger.remove();
24135 this.wrap.remove();
24137 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24140 checkStrength: function ()
24142 var pwd = this.inputEl().getValue();
24143 if (pwd == this._lastPwd) {
24148 if (this.ClientSideStrongPassword(pwd)) {
24150 } else if (this.ClientSideMediumPassword(pwd)) {
24152 } else if (this.ClientSideWeakPassword(pwd)) {
24158 Roo.log('strength1: ' + strength);
24160 //var pm = this.trigger.child('div/div/div').dom;
24161 var pm = this.trigger.child('div/div');
24162 pm.removeClass(this.meterClass);
24163 pm.addClass(this.meterClass[strength]);
24166 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24168 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24170 this._lastPwd = pwd;
24174 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24176 this._lastPwd = '';
24178 var pm = this.trigger.child('div/div');
24179 pm.removeClass(this.meterClass);
24180 pm.addClass('roo-password-meter-grey');
24183 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24186 this.inputEl().dom.type='password';
24189 validateValue: function (value)
24191 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24194 if (value.length == 0) {
24195 if (this.allowBlank) {
24196 this.clearInvalid();
24200 this.markInvalid(this.errors.PwdEmpty);
24201 this.errorMsg = this.errors.PwdEmpty;
24209 if (!value.match(/[\x21-\x7e]+/)) {
24210 this.markInvalid(this.errors.PwdBadChar);
24211 this.errorMsg = this.errors.PwdBadChar;
24214 if (value.length < 6) {
24215 this.markInvalid(this.errors.PwdShort);
24216 this.errorMsg = this.errors.PwdShort;
24219 if (value.length > 16) {
24220 this.markInvalid(this.errors.PwdLong);
24221 this.errorMsg = this.errors.PwdLong;
24225 if (this.ClientSideStrongPassword(value)) {
24227 } else if (this.ClientSideMediumPassword(value)) {
24229 } else if (this.ClientSideWeakPassword(value)) {
24236 if (strength < 2) {
24237 //this.markInvalid(this.errors.TooWeak);
24238 this.errorMsg = this.errors.TooWeak;
24243 console.log('strength2: ' + strength);
24245 //var pm = this.trigger.child('div/div/div').dom;
24247 var pm = this.trigger.child('div/div');
24248 pm.removeClass(this.meterClass);
24249 pm.addClass(this.meterClass[strength]);
24251 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24253 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24255 this.errorMsg = '';
24259 CharacterSetChecks: function (type)
24262 this.fResult = false;
24265 isctype: function (character, type)
24268 case this.kCapitalLetter:
24269 if (character >= 'A' && character <= 'Z') {
24274 case this.kSmallLetter:
24275 if (character >= 'a' && character <= 'z') {
24281 if (character >= '0' && character <= '9') {
24286 case this.kPunctuation:
24287 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24298 IsLongEnough: function (pwd, size)
24300 return !(pwd == null || isNaN(size) || pwd.length < size);
24303 SpansEnoughCharacterSets: function (word, nb)
24305 if (!this.IsLongEnough(word, nb))
24310 var characterSetChecks = new Array(
24311 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24312 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24315 for (var index = 0; index < word.length; ++index) {
24316 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24317 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24318 characterSetChecks[nCharSet].fResult = true;
24325 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24326 if (characterSetChecks[nCharSet].fResult) {
24331 if (nCharSets < nb) {
24337 ClientSideStrongPassword: function (pwd)
24339 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24342 ClientSideMediumPassword: function (pwd)
24344 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24347 ClientSideWeakPassword: function (pwd)
24349 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24352 })//<script type="text/javascript">
24355 * Based Ext JS Library 1.1.1
24356 * Copyright(c) 2006-2007, Ext JS, LLC.
24362 * @class Roo.HtmlEditorCore
24363 * @extends Roo.Component
24364 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24366 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24369 Roo.HtmlEditorCore = function(config){
24372 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24377 * @event initialize
24378 * Fires when the editor is fully initialized (including the iframe)
24379 * @param {Roo.HtmlEditorCore} this
24384 * Fires when the editor is first receives the focus. Any insertion must wait
24385 * until after this event.
24386 * @param {Roo.HtmlEditorCore} this
24390 * @event beforesync
24391 * Fires before the textarea is updated with content from the editor iframe. Return false
24392 * to cancel the sync.
24393 * @param {Roo.HtmlEditorCore} this
24394 * @param {String} html
24398 * @event beforepush
24399 * Fires before the iframe editor is updated with content from the textarea. Return false
24400 * to cancel the push.
24401 * @param {Roo.HtmlEditorCore} this
24402 * @param {String} html
24407 * Fires when the textarea is updated with content from the editor iframe.
24408 * @param {Roo.HtmlEditorCore} this
24409 * @param {String} html
24414 * Fires when the iframe editor is updated with content from the textarea.
24415 * @param {Roo.HtmlEditorCore} this
24416 * @param {String} html
24421 * @event editorevent
24422 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24423 * @param {Roo.HtmlEditorCore} this
24429 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24431 // defaults : white / black...
24432 this.applyBlacklists();
24439 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24443 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24449 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24454 * @cfg {Number} height (in pixels)
24458 * @cfg {Number} width (in pixels)
24463 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24466 stylesheets: false,
24471 // private properties
24472 validationEvent : false,
24474 initialized : false,
24476 sourceEditMode : false,
24477 onFocus : Roo.emptyFn,
24479 hideMode:'offsets',
24483 // blacklist + whitelisted elements..
24490 * Protected method that will not generally be called directly. It
24491 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24492 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24494 getDocMarkup : function(){
24498 // inherit styels from page...??
24499 if (this.stylesheets === false) {
24501 Roo.get(document.head).select('style').each(function(node) {
24502 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24505 Roo.get(document.head).select('link').each(function(node) {
24506 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24509 } else if (!this.stylesheets.length) {
24511 st = '<style type="text/css">' +
24512 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24515 for (var i in this.stylesheets) {
24516 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24521 st += '<style type="text/css">' +
24522 'IMG { cursor: pointer } ' +
24525 var cls = 'roo-htmleditor-body';
24527 if(this.bodyCls.length){
24528 cls += ' ' + this.bodyCls;
24531 return '<html><head>' + st +
24532 //<style type="text/css">' +
24533 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24535 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24539 onRender : function(ct, position)
24542 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24543 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24546 this.el.dom.style.border = '0 none';
24547 this.el.dom.setAttribute('tabIndex', -1);
24548 this.el.addClass('x-hidden hide');
24552 if(Roo.isIE){ // fix IE 1px bogus margin
24553 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24557 this.frameId = Roo.id();
24561 var iframe = this.owner.wrap.createChild({
24563 cls: 'form-control', // bootstrap..
24565 name: this.frameId,
24566 frameBorder : 'no',
24567 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24572 this.iframe = iframe.dom;
24574 this.assignDocWin();
24576 this.doc.designMode = 'on';
24579 this.doc.write(this.getDocMarkup());
24583 var task = { // must defer to wait for browser to be ready
24585 //console.log("run task?" + this.doc.readyState);
24586 this.assignDocWin();
24587 if(this.doc.body || this.doc.readyState == 'complete'){
24589 this.doc.designMode="on";
24593 Roo.TaskMgr.stop(task);
24594 this.initEditor.defer(10, this);
24601 Roo.TaskMgr.start(task);
24606 onResize : function(w, h)
24608 Roo.log('resize: ' +w + ',' + h );
24609 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24613 if(typeof w == 'number'){
24615 this.iframe.style.width = w + 'px';
24617 if(typeof h == 'number'){
24619 this.iframe.style.height = h + 'px';
24621 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24628 * Toggles the editor between standard and source edit mode.
24629 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24631 toggleSourceEdit : function(sourceEditMode){
24633 this.sourceEditMode = sourceEditMode === true;
24635 if(this.sourceEditMode){
24637 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24640 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24641 //this.iframe.className = '';
24644 //this.setSize(this.owner.wrap.getSize());
24645 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24652 * Protected method that will not generally be called directly. If you need/want
24653 * custom HTML cleanup, this is the method you should override.
24654 * @param {String} html The HTML to be cleaned
24655 * return {String} The cleaned HTML
24657 cleanHtml : function(html){
24658 html = String(html);
24659 if(html.length > 5){
24660 if(Roo.isSafari){ // strip safari nonsense
24661 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24664 if(html == ' '){
24671 * HTML Editor -> Textarea
24672 * Protected method that will not generally be called directly. Syncs the contents
24673 * of the editor iframe with the textarea.
24675 syncValue : function(){
24676 if(this.initialized){
24677 var bd = (this.doc.body || this.doc.documentElement);
24678 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24679 var html = bd.innerHTML;
24681 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24682 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24684 html = '<div style="'+m[0]+'">' + html + '</div>';
24687 html = this.cleanHtml(html);
24688 // fix up the special chars.. normaly like back quotes in word...
24689 // however we do not want to do this with chinese..
24690 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24692 var cc = match.charCodeAt();
24694 // Get the character value, handling surrogate pairs
24695 if (match.length == 2) {
24696 // It's a surrogate pair, calculate the Unicode code point
24697 var high = match.charCodeAt(0) - 0xD800;
24698 var low = match.charCodeAt(1) - 0xDC00;
24699 cc = (high * 0x400) + low + 0x10000;
24701 (cc >= 0x4E00 && cc < 0xA000 ) ||
24702 (cc >= 0x3400 && cc < 0x4E00 ) ||
24703 (cc >= 0xf900 && cc < 0xfb00 )
24708 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24709 return "&#" + cc + ";";
24716 if(this.owner.fireEvent('beforesync', this, html) !== false){
24717 this.el.dom.value = html;
24718 this.owner.fireEvent('sync', this, html);
24724 * Protected method that will not generally be called directly. Pushes the value of the textarea
24725 * into the iframe editor.
24727 pushValue : function(){
24728 if(this.initialized){
24729 var v = this.el.dom.value.trim();
24731 // if(v.length < 1){
24735 if(this.owner.fireEvent('beforepush', this, v) !== false){
24736 var d = (this.doc.body || this.doc.documentElement);
24738 this.cleanUpPaste();
24739 this.el.dom.value = d.innerHTML;
24740 this.owner.fireEvent('push', this, v);
24746 deferFocus : function(){
24747 this.focus.defer(10, this);
24751 focus : function(){
24752 if(this.win && !this.sourceEditMode){
24759 assignDocWin: function()
24761 var iframe = this.iframe;
24764 this.doc = iframe.contentWindow.document;
24765 this.win = iframe.contentWindow;
24767 // if (!Roo.get(this.frameId)) {
24770 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24771 // this.win = Roo.get(this.frameId).dom.contentWindow;
24773 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24777 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24778 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24783 initEditor : function(){
24784 //console.log("INIT EDITOR");
24785 this.assignDocWin();
24789 this.doc.designMode="on";
24791 this.doc.write(this.getDocMarkup());
24794 var dbody = (this.doc.body || this.doc.documentElement);
24795 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24796 // this copies styles from the containing element into thsi one..
24797 // not sure why we need all of this..
24798 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24800 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24801 //ss['background-attachment'] = 'fixed'; // w3c
24802 dbody.bgProperties = 'fixed'; // ie
24803 //Roo.DomHelper.applyStyles(dbody, ss);
24804 Roo.EventManager.on(this.doc, {
24805 //'mousedown': this.onEditorEvent,
24806 'mouseup': this.onEditorEvent,
24807 'dblclick': this.onEditorEvent,
24808 'click': this.onEditorEvent,
24809 'keyup': this.onEditorEvent,
24814 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24816 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24817 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24819 this.initialized = true;
24821 this.owner.fireEvent('initialize', this);
24826 onDestroy : function(){
24832 //for (var i =0; i < this.toolbars.length;i++) {
24833 // // fixme - ask toolbars for heights?
24834 // this.toolbars[i].onDestroy();
24837 //this.wrap.dom.innerHTML = '';
24838 //this.wrap.remove();
24843 onFirstFocus : function(){
24845 this.assignDocWin();
24848 this.activated = true;
24851 if(Roo.isGecko){ // prevent silly gecko errors
24853 var s = this.win.getSelection();
24854 if(!s.focusNode || s.focusNode.nodeType != 3){
24855 var r = s.getRangeAt(0);
24856 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24861 this.execCmd('useCSS', true);
24862 this.execCmd('styleWithCSS', false);
24865 this.owner.fireEvent('activate', this);
24869 adjustFont: function(btn){
24870 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24871 //if(Roo.isSafari){ // safari
24874 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24875 if(Roo.isSafari){ // safari
24876 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24877 v = (v < 10) ? 10 : v;
24878 v = (v > 48) ? 48 : v;
24879 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24884 v = Math.max(1, v+adjust);
24886 this.execCmd('FontSize', v );
24889 onEditorEvent : function(e)
24891 this.owner.fireEvent('editorevent', this, e);
24892 // this.updateToolbar();
24893 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24896 insertTag : function(tg)
24898 // could be a bit smarter... -> wrap the current selected tRoo..
24899 if (tg.toLowerCase() == 'span' ||
24900 tg.toLowerCase() == 'code' ||
24901 tg.toLowerCase() == 'sup' ||
24902 tg.toLowerCase() == 'sub'
24905 range = this.createRange(this.getSelection());
24906 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24907 wrappingNode.appendChild(range.extractContents());
24908 range.insertNode(wrappingNode);
24915 this.execCmd("formatblock", tg);
24919 insertText : function(txt)
24923 var range = this.createRange();
24924 range.deleteContents();
24925 //alert(Sender.getAttribute('label'));
24927 range.insertNode(this.doc.createTextNode(txt));
24933 * Executes a Midas editor command on the editor document and performs necessary focus and
24934 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24935 * @param {String} cmd The Midas command
24936 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24938 relayCmd : function(cmd, value){
24940 this.execCmd(cmd, value);
24941 this.owner.fireEvent('editorevent', this);
24942 //this.updateToolbar();
24943 this.owner.deferFocus();
24947 * Executes a Midas editor command directly on the editor document.
24948 * For visual commands, you should use {@link #relayCmd} instead.
24949 * <b>This should only be called after the editor is initialized.</b>
24950 * @param {String} cmd The Midas command
24951 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24953 execCmd : function(cmd, value){
24954 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24961 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24963 * @param {String} text | dom node..
24965 insertAtCursor : function(text)
24968 if(!this.activated){
24974 var r = this.doc.selection.createRange();
24985 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24989 // from jquery ui (MIT licenced)
24991 var win = this.win;
24993 if (win.getSelection && win.getSelection().getRangeAt) {
24994 range = win.getSelection().getRangeAt(0);
24995 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24996 range.insertNode(node);
24997 } else if (win.document.selection && win.document.selection.createRange) {
24998 // no firefox support
24999 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25000 win.document.selection.createRange().pasteHTML(txt);
25002 // no firefox support
25003 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25004 this.execCmd('InsertHTML', txt);
25013 mozKeyPress : function(e){
25015 var c = e.getCharCode(), cmd;
25018 c = String.fromCharCode(c).toLowerCase();
25032 this.cleanUpPaste.defer(100, this);
25040 e.preventDefault();
25048 fixKeys : function(){ // load time branching for fastest keydown performance
25050 return function(e){
25051 var k = e.getKey(), r;
25054 r = this.doc.selection.createRange();
25057 r.pasteHTML('    ');
25064 r = this.doc.selection.createRange();
25066 var target = r.parentElement();
25067 if(!target || target.tagName.toLowerCase() != 'li'){
25069 r.pasteHTML('<br />');
25075 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25076 this.cleanUpPaste.defer(100, this);
25082 }else if(Roo.isOpera){
25083 return function(e){
25084 var k = e.getKey();
25088 this.execCmd('InsertHTML','    ');
25091 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25092 this.cleanUpPaste.defer(100, this);
25097 }else if(Roo.isSafari){
25098 return function(e){
25099 var k = e.getKey();
25103 this.execCmd('InsertText','\t');
25107 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25108 this.cleanUpPaste.defer(100, this);
25116 getAllAncestors: function()
25118 var p = this.getSelectedNode();
25121 a.push(p); // push blank onto stack..
25122 p = this.getParentElement();
25126 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25130 a.push(this.doc.body);
25134 lastSelNode : false,
25137 getSelection : function()
25139 this.assignDocWin();
25140 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25143 getSelectedNode: function()
25145 // this may only work on Gecko!!!
25147 // should we cache this!!!!
25152 var range = this.createRange(this.getSelection()).cloneRange();
25155 var parent = range.parentElement();
25157 var testRange = range.duplicate();
25158 testRange.moveToElementText(parent);
25159 if (testRange.inRange(range)) {
25162 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25165 parent = parent.parentElement;
25170 // is ancestor a text element.
25171 var ac = range.commonAncestorContainer;
25172 if (ac.nodeType == 3) {
25173 ac = ac.parentNode;
25176 var ar = ac.childNodes;
25179 var other_nodes = [];
25180 var has_other_nodes = false;
25181 for (var i=0;i<ar.length;i++) {
25182 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25185 // fullly contained node.
25187 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25192 // probably selected..
25193 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25194 other_nodes.push(ar[i]);
25198 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25203 has_other_nodes = true;
25205 if (!nodes.length && other_nodes.length) {
25206 nodes= other_nodes;
25208 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25214 createRange: function(sel)
25216 // this has strange effects when using with
25217 // top toolbar - not sure if it's a great idea.
25218 //this.editor.contentWindow.focus();
25219 if (typeof sel != "undefined") {
25221 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25223 return this.doc.createRange();
25226 return this.doc.createRange();
25229 getParentElement: function()
25232 this.assignDocWin();
25233 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25235 var range = this.createRange(sel);
25238 var p = range.commonAncestorContainer;
25239 while (p.nodeType == 3) { // text node
25250 * Range intersection.. the hard stuff...
25254 * [ -- selected range --- ]
25258 * if end is before start or hits it. fail.
25259 * if start is after end or hits it fail.
25261 * if either hits (but other is outside. - then it's not
25267 // @see http://www.thismuchiknow.co.uk/?p=64.
25268 rangeIntersectsNode : function(range, node)
25270 var nodeRange = node.ownerDocument.createRange();
25272 nodeRange.selectNode(node);
25274 nodeRange.selectNodeContents(node);
25277 var rangeStartRange = range.cloneRange();
25278 rangeStartRange.collapse(true);
25280 var rangeEndRange = range.cloneRange();
25281 rangeEndRange.collapse(false);
25283 var nodeStartRange = nodeRange.cloneRange();
25284 nodeStartRange.collapse(true);
25286 var nodeEndRange = nodeRange.cloneRange();
25287 nodeEndRange.collapse(false);
25289 return rangeStartRange.compareBoundaryPoints(
25290 Range.START_TO_START, nodeEndRange) == -1 &&
25291 rangeEndRange.compareBoundaryPoints(
25292 Range.START_TO_START, nodeStartRange) == 1;
25296 rangeCompareNode : function(range, node)
25298 var nodeRange = node.ownerDocument.createRange();
25300 nodeRange.selectNode(node);
25302 nodeRange.selectNodeContents(node);
25306 range.collapse(true);
25308 nodeRange.collapse(true);
25310 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25311 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25313 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25315 var nodeIsBefore = ss == 1;
25316 var nodeIsAfter = ee == -1;
25318 if (nodeIsBefore && nodeIsAfter) {
25321 if (!nodeIsBefore && nodeIsAfter) {
25322 return 1; //right trailed.
25325 if (nodeIsBefore && !nodeIsAfter) {
25326 return 2; // left trailed.
25332 // private? - in a new class?
25333 cleanUpPaste : function()
25335 // cleans up the whole document..
25336 Roo.log('cleanuppaste');
25338 this.cleanUpChildren(this.doc.body);
25339 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25340 if (clean != this.doc.body.innerHTML) {
25341 this.doc.body.innerHTML = clean;
25346 cleanWordChars : function(input) {// change the chars to hex code
25347 var he = Roo.HtmlEditorCore;
25349 var output = input;
25350 Roo.each(he.swapCodes, function(sw) {
25351 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25353 output = output.replace(swapper, sw[1]);
25360 cleanUpChildren : function (n)
25362 if (!n.childNodes.length) {
25365 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25366 this.cleanUpChild(n.childNodes[i]);
25373 cleanUpChild : function (node)
25376 //console.log(node);
25377 if (node.nodeName == "#text") {
25378 // clean up silly Windows -- stuff?
25381 if (node.nodeName == "#comment") {
25382 node.parentNode.removeChild(node);
25383 // clean up silly Windows -- stuff?
25386 var lcname = node.tagName.toLowerCase();
25387 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25388 // whitelist of tags..
25390 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25392 node.parentNode.removeChild(node);
25397 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25399 // spans with no attributes - just remove them..
25400 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25401 remove_keep_children = true;
25404 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25405 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25407 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25408 // remove_keep_children = true;
25411 if (remove_keep_children) {
25412 this.cleanUpChildren(node);
25413 // inserts everything just before this node...
25414 while (node.childNodes.length) {
25415 var cn = node.childNodes[0];
25416 node.removeChild(cn);
25417 node.parentNode.insertBefore(cn, node);
25419 node.parentNode.removeChild(node);
25423 if (!node.attributes || !node.attributes.length) {
25428 this.cleanUpChildren(node);
25432 function cleanAttr(n,v)
25435 if (v.match(/^\./) || v.match(/^\//)) {
25438 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25441 if (v.match(/^#/)) {
25444 if (v.match(/^\{/)) { // allow template editing.
25447 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25448 node.removeAttribute(n);
25452 var cwhite = this.cwhite;
25453 var cblack = this.cblack;
25455 function cleanStyle(n,v)
25457 if (v.match(/expression/)) { //XSS?? should we even bother..
25458 node.removeAttribute(n);
25462 var parts = v.split(/;/);
25465 Roo.each(parts, function(p) {
25466 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25470 var l = p.split(':').shift().replace(/\s+/g,'');
25471 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25473 if ( cwhite.length && cblack.indexOf(l) > -1) {
25474 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25475 //node.removeAttribute(n);
25479 // only allow 'c whitelisted system attributes'
25480 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25481 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25482 //node.removeAttribute(n);
25492 if (clean.length) {
25493 node.setAttribute(n, clean.join(';'));
25495 node.removeAttribute(n);
25501 for (var i = node.attributes.length-1; i > -1 ; i--) {
25502 var a = node.attributes[i];
25505 if (a.name.toLowerCase().substr(0,2)=='on') {
25506 node.removeAttribute(a.name);
25509 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25510 node.removeAttribute(a.name);
25513 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25514 cleanAttr(a.name,a.value); // fixme..
25517 if (a.name == 'style') {
25518 cleanStyle(a.name,a.value);
25521 /// clean up MS crap..
25522 // tecnically this should be a list of valid class'es..
25525 if (a.name == 'class') {
25526 if (a.value.match(/^Mso/)) {
25527 node.removeAttribute('class');
25530 if (a.value.match(/^body$/)) {
25531 node.removeAttribute('class');
25542 this.cleanUpChildren(node);
25548 * Clean up MS wordisms...
25550 cleanWord : function(node)
25553 this.cleanWord(this.doc.body);
25558 node.nodeName == 'SPAN' &&
25559 !node.hasAttributes() &&
25560 node.childNodes.length == 1 &&
25561 node.firstChild.nodeName == "#text"
25563 var textNode = node.firstChild;
25564 node.removeChild(textNode);
25565 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25566 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25568 node.parentNode.insertBefore(textNode, node);
25569 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25570 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25572 node.parentNode.removeChild(node);
25575 if (node.nodeName == "#text") {
25576 // clean up silly Windows -- stuff?
25579 if (node.nodeName == "#comment") {
25580 node.parentNode.removeChild(node);
25581 // clean up silly Windows -- stuff?
25585 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25586 node.parentNode.removeChild(node);
25589 //Roo.log(node.tagName);
25590 // remove - but keep children..
25591 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25592 //Roo.log('-- removed');
25593 while (node.childNodes.length) {
25594 var cn = node.childNodes[0];
25595 node.removeChild(cn);
25596 node.parentNode.insertBefore(cn, node);
25597 // move node to parent - and clean it..
25598 this.cleanWord(cn);
25600 node.parentNode.removeChild(node);
25601 /// no need to iterate chidlren = it's got none..
25602 //this.iterateChildren(node, this.cleanWord);
25606 if (node.className.length) {
25608 var cn = node.className.split(/\W+/);
25610 Roo.each(cn, function(cls) {
25611 if (cls.match(/Mso[a-zA-Z]+/)) {
25616 node.className = cna.length ? cna.join(' ') : '';
25618 node.removeAttribute("class");
25622 if (node.hasAttribute("lang")) {
25623 node.removeAttribute("lang");
25626 if (node.hasAttribute("style")) {
25628 var styles = node.getAttribute("style").split(";");
25630 Roo.each(styles, function(s) {
25631 if (!s.match(/:/)) {
25634 var kv = s.split(":");
25635 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25638 // what ever is left... we allow.
25641 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25642 if (!nstyle.length) {
25643 node.removeAttribute('style');
25646 this.iterateChildren(node, this.cleanWord);
25652 * iterateChildren of a Node, calling fn each time, using this as the scole..
25653 * @param {DomNode} node node to iterate children of.
25654 * @param {Function} fn method of this class to call on each item.
25656 iterateChildren : function(node, fn)
25658 if (!node.childNodes.length) {
25661 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25662 fn.call(this, node.childNodes[i])
25668 * cleanTableWidths.
25670 * Quite often pasting from word etc.. results in tables with column and widths.
25671 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25674 cleanTableWidths : function(node)
25679 this.cleanTableWidths(this.doc.body);
25684 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25687 Roo.log(node.tagName);
25688 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25689 this.iterateChildren(node, this.cleanTableWidths);
25692 if (node.hasAttribute('width')) {
25693 node.removeAttribute('width');
25697 if (node.hasAttribute("style")) {
25700 var styles = node.getAttribute("style").split(";");
25702 Roo.each(styles, function(s) {
25703 if (!s.match(/:/)) {
25706 var kv = s.split(":");
25707 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25710 // what ever is left... we allow.
25713 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25714 if (!nstyle.length) {
25715 node.removeAttribute('style');
25719 this.iterateChildren(node, this.cleanTableWidths);
25727 domToHTML : function(currentElement, depth, nopadtext) {
25729 depth = depth || 0;
25730 nopadtext = nopadtext || false;
25732 if (!currentElement) {
25733 return this.domToHTML(this.doc.body);
25736 //Roo.log(currentElement);
25738 var allText = false;
25739 var nodeName = currentElement.nodeName;
25740 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25742 if (nodeName == '#text') {
25744 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25749 if (nodeName != 'BODY') {
25752 // Prints the node tagName, such as <A>, <IMG>, etc
25755 for(i = 0; i < currentElement.attributes.length;i++) {
25757 var aname = currentElement.attributes.item(i).name;
25758 if (!currentElement.attributes.item(i).value.length) {
25761 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25764 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25773 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25776 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25781 // Traverse the tree
25783 var currentElementChild = currentElement.childNodes.item(i);
25784 var allText = true;
25785 var innerHTML = '';
25787 while (currentElementChild) {
25788 // Formatting code (indent the tree so it looks nice on the screen)
25789 var nopad = nopadtext;
25790 if (lastnode == 'SPAN') {
25794 if (currentElementChild.nodeName == '#text') {
25795 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25796 toadd = nopadtext ? toadd : toadd.trim();
25797 if (!nopad && toadd.length > 80) {
25798 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25800 innerHTML += toadd;
25803 currentElementChild = currentElement.childNodes.item(i);
25809 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25811 // Recursively traverse the tree structure of the child node
25812 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25813 lastnode = currentElementChild.nodeName;
25815 currentElementChild=currentElement.childNodes.item(i);
25821 // The remaining code is mostly for formatting the tree
25822 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25827 ret+= "</"+tagName+">";
25833 applyBlacklists : function()
25835 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25836 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25840 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25841 if (b.indexOf(tag) > -1) {
25844 this.white.push(tag);
25848 Roo.each(w, function(tag) {
25849 if (b.indexOf(tag) > -1) {
25852 if (this.white.indexOf(tag) > -1) {
25855 this.white.push(tag);
25860 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25861 if (w.indexOf(tag) > -1) {
25864 this.black.push(tag);
25868 Roo.each(b, function(tag) {
25869 if (w.indexOf(tag) > -1) {
25872 if (this.black.indexOf(tag) > -1) {
25875 this.black.push(tag);
25880 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25881 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25885 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25886 if (b.indexOf(tag) > -1) {
25889 this.cwhite.push(tag);
25893 Roo.each(w, function(tag) {
25894 if (b.indexOf(tag) > -1) {
25897 if (this.cwhite.indexOf(tag) > -1) {
25900 this.cwhite.push(tag);
25905 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25906 if (w.indexOf(tag) > -1) {
25909 this.cblack.push(tag);
25913 Roo.each(b, function(tag) {
25914 if (w.indexOf(tag) > -1) {
25917 if (this.cblack.indexOf(tag) > -1) {
25920 this.cblack.push(tag);
25925 setStylesheets : function(stylesheets)
25927 if(typeof(stylesheets) == 'string'){
25928 Roo.get(this.iframe.contentDocument.head).createChild({
25930 rel : 'stylesheet',
25939 Roo.each(stylesheets, function(s) {
25944 Roo.get(_this.iframe.contentDocument.head).createChild({
25946 rel : 'stylesheet',
25955 removeStylesheets : function()
25959 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25964 setStyle : function(style)
25966 Roo.get(this.iframe.contentDocument.head).createChild({
25975 // hide stuff that is not compatible
25989 * @event specialkey
25993 * @cfg {String} fieldClass @hide
25996 * @cfg {String} focusClass @hide
25999 * @cfg {String} autoCreate @hide
26002 * @cfg {String} inputType @hide
26005 * @cfg {String} invalidClass @hide
26008 * @cfg {String} invalidText @hide
26011 * @cfg {String} msgFx @hide
26014 * @cfg {String} validateOnBlur @hide
26018 Roo.HtmlEditorCore.white = [
26019 'area', 'br', 'img', 'input', 'hr', 'wbr',
26021 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26022 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26023 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26024 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26025 'table', 'ul', 'xmp',
26027 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26030 'dir', 'menu', 'ol', 'ul', 'dl',
26036 Roo.HtmlEditorCore.black = [
26037 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26039 'base', 'basefont', 'bgsound', 'blink', 'body',
26040 'frame', 'frameset', 'head', 'html', 'ilayer',
26041 'iframe', 'layer', 'link', 'meta', 'object',
26042 'script', 'style' ,'title', 'xml' // clean later..
26044 Roo.HtmlEditorCore.clean = [
26045 'script', 'style', 'title', 'xml'
26047 Roo.HtmlEditorCore.remove = [
26052 Roo.HtmlEditorCore.ablack = [
26056 Roo.HtmlEditorCore.aclean = [
26057 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26061 Roo.HtmlEditorCore.pwhite= [
26062 'http', 'https', 'mailto'
26065 // white listed style attributes.
26066 Roo.HtmlEditorCore.cwhite= [
26067 // 'text-align', /// default is to allow most things..
26073 // black listed style attributes.
26074 Roo.HtmlEditorCore.cblack= [
26075 // 'font-size' -- this can be set by the project
26079 Roo.HtmlEditorCore.swapCodes =[
26080 [ 8211, "–" ],
26081 [ 8212, "—" ],
26098 * @class Roo.bootstrap.HtmlEditor
26099 * @extends Roo.bootstrap.TextArea
26100 * Bootstrap HtmlEditor class
26103 * Create a new HtmlEditor
26104 * @param {Object} config The config object
26107 Roo.bootstrap.HtmlEditor = function(config){
26108 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26109 if (!this.toolbars) {
26110 this.toolbars = [];
26113 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26116 * @event initialize
26117 * Fires when the editor is fully initialized (including the iframe)
26118 * @param {HtmlEditor} this
26123 * Fires when the editor is first receives the focus. Any insertion must wait
26124 * until after this event.
26125 * @param {HtmlEditor} this
26129 * @event beforesync
26130 * Fires before the textarea is updated with content from the editor iframe. Return false
26131 * to cancel the sync.
26132 * @param {HtmlEditor} this
26133 * @param {String} html
26137 * @event beforepush
26138 * Fires before the iframe editor is updated with content from the textarea. Return false
26139 * to cancel the push.
26140 * @param {HtmlEditor} this
26141 * @param {String} html
26146 * Fires when the textarea is updated with content from the editor iframe.
26147 * @param {HtmlEditor} this
26148 * @param {String} html
26153 * Fires when the iframe editor is updated with content from the textarea.
26154 * @param {HtmlEditor} this
26155 * @param {String} html
26159 * @event editmodechange
26160 * Fires when the editor switches edit modes
26161 * @param {HtmlEditor} this
26162 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26164 editmodechange: true,
26166 * @event editorevent
26167 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26168 * @param {HtmlEditor} this
26172 * @event firstfocus
26173 * Fires when on first focus - needed by toolbars..
26174 * @param {HtmlEditor} this
26179 * Auto save the htmlEditor value as a file into Events
26180 * @param {HtmlEditor} this
26184 * @event savedpreview
26185 * preview the saved version of htmlEditor
26186 * @param {HtmlEditor} this
26193 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26197 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26202 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26207 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26212 * @cfg {Number} height (in pixels)
26216 * @cfg {Number} width (in pixels)
26221 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26224 stylesheets: false,
26229 // private properties
26230 validationEvent : false,
26232 initialized : false,
26235 onFocus : Roo.emptyFn,
26237 hideMode:'offsets',
26239 tbContainer : false,
26243 toolbarContainer :function() {
26244 return this.wrap.select('.x-html-editor-tb',true).first();
26248 * Protected method that will not generally be called directly. It
26249 * is called when the editor creates its toolbar. Override this method if you need to
26250 * add custom toolbar buttons.
26251 * @param {HtmlEditor} editor
26253 createToolbar : function(){
26254 Roo.log('renewing');
26255 Roo.log("create toolbars");
26257 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26258 this.toolbars[0].render(this.toolbarContainer());
26262 // if (!editor.toolbars || !editor.toolbars.length) {
26263 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26266 // for (var i =0 ; i < editor.toolbars.length;i++) {
26267 // editor.toolbars[i] = Roo.factory(
26268 // typeof(editor.toolbars[i]) == 'string' ?
26269 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26270 // Roo.bootstrap.HtmlEditor);
26271 // editor.toolbars[i].init(editor);
26277 onRender : function(ct, position)
26279 // Roo.log("Call onRender: " + this.xtype);
26281 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26283 this.wrap = this.inputEl().wrap({
26284 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26287 this.editorcore.onRender(ct, position);
26289 if (this.resizable) {
26290 this.resizeEl = new Roo.Resizable(this.wrap, {
26294 minHeight : this.height,
26295 height: this.height,
26296 handles : this.resizable,
26299 resize : function(r, w, h) {
26300 _t.onResize(w,h); // -something
26306 this.createToolbar(this);
26309 if(!this.width && this.resizable){
26310 this.setSize(this.wrap.getSize());
26312 if (this.resizeEl) {
26313 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26314 // should trigger onReize..
26320 onResize : function(w, h)
26322 Roo.log('resize: ' +w + ',' + h );
26323 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26327 if(this.inputEl() ){
26328 if(typeof w == 'number'){
26329 var aw = w - this.wrap.getFrameWidth('lr');
26330 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26333 if(typeof h == 'number'){
26334 var tbh = -11; // fixme it needs to tool bar size!
26335 for (var i =0; i < this.toolbars.length;i++) {
26336 // fixme - ask toolbars for heights?
26337 tbh += this.toolbars[i].el.getHeight();
26338 //if (this.toolbars[i].footer) {
26339 // tbh += this.toolbars[i].footer.el.getHeight();
26347 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26348 ah -= 5; // knock a few pixes off for look..
26349 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26353 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26354 this.editorcore.onResize(ew,eh);
26359 * Toggles the editor between standard and source edit mode.
26360 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26362 toggleSourceEdit : function(sourceEditMode)
26364 this.editorcore.toggleSourceEdit(sourceEditMode);
26366 if(this.editorcore.sourceEditMode){
26367 Roo.log('editor - showing textarea');
26370 // Roo.log(this.syncValue());
26372 this.inputEl().removeClass(['hide', 'x-hidden']);
26373 this.inputEl().dom.removeAttribute('tabIndex');
26374 this.inputEl().focus();
26376 Roo.log('editor - hiding textarea');
26378 // Roo.log(this.pushValue());
26381 this.inputEl().addClass(['hide', 'x-hidden']);
26382 this.inputEl().dom.setAttribute('tabIndex', -1);
26383 //this.deferFocus();
26386 if(this.resizable){
26387 this.setSize(this.wrap.getSize());
26390 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26393 // private (for BoxComponent)
26394 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26396 // private (for BoxComponent)
26397 getResizeEl : function(){
26401 // private (for BoxComponent)
26402 getPositionEl : function(){
26407 initEvents : function(){
26408 this.originalValue = this.getValue();
26412 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26415 // markInvalid : Roo.emptyFn,
26417 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26420 // clearInvalid : Roo.emptyFn,
26422 setValue : function(v){
26423 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26424 this.editorcore.pushValue();
26429 deferFocus : function(){
26430 this.focus.defer(10, this);
26434 focus : function(){
26435 this.editorcore.focus();
26441 onDestroy : function(){
26447 for (var i =0; i < this.toolbars.length;i++) {
26448 // fixme - ask toolbars for heights?
26449 this.toolbars[i].onDestroy();
26452 this.wrap.dom.innerHTML = '';
26453 this.wrap.remove();
26458 onFirstFocus : function(){
26459 //Roo.log("onFirstFocus");
26460 this.editorcore.onFirstFocus();
26461 for (var i =0; i < this.toolbars.length;i++) {
26462 this.toolbars[i].onFirstFocus();
26468 syncValue : function()
26470 this.editorcore.syncValue();
26473 pushValue : function()
26475 this.editorcore.pushValue();
26479 // hide stuff that is not compatible
26493 * @event specialkey
26497 * @cfg {String} fieldClass @hide
26500 * @cfg {String} focusClass @hide
26503 * @cfg {String} autoCreate @hide
26506 * @cfg {String} inputType @hide
26510 * @cfg {String} invalidText @hide
26513 * @cfg {String} msgFx @hide
26516 * @cfg {String} validateOnBlur @hide
26525 Roo.namespace('Roo.bootstrap.htmleditor');
26527 * @class Roo.bootstrap.HtmlEditorToolbar1
26533 new Roo.bootstrap.HtmlEditor({
26536 new Roo.bootstrap.HtmlEditorToolbar1({
26537 disable : { fonts: 1 , format: 1, ..., ... , ...],
26543 * @cfg {Object} disable List of elements to disable..
26544 * @cfg {Array} btns List of additional buttons.
26548 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26551 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26554 Roo.apply(this, config);
26556 // default disabled, based on 'good practice'..
26557 this.disable = this.disable || {};
26558 Roo.applyIf(this.disable, {
26561 specialElements : true
26563 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26565 this.editor = config.editor;
26566 this.editorcore = config.editor.editorcore;
26568 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26570 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26571 // dont call parent... till later.
26573 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26578 editorcore : false,
26583 "h1","h2","h3","h4","h5","h6",
26585 "abbr", "acronym", "address", "cite", "samp", "var",
26589 onRender : function(ct, position)
26591 // Roo.log("Call onRender: " + this.xtype);
26593 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26595 this.el.dom.style.marginBottom = '0';
26597 var editorcore = this.editorcore;
26598 var editor= this.editor;
26601 var btn = function(id,cmd , toggle, handler, html){
26603 var event = toggle ? 'toggle' : 'click';
26608 xns: Roo.bootstrap,
26612 enableToggle:toggle !== false,
26614 pressed : toggle ? false : null,
26617 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26618 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26624 // var cb_box = function...
26629 xns: Roo.bootstrap,
26634 xns: Roo.bootstrap,
26638 Roo.each(this.formats, function(f) {
26639 style.menu.items.push({
26641 xns: Roo.bootstrap,
26642 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26647 editorcore.insertTag(this.tagname);
26654 children.push(style);
26656 btn('bold',false,true);
26657 btn('italic',false,true);
26658 btn('align-left', 'justifyleft',true);
26659 btn('align-center', 'justifycenter',true);
26660 btn('align-right' , 'justifyright',true);
26661 btn('link', false, false, function(btn) {
26662 //Roo.log("create link?");
26663 var url = prompt(this.createLinkText, this.defaultLinkValue);
26664 if(url && url != 'http:/'+'/'){
26665 this.editorcore.relayCmd('createlink', url);
26668 btn('list','insertunorderedlist',true);
26669 btn('pencil', false,true, function(btn){
26671 this.toggleSourceEdit(btn.pressed);
26674 if (this.editor.btns.length > 0) {
26675 for (var i = 0; i<this.editor.btns.length; i++) {
26676 children.push(this.editor.btns[i]);
26684 xns: Roo.bootstrap,
26689 xns: Roo.bootstrap,
26694 cog.menu.items.push({
26696 xns: Roo.bootstrap,
26697 html : Clean styles,
26702 editorcore.insertTag(this.tagname);
26711 this.xtype = 'NavSimplebar';
26713 for(var i=0;i< children.length;i++) {
26715 this.buttons.add(this.addxtypeChild(children[i]));
26719 editor.on('editorevent', this.updateToolbar, this);
26721 onBtnClick : function(id)
26723 this.editorcore.relayCmd(id);
26724 this.editorcore.focus();
26728 * Protected method that will not generally be called directly. It triggers
26729 * a toolbar update by reading the markup state of the current selection in the editor.
26731 updateToolbar: function(){
26733 if(!this.editorcore.activated){
26734 this.editor.onFirstFocus(); // is this neeed?
26738 var btns = this.buttons;
26739 var doc = this.editorcore.doc;
26740 btns.get('bold').setActive(doc.queryCommandState('bold'));
26741 btns.get('italic').setActive(doc.queryCommandState('italic'));
26742 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26744 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26745 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26746 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26748 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26749 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26752 var ans = this.editorcore.getAllAncestors();
26753 if (this.formatCombo) {
26756 var store = this.formatCombo.store;
26757 this.formatCombo.setValue("");
26758 for (var i =0; i < ans.length;i++) {
26759 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26761 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26769 // hides menus... - so this cant be on a menu...
26770 Roo.bootstrap.MenuMgr.hideAll();
26772 Roo.bootstrap.MenuMgr.hideAll();
26773 //this.editorsyncValue();
26775 onFirstFocus: function() {
26776 this.buttons.each(function(item){
26780 toggleSourceEdit : function(sourceEditMode){
26783 if(sourceEditMode){
26784 Roo.log("disabling buttons");
26785 this.buttons.each( function(item){
26786 if(item.cmd != 'pencil'){
26792 Roo.log("enabling buttons");
26793 if(this.editorcore.initialized){
26794 this.buttons.each( function(item){
26800 Roo.log("calling toggole on editor");
26801 // tell the editor that it's been pressed..
26802 this.editor.toggleSourceEdit(sourceEditMode);
26816 * @class Roo.bootstrap.Markdown
26817 * @extends Roo.bootstrap.TextArea
26818 * Bootstrap Showdown editable area
26819 * @cfg {string} content
26822 * Create a new Showdown
26825 Roo.bootstrap.Markdown = function(config){
26826 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26830 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26834 initEvents : function()
26837 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26838 this.markdownEl = this.el.createChild({
26839 cls : 'roo-markdown-area'
26841 this.inputEl().addClass('d-none');
26842 if (this.getValue() == '') {
26843 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26846 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26848 this.markdownEl.on('click', this.toggleTextEdit, this);
26849 this.on('blur', this.toggleTextEdit, this);
26850 this.on('specialkey', this.resizeTextArea, this);
26853 toggleTextEdit : function()
26855 var sh = this.markdownEl.getHeight();
26856 this.inputEl().addClass('d-none');
26857 this.markdownEl.addClass('d-none');
26858 if (!this.editing) {
26860 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26861 this.inputEl().removeClass('d-none');
26862 this.inputEl().focus();
26863 this.editing = true;
26866 // show showdown...
26867 this.updateMarkdown();
26868 this.markdownEl.removeClass('d-none');
26869 this.editing = false;
26872 updateMarkdown : function()
26874 if (this.getValue() == '') {
26875 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26879 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26882 resizeTextArea: function () {
26885 Roo.log([sh, this.getValue().split("\n").length * 30]);
26886 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26888 setValue : function(val)
26890 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26891 if (!this.editing) {
26892 this.updateMarkdown();
26898 if (!this.editing) {
26899 this.toggleTextEdit();
26907 * @class Roo.bootstrap.Table.AbstractSelectionModel
26908 * @extends Roo.util.Observable
26909 * Abstract base class for grid SelectionModels. It provides the interface that should be
26910 * implemented by descendant classes. This class should not be directly instantiated.
26913 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26914 this.locked = false;
26915 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26919 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26920 /** @ignore Called by the grid automatically. Do not call directly. */
26921 init : function(grid){
26927 * Locks the selections.
26930 this.locked = true;
26934 * Unlocks the selections.
26936 unlock : function(){
26937 this.locked = false;
26941 * Returns true if the selections are locked.
26942 * @return {Boolean}
26944 isLocked : function(){
26945 return this.locked;
26949 initEvents : function ()
26955 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26956 * @class Roo.bootstrap.Table.RowSelectionModel
26957 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26958 * It supports multiple selections and keyboard selection/navigation.
26960 * @param {Object} config
26963 Roo.bootstrap.Table.RowSelectionModel = function(config){
26964 Roo.apply(this, config);
26965 this.selections = new Roo.util.MixedCollection(false, function(o){
26970 this.lastActive = false;
26974 * @event selectionchange
26975 * Fires when the selection changes
26976 * @param {SelectionModel} this
26978 "selectionchange" : true,
26980 * @event afterselectionchange
26981 * Fires after the selection changes (eg. by key press or clicking)
26982 * @param {SelectionModel} this
26984 "afterselectionchange" : true,
26986 * @event beforerowselect
26987 * Fires when a row is selected being selected, return false to cancel.
26988 * @param {SelectionModel} this
26989 * @param {Number} rowIndex The selected index
26990 * @param {Boolean} keepExisting False if other selections will be cleared
26992 "beforerowselect" : true,
26995 * Fires when a row is selected.
26996 * @param {SelectionModel} this
26997 * @param {Number} rowIndex The selected index
26998 * @param {Roo.data.Record} r The record
27000 "rowselect" : true,
27002 * @event rowdeselect
27003 * Fires when a row is deselected.
27004 * @param {SelectionModel} this
27005 * @param {Number} rowIndex The selected index
27007 "rowdeselect" : true
27009 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27010 this.locked = false;
27013 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27015 * @cfg {Boolean} singleSelect
27016 * True to allow selection of only one row at a time (defaults to false)
27018 singleSelect : false,
27021 initEvents : function()
27024 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27025 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27026 //}else{ // allow click to work like normal
27027 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27029 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27030 this.grid.on("rowclick", this.handleMouseDown, this);
27032 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27033 "up" : function(e){
27035 this.selectPrevious(e.shiftKey);
27036 }else if(this.last !== false && this.lastActive !== false){
27037 var last = this.last;
27038 this.selectRange(this.last, this.lastActive-1);
27039 this.grid.getView().focusRow(this.lastActive);
27040 if(last !== false){
27044 this.selectFirstRow();
27046 this.fireEvent("afterselectionchange", this);
27048 "down" : function(e){
27050 this.selectNext(e.shiftKey);
27051 }else if(this.last !== false && this.lastActive !== false){
27052 var last = this.last;
27053 this.selectRange(this.last, this.lastActive+1);
27054 this.grid.getView().focusRow(this.lastActive);
27055 if(last !== false){
27059 this.selectFirstRow();
27061 this.fireEvent("afterselectionchange", this);
27065 this.grid.store.on('load', function(){
27066 this.selections.clear();
27069 var view = this.grid.view;
27070 view.on("refresh", this.onRefresh, this);
27071 view.on("rowupdated", this.onRowUpdated, this);
27072 view.on("rowremoved", this.onRemove, this);
27077 onRefresh : function()
27079 var ds = this.grid.store, i, v = this.grid.view;
27080 var s = this.selections;
27081 s.each(function(r){
27082 if((i = ds.indexOfId(r.id)) != -1){
27091 onRemove : function(v, index, r){
27092 this.selections.remove(r);
27096 onRowUpdated : function(v, index, r){
27097 if(this.isSelected(r)){
27098 v.onRowSelect(index);
27104 * @param {Array} records The records to select
27105 * @param {Boolean} keepExisting (optional) True to keep existing selections
27107 selectRecords : function(records, keepExisting)
27110 this.clearSelections();
27112 var ds = this.grid.store;
27113 for(var i = 0, len = records.length; i < len; i++){
27114 this.selectRow(ds.indexOf(records[i]), true);
27119 * Gets the number of selected rows.
27122 getCount : function(){
27123 return this.selections.length;
27127 * Selects the first row in the grid.
27129 selectFirstRow : function(){
27134 * Select the last row.
27135 * @param {Boolean} keepExisting (optional) True to keep existing selections
27137 selectLastRow : function(keepExisting){
27138 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27139 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27143 * Selects the row immediately following the last selected row.
27144 * @param {Boolean} keepExisting (optional) True to keep existing selections
27146 selectNext : function(keepExisting)
27148 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27149 this.selectRow(this.last+1, keepExisting);
27150 this.grid.getView().focusRow(this.last);
27155 * Selects the row that precedes the last selected row.
27156 * @param {Boolean} keepExisting (optional) True to keep existing selections
27158 selectPrevious : function(keepExisting){
27160 this.selectRow(this.last-1, keepExisting);
27161 this.grid.getView().focusRow(this.last);
27166 * Returns the selected records
27167 * @return {Array} Array of selected records
27169 getSelections : function(){
27170 return [].concat(this.selections.items);
27174 * Returns the first selected record.
27177 getSelected : function(){
27178 return this.selections.itemAt(0);
27183 * Clears all selections.
27185 clearSelections : function(fast)
27191 var ds = this.grid.store;
27192 var s = this.selections;
27193 s.each(function(r){
27194 this.deselectRow(ds.indexOfId(r.id));
27198 this.selections.clear();
27205 * Selects all rows.
27207 selectAll : function(){
27211 this.selections.clear();
27212 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27213 this.selectRow(i, true);
27218 * Returns True if there is a selection.
27219 * @return {Boolean}
27221 hasSelection : function(){
27222 return this.selections.length > 0;
27226 * Returns True if the specified row is selected.
27227 * @param {Number/Record} record The record or index of the record to check
27228 * @return {Boolean}
27230 isSelected : function(index){
27231 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27232 return (r && this.selections.key(r.id) ? true : false);
27236 * Returns True if the specified record id is selected.
27237 * @param {String} id The id of record to check
27238 * @return {Boolean}
27240 isIdSelected : function(id){
27241 return (this.selections.key(id) ? true : false);
27246 handleMouseDBClick : function(e, t){
27250 handleMouseDown : function(e, t)
27252 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27253 if(this.isLocked() || rowIndex < 0 ){
27256 if(e.shiftKey && this.last !== false){
27257 var last = this.last;
27258 this.selectRange(last, rowIndex, e.ctrlKey);
27259 this.last = last; // reset the last
27263 var isSelected = this.isSelected(rowIndex);
27264 //Roo.log("select row:" + rowIndex);
27266 this.deselectRow(rowIndex);
27268 this.selectRow(rowIndex, true);
27272 if(e.button !== 0 && isSelected){
27273 alert('rowIndex 2: ' + rowIndex);
27274 view.focusRow(rowIndex);
27275 }else if(e.ctrlKey && isSelected){
27276 this.deselectRow(rowIndex);
27277 }else if(!isSelected){
27278 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27279 view.focusRow(rowIndex);
27283 this.fireEvent("afterselectionchange", this);
27286 handleDragableRowClick : function(grid, rowIndex, e)
27288 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27289 this.selectRow(rowIndex, false);
27290 grid.view.focusRow(rowIndex);
27291 this.fireEvent("afterselectionchange", this);
27296 * Selects multiple rows.
27297 * @param {Array} rows Array of the indexes of the row to select
27298 * @param {Boolean} keepExisting (optional) True to keep existing selections
27300 selectRows : function(rows, keepExisting){
27302 this.clearSelections();
27304 for(var i = 0, len = rows.length; i < len; i++){
27305 this.selectRow(rows[i], true);
27310 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27311 * @param {Number} startRow The index of the first row in the range
27312 * @param {Number} endRow The index of the last row in the range
27313 * @param {Boolean} keepExisting (optional) True to retain existing selections
27315 selectRange : function(startRow, endRow, keepExisting){
27320 this.clearSelections();
27322 if(startRow <= endRow){
27323 for(var i = startRow; i <= endRow; i++){
27324 this.selectRow(i, true);
27327 for(var i = startRow; i >= endRow; i--){
27328 this.selectRow(i, true);
27334 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27335 * @param {Number} startRow The index of the first row in the range
27336 * @param {Number} endRow The index of the last row in the range
27338 deselectRange : function(startRow, endRow, preventViewNotify){
27342 for(var i = startRow; i <= endRow; i++){
27343 this.deselectRow(i, preventViewNotify);
27349 * @param {Number} row The index of the row to select
27350 * @param {Boolean} keepExisting (optional) True to keep existing selections
27352 selectRow : function(index, keepExisting, preventViewNotify)
27354 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27357 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27358 if(!keepExisting || this.singleSelect){
27359 this.clearSelections();
27362 var r = this.grid.store.getAt(index);
27363 //console.log('selectRow - record id :' + r.id);
27365 this.selections.add(r);
27366 this.last = this.lastActive = index;
27367 if(!preventViewNotify){
27368 var proxy = new Roo.Element(
27369 this.grid.getRowDom(index)
27371 proxy.addClass('bg-info info');
27373 this.fireEvent("rowselect", this, index, r);
27374 this.fireEvent("selectionchange", this);
27380 * @param {Number} row The index of the row to deselect
27382 deselectRow : function(index, preventViewNotify)
27387 if(this.last == index){
27390 if(this.lastActive == index){
27391 this.lastActive = false;
27394 var r = this.grid.store.getAt(index);
27399 this.selections.remove(r);
27400 //.console.log('deselectRow - record id :' + r.id);
27401 if(!preventViewNotify){
27403 var proxy = new Roo.Element(
27404 this.grid.getRowDom(index)
27406 proxy.removeClass('bg-info info');
27408 this.fireEvent("rowdeselect", this, index);
27409 this.fireEvent("selectionchange", this);
27413 restoreLast : function(){
27415 this.last = this._last;
27420 acceptsNav : function(row, col, cm){
27421 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27425 onEditorKey : function(field, e){
27426 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27431 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27433 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27435 }else if(k == e.ENTER && !e.ctrlKey){
27439 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27441 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27443 }else if(k == e.ESC){
27447 g.startEditing(newCell[0], newCell[1]);
27453 * Ext JS Library 1.1.1
27454 * Copyright(c) 2006-2007, Ext JS, LLC.
27456 * Originally Released Under LGPL - original licence link has changed is not relivant.
27459 * <script type="text/javascript">
27463 * @class Roo.bootstrap.PagingToolbar
27464 * @extends Roo.bootstrap.NavSimplebar
27465 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27467 * Create a new PagingToolbar
27468 * @param {Object} config The config object
27469 * @param {Roo.data.Store} store
27471 Roo.bootstrap.PagingToolbar = function(config)
27473 // old args format still supported... - xtype is prefered..
27474 // created from xtype...
27476 this.ds = config.dataSource;
27478 if (config.store && !this.ds) {
27479 this.store= Roo.factory(config.store, Roo.data);
27480 this.ds = this.store;
27481 this.ds.xmodule = this.xmodule || false;
27484 this.toolbarItems = [];
27485 if (config.items) {
27486 this.toolbarItems = config.items;
27489 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27494 this.bind(this.ds);
27497 if (Roo.bootstrap.version == 4) {
27498 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27500 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27505 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27507 * @cfg {Roo.data.Store} dataSource
27508 * The underlying data store providing the paged data
27511 * @cfg {String/HTMLElement/Element} container
27512 * container The id or element that will contain the toolbar
27515 * @cfg {Boolean} displayInfo
27516 * True to display the displayMsg (defaults to false)
27519 * @cfg {Number} pageSize
27520 * The number of records to display per page (defaults to 20)
27524 * @cfg {String} displayMsg
27525 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27527 displayMsg : 'Displaying {0} - {1} of {2}',
27529 * @cfg {String} emptyMsg
27530 * The message to display when no records are found (defaults to "No data to display")
27532 emptyMsg : 'No data to display',
27534 * Customizable piece of the default paging text (defaults to "Page")
27537 beforePageText : "Page",
27539 * Customizable piece of the default paging text (defaults to "of %0")
27542 afterPageText : "of {0}",
27544 * Customizable piece of the default paging text (defaults to "First Page")
27547 firstText : "First Page",
27549 * Customizable piece of the default paging text (defaults to "Previous Page")
27552 prevText : "Previous Page",
27554 * Customizable piece of the default paging text (defaults to "Next Page")
27557 nextText : "Next Page",
27559 * Customizable piece of the default paging text (defaults to "Last Page")
27562 lastText : "Last Page",
27564 * Customizable piece of the default paging text (defaults to "Refresh")
27567 refreshText : "Refresh",
27571 onRender : function(ct, position)
27573 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27574 this.navgroup.parentId = this.id;
27575 this.navgroup.onRender(this.el, null);
27576 // add the buttons to the navgroup
27578 if(this.displayInfo){
27579 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27580 this.displayEl = this.el.select('.x-paging-info', true).first();
27581 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27582 // this.displayEl = navel.el.select('span',true).first();
27588 Roo.each(_this.buttons, function(e){ // this might need to use render????
27589 Roo.factory(e).render(_this.el);
27593 Roo.each(_this.toolbarItems, function(e) {
27594 _this.navgroup.addItem(e);
27598 this.first = this.navgroup.addItem({
27599 tooltip: this.firstText,
27600 cls: "prev btn-outline-secondary",
27601 html : ' <i class="fa fa-step-backward"></i>',
27603 preventDefault: true,
27604 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27607 this.prev = this.navgroup.addItem({
27608 tooltip: this.prevText,
27609 cls: "prev btn-outline-secondary",
27610 html : ' <i class="fa fa-backward"></i>',
27612 preventDefault: true,
27613 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27615 //this.addSeparator();
27618 var field = this.navgroup.addItem( {
27620 cls : 'x-paging-position btn-outline-secondary',
27622 html : this.beforePageText +
27623 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27624 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27627 this.field = field.el.select('input', true).first();
27628 this.field.on("keydown", this.onPagingKeydown, this);
27629 this.field.on("focus", function(){this.dom.select();});
27632 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27633 //this.field.setHeight(18);
27634 //this.addSeparator();
27635 this.next = this.navgroup.addItem({
27636 tooltip: this.nextText,
27637 cls: "next btn-outline-secondary",
27638 html : ' <i class="fa fa-forward"></i>',
27640 preventDefault: true,
27641 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27643 this.last = this.navgroup.addItem({
27644 tooltip: this.lastText,
27645 html : ' <i class="fa fa-step-forward"></i>',
27646 cls: "next btn-outline-secondary",
27648 preventDefault: true,
27649 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27651 //this.addSeparator();
27652 this.loading = this.navgroup.addItem({
27653 tooltip: this.refreshText,
27654 cls: "btn-outline-secondary",
27655 html : ' <i class="fa fa-refresh"></i>',
27656 preventDefault: true,
27657 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27663 updateInfo : function(){
27664 if(this.displayEl){
27665 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27666 var msg = count == 0 ?
27670 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27672 this.displayEl.update(msg);
27677 onLoad : function(ds, r, o)
27679 this.cursor = o.params && o.params.start ? o.params.start : 0;
27681 var d = this.getPageData(),
27686 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27687 this.field.dom.value = ap;
27688 this.first.setDisabled(ap == 1);
27689 this.prev.setDisabled(ap == 1);
27690 this.next.setDisabled(ap == ps);
27691 this.last.setDisabled(ap == ps);
27692 this.loading.enable();
27697 getPageData : function(){
27698 var total = this.ds.getTotalCount();
27701 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27702 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27707 onLoadError : function(){
27708 this.loading.enable();
27712 onPagingKeydown : function(e){
27713 var k = e.getKey();
27714 var d = this.getPageData();
27716 var v = this.field.dom.value, pageNum;
27717 if(!v || isNaN(pageNum = parseInt(v, 10))){
27718 this.field.dom.value = d.activePage;
27721 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27722 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27725 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))
27727 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27728 this.field.dom.value = pageNum;
27729 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27732 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27734 var v = this.field.dom.value, pageNum;
27735 var increment = (e.shiftKey) ? 10 : 1;
27736 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27739 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27740 this.field.dom.value = d.activePage;
27743 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27745 this.field.dom.value = parseInt(v, 10) + increment;
27746 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27747 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27754 beforeLoad : function(){
27756 this.loading.disable();
27761 onClick : function(which){
27770 ds.load({params:{start: 0, limit: this.pageSize}});
27773 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27776 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27779 var total = ds.getTotalCount();
27780 var extra = total % this.pageSize;
27781 var lastStart = extra ? (total - extra) : total-this.pageSize;
27782 ds.load({params:{start: lastStart, limit: this.pageSize}});
27785 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27791 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27792 * @param {Roo.data.Store} store The data store to unbind
27794 unbind : function(ds){
27795 ds.un("beforeload", this.beforeLoad, this);
27796 ds.un("load", this.onLoad, this);
27797 ds.un("loadexception", this.onLoadError, this);
27798 ds.un("remove", this.updateInfo, this);
27799 ds.un("add", this.updateInfo, this);
27800 this.ds = undefined;
27804 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27805 * @param {Roo.data.Store} store The data store to bind
27807 bind : function(ds){
27808 ds.on("beforeload", this.beforeLoad, this);
27809 ds.on("load", this.onLoad, this);
27810 ds.on("loadexception", this.onLoadError, this);
27811 ds.on("remove", this.updateInfo, this);
27812 ds.on("add", this.updateInfo, this);
27823 * @class Roo.bootstrap.MessageBar
27824 * @extends Roo.bootstrap.Component
27825 * Bootstrap MessageBar class
27826 * @cfg {String} html contents of the MessageBar
27827 * @cfg {String} weight (info | success | warning | danger) default info
27828 * @cfg {String} beforeClass insert the bar before the given class
27829 * @cfg {Boolean} closable (true | false) default false
27830 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27833 * Create a new Element
27834 * @param {Object} config The config object
27837 Roo.bootstrap.MessageBar = function(config){
27838 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27841 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27847 beforeClass: 'bootstrap-sticky-wrap',
27849 getAutoCreate : function(){
27853 cls: 'alert alert-dismissable alert-' + this.weight,
27858 html: this.html || ''
27864 cfg.cls += ' alert-messages-fixed';
27878 onRender : function(ct, position)
27880 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27883 var cfg = Roo.apply({}, this.getAutoCreate());
27887 cfg.cls += ' ' + this.cls;
27890 cfg.style = this.style;
27892 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27894 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27897 this.el.select('>button.close').on('click', this.hide, this);
27903 if (!this.rendered) {
27909 this.fireEvent('show', this);
27915 if (!this.rendered) {
27921 this.fireEvent('hide', this);
27924 update : function()
27926 // var e = this.el.dom.firstChild;
27928 // if(this.closable){
27929 // e = e.nextSibling;
27932 // e.data = this.html || '';
27934 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27950 * @class Roo.bootstrap.Graph
27951 * @extends Roo.bootstrap.Component
27952 * Bootstrap Graph class
27956 @cfg {String} graphtype bar | vbar | pie
27957 @cfg {number} g_x coodinator | centre x (pie)
27958 @cfg {number} g_y coodinator | centre y (pie)
27959 @cfg {number} g_r radius (pie)
27960 @cfg {number} g_height height of the chart (respected by all elements in the set)
27961 @cfg {number} g_width width of the chart (respected by all elements in the set)
27962 @cfg {Object} title The title of the chart
27965 -opts (object) options for the chart
27967 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27968 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27970 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.
27971 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27973 o stretch (boolean)
27975 -opts (object) options for the pie
27978 o startAngle (number)
27979 o endAngle (number)
27983 * Create a new Input
27984 * @param {Object} config The config object
27987 Roo.bootstrap.Graph = function(config){
27988 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27994 * The img click event for the img.
27995 * @param {Roo.EventObject} e
28001 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28012 //g_colors: this.colors,
28019 getAutoCreate : function(){
28030 onRender : function(ct,position){
28033 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28035 if (typeof(Raphael) == 'undefined') {
28036 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28040 this.raphael = Raphael(this.el.dom);
28042 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28043 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28044 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28045 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28047 r.text(160, 10, "Single Series Chart").attr(txtattr);
28048 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28049 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28050 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28052 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28053 r.barchart(330, 10, 300, 220, data1);
28054 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28055 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28058 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28059 // r.barchart(30, 30, 560, 250, xdata, {
28060 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28061 // axis : "0 0 1 1",
28062 // axisxlabels : xdata
28063 // //yvalues : cols,
28066 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28068 // this.load(null,xdata,{
28069 // axis : "0 0 1 1",
28070 // axisxlabels : xdata
28075 load : function(graphtype,xdata,opts)
28077 this.raphael.clear();
28079 graphtype = this.graphtype;
28084 var r = this.raphael,
28085 fin = function () {
28086 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28088 fout = function () {
28089 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28091 pfin = function() {
28092 this.sector.stop();
28093 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28096 this.label[0].stop();
28097 this.label[0].attr({ r: 7.5 });
28098 this.label[1].attr({ "font-weight": 800 });
28101 pfout = function() {
28102 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28105 this.label[0].animate({ r: 5 }, 500, "bounce");
28106 this.label[1].attr({ "font-weight": 400 });
28112 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28115 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28118 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28119 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28121 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28128 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28133 setTitle: function(o)
28138 initEvents: function() {
28141 this.el.on('click', this.onClick, this);
28145 onClick : function(e)
28147 Roo.log('img onclick');
28148 this.fireEvent('click', this, e);
28160 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28163 * @class Roo.bootstrap.dash.NumberBox
28164 * @extends Roo.bootstrap.Component
28165 * Bootstrap NumberBox class
28166 * @cfg {String} headline Box headline
28167 * @cfg {String} content Box content
28168 * @cfg {String} icon Box icon
28169 * @cfg {String} footer Footer text
28170 * @cfg {String} fhref Footer href
28173 * Create a new NumberBox
28174 * @param {Object} config The config object
28178 Roo.bootstrap.dash.NumberBox = function(config){
28179 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28183 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28192 getAutoCreate : function(){
28196 cls : 'small-box ',
28204 cls : 'roo-headline',
28205 html : this.headline
28209 cls : 'roo-content',
28210 html : this.content
28224 cls : 'ion ' + this.icon
28233 cls : 'small-box-footer',
28234 href : this.fhref || '#',
28238 cfg.cn.push(footer);
28245 onRender : function(ct,position){
28246 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28253 setHeadline: function (value)
28255 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28258 setFooter: function (value, href)
28260 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28263 this.el.select('a.small-box-footer',true).first().attr('href', href);
28268 setContent: function (value)
28270 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28273 initEvents: function()
28287 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28290 * @class Roo.bootstrap.dash.TabBox
28291 * @extends Roo.bootstrap.Component
28292 * Bootstrap TabBox class
28293 * @cfg {String} title Title of the TabBox
28294 * @cfg {String} icon Icon of the TabBox
28295 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28296 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28299 * Create a new TabBox
28300 * @param {Object} config The config object
28304 Roo.bootstrap.dash.TabBox = function(config){
28305 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28310 * When a pane is added
28311 * @param {Roo.bootstrap.dash.TabPane} pane
28315 * @event activatepane
28316 * When a pane is activated
28317 * @param {Roo.bootstrap.dash.TabPane} pane
28319 "activatepane" : true
28327 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28332 tabScrollable : false,
28334 getChildContainer : function()
28336 return this.el.select('.tab-content', true).first();
28339 getAutoCreate : function(){
28343 cls: 'pull-left header',
28351 cls: 'fa ' + this.icon
28357 cls: 'nav nav-tabs pull-right',
28363 if(this.tabScrollable){
28370 cls: 'nav nav-tabs pull-right',
28381 cls: 'nav-tabs-custom',
28386 cls: 'tab-content no-padding',
28394 initEvents : function()
28396 //Roo.log('add add pane handler');
28397 this.on('addpane', this.onAddPane, this);
28400 * Updates the box title
28401 * @param {String} html to set the title to.
28403 setTitle : function(value)
28405 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28407 onAddPane : function(pane)
28409 this.panes.push(pane);
28410 //Roo.log('addpane');
28412 // tabs are rendere left to right..
28413 if(!this.showtabs){
28417 var ctr = this.el.select('.nav-tabs', true).first();
28420 var existing = ctr.select('.nav-tab',true);
28421 var qty = existing.getCount();;
28424 var tab = ctr.createChild({
28426 cls : 'nav-tab' + (qty ? '' : ' active'),
28434 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28437 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28439 pane.el.addClass('active');
28444 onTabClick : function(ev,un,ob,pane)
28446 //Roo.log('tab - prev default');
28447 ev.preventDefault();
28450 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28451 pane.tab.addClass('active');
28452 //Roo.log(pane.title);
28453 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28454 // technically we should have a deactivate event.. but maybe add later.
28455 // and it should not de-activate the selected tab...
28456 this.fireEvent('activatepane', pane);
28457 pane.el.addClass('active');
28458 pane.fireEvent('activate');
28463 getActivePane : function()
28466 Roo.each(this.panes, function(p) {
28467 if(p.el.hasClass('active')){
28488 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28490 * @class Roo.bootstrap.TabPane
28491 * @extends Roo.bootstrap.Component
28492 * Bootstrap TabPane class
28493 * @cfg {Boolean} active (false | true) Default false
28494 * @cfg {String} title title of panel
28498 * Create a new TabPane
28499 * @param {Object} config The config object
28502 Roo.bootstrap.dash.TabPane = function(config){
28503 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28509 * When a pane is activated
28510 * @param {Roo.bootstrap.dash.TabPane} pane
28517 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28522 // the tabBox that this is attached to.
28525 getAutoCreate : function()
28533 cfg.cls += ' active';
28538 initEvents : function()
28540 //Roo.log('trigger add pane handler');
28541 this.parent().fireEvent('addpane', this)
28545 * Updates the tab title
28546 * @param {String} html to set the title to.
28548 setTitle: function(str)
28554 this.tab.select('a', true).first().dom.innerHTML = str;
28571 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28574 * @class Roo.bootstrap.menu.Menu
28575 * @extends Roo.bootstrap.Component
28576 * Bootstrap Menu class - container for Menu
28577 * @cfg {String} html Text of the menu
28578 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28579 * @cfg {String} icon Font awesome icon
28580 * @cfg {String} pos Menu align to (top | bottom) default bottom
28584 * Create a new Menu
28585 * @param {Object} config The config object
28589 Roo.bootstrap.menu.Menu = function(config){
28590 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28594 * @event beforeshow
28595 * Fires before this menu is displayed
28596 * @param {Roo.bootstrap.menu.Menu} this
28600 * @event beforehide
28601 * Fires before this menu is hidden
28602 * @param {Roo.bootstrap.menu.Menu} this
28607 * Fires after this menu is displayed
28608 * @param {Roo.bootstrap.menu.Menu} this
28613 * Fires after this menu is hidden
28614 * @param {Roo.bootstrap.menu.Menu} this
28619 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28620 * @param {Roo.bootstrap.menu.Menu} this
28621 * @param {Roo.EventObject} e
28628 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28632 weight : 'default',
28637 getChildContainer : function() {
28638 if(this.isSubMenu){
28642 return this.el.select('ul.dropdown-menu', true).first();
28645 getAutoCreate : function()
28650 cls : 'roo-menu-text',
28658 cls : 'fa ' + this.icon
28669 cls : 'dropdown-button btn btn-' + this.weight,
28674 cls : 'dropdown-toggle btn btn-' + this.weight,
28684 cls : 'dropdown-menu'
28690 if(this.pos == 'top'){
28691 cfg.cls += ' dropup';
28694 if(this.isSubMenu){
28697 cls : 'dropdown-menu'
28704 onRender : function(ct, position)
28706 this.isSubMenu = ct.hasClass('dropdown-submenu');
28708 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28711 initEvents : function()
28713 if(this.isSubMenu){
28717 this.hidden = true;
28719 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28720 this.triggerEl.on('click', this.onTriggerPress, this);
28722 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28723 this.buttonEl.on('click', this.onClick, this);
28729 if(this.isSubMenu){
28733 return this.el.select('ul.dropdown-menu', true).first();
28736 onClick : function(e)
28738 this.fireEvent("click", this, e);
28741 onTriggerPress : function(e)
28743 if (this.isVisible()) {
28750 isVisible : function(){
28751 return !this.hidden;
28756 this.fireEvent("beforeshow", this);
28758 this.hidden = false;
28759 this.el.addClass('open');
28761 Roo.get(document).on("mouseup", this.onMouseUp, this);
28763 this.fireEvent("show", this);
28770 this.fireEvent("beforehide", this);
28772 this.hidden = true;
28773 this.el.removeClass('open');
28775 Roo.get(document).un("mouseup", this.onMouseUp);
28777 this.fireEvent("hide", this);
28780 onMouseUp : function()
28794 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28797 * @class Roo.bootstrap.menu.Item
28798 * @extends Roo.bootstrap.Component
28799 * Bootstrap MenuItem class
28800 * @cfg {Boolean} submenu (true | false) default false
28801 * @cfg {String} html text of the item
28802 * @cfg {String} href the link
28803 * @cfg {Boolean} disable (true | false) default false
28804 * @cfg {Boolean} preventDefault (true | false) default true
28805 * @cfg {String} icon Font awesome icon
28806 * @cfg {String} pos Submenu align to (left | right) default right
28810 * Create a new Item
28811 * @param {Object} config The config object
28815 Roo.bootstrap.menu.Item = function(config){
28816 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28820 * Fires when the mouse is hovering over this menu
28821 * @param {Roo.bootstrap.menu.Item} this
28822 * @param {Roo.EventObject} e
28827 * Fires when the mouse exits this menu
28828 * @param {Roo.bootstrap.menu.Item} this
28829 * @param {Roo.EventObject} e
28835 * The raw click event for the entire grid.
28836 * @param {Roo.EventObject} e
28842 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28847 preventDefault: true,
28852 getAutoCreate : function()
28857 cls : 'roo-menu-item-text',
28865 cls : 'fa ' + this.icon
28874 href : this.href || '#',
28881 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28885 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28887 if(this.pos == 'left'){
28888 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28895 initEvents : function()
28897 this.el.on('mouseover', this.onMouseOver, this);
28898 this.el.on('mouseout', this.onMouseOut, this);
28900 this.el.select('a', true).first().on('click', this.onClick, this);
28904 onClick : function(e)
28906 if(this.preventDefault){
28907 e.preventDefault();
28910 this.fireEvent("click", this, e);
28913 onMouseOver : function(e)
28915 if(this.submenu && this.pos == 'left'){
28916 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28919 this.fireEvent("mouseover", this, e);
28922 onMouseOut : function(e)
28924 this.fireEvent("mouseout", this, e);
28936 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28939 * @class Roo.bootstrap.menu.Separator
28940 * @extends Roo.bootstrap.Component
28941 * Bootstrap Separator class
28944 * Create a new Separator
28945 * @param {Object} config The config object
28949 Roo.bootstrap.menu.Separator = function(config){
28950 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28953 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28955 getAutoCreate : function(){
28976 * @class Roo.bootstrap.Tooltip
28977 * Bootstrap Tooltip class
28978 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28979 * to determine which dom element triggers the tooltip.
28981 * It needs to add support for additional attributes like tooltip-position
28984 * Create a new Toolti
28985 * @param {Object} config The config object
28988 Roo.bootstrap.Tooltip = function(config){
28989 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28991 this.alignment = Roo.bootstrap.Tooltip.alignment;
28993 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28994 this.alignment = config.alignment;
28999 Roo.apply(Roo.bootstrap.Tooltip, {
29001 * @function init initialize tooltip monitoring.
29005 currentTip : false,
29006 currentRegion : false,
29012 Roo.get(document).on('mouseover', this.enter ,this);
29013 Roo.get(document).on('mouseout', this.leave, this);
29016 this.currentTip = new Roo.bootstrap.Tooltip();
29019 enter : function(ev)
29021 var dom = ev.getTarget();
29023 //Roo.log(['enter',dom]);
29024 var el = Roo.fly(dom);
29025 if (this.currentEl) {
29027 //Roo.log(this.currentEl);
29028 //Roo.log(this.currentEl.contains(dom));
29029 if (this.currentEl == el) {
29032 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29038 if (this.currentTip.el) {
29039 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29043 if(!el || el.dom == document){
29049 // you can not look for children, as if el is the body.. then everythign is the child..
29050 if (!el.attr('tooltip')) { //
29051 if (!el.select("[tooltip]").elements.length) {
29054 // is the mouse over this child...?
29055 bindEl = el.select("[tooltip]").first();
29056 var xy = ev.getXY();
29057 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29058 //Roo.log("not in region.");
29061 //Roo.log("child element over..");
29064 this.currentEl = bindEl;
29065 this.currentTip.bind(bindEl);
29066 this.currentRegion = Roo.lib.Region.getRegion(dom);
29067 this.currentTip.enter();
29070 leave : function(ev)
29072 var dom = ev.getTarget();
29073 //Roo.log(['leave',dom]);
29074 if (!this.currentEl) {
29079 if (dom != this.currentEl.dom) {
29082 var xy = ev.getXY();
29083 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29086 // only activate leave if mouse cursor is outside... bounding box..
29091 if (this.currentTip) {
29092 this.currentTip.leave();
29094 //Roo.log('clear currentEl');
29095 this.currentEl = false;
29100 'left' : ['r-l', [-2,0], 'right'],
29101 'right' : ['l-r', [2,0], 'left'],
29102 'bottom' : ['t-b', [0,2], 'top'],
29103 'top' : [ 'b-t', [0,-2], 'bottom']
29109 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29114 delay : null, // can be { show : 300 , hide: 500}
29118 hoverState : null, //???
29120 placement : 'bottom',
29124 getAutoCreate : function(){
29131 cls : 'tooltip-arrow arrow'
29134 cls : 'tooltip-inner'
29141 bind : function(el)
29146 initEvents : function()
29148 this.arrowEl = this.el.select('.arrow', true).first();
29149 this.innerEl = this.el.select('.tooltip-inner', true).first();
29152 enter : function () {
29154 if (this.timeout != null) {
29155 clearTimeout(this.timeout);
29158 this.hoverState = 'in';
29159 //Roo.log("enter - show");
29160 if (!this.delay || !this.delay.show) {
29165 this.timeout = setTimeout(function () {
29166 if (_t.hoverState == 'in') {
29169 }, this.delay.show);
29173 clearTimeout(this.timeout);
29175 this.hoverState = 'out';
29176 if (!this.delay || !this.delay.hide) {
29182 this.timeout = setTimeout(function () {
29183 //Roo.log("leave - timeout");
29185 if (_t.hoverState == 'out') {
29187 Roo.bootstrap.Tooltip.currentEl = false;
29192 show : function (msg)
29195 this.render(document.body);
29198 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29200 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29202 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29204 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29205 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29207 var placement = typeof this.placement == 'function' ?
29208 this.placement.call(this, this.el, on_el) :
29211 var autoToken = /\s?auto?\s?/i;
29212 var autoPlace = autoToken.test(placement);
29214 placement = placement.replace(autoToken, '') || 'top';
29218 //this.el.setXY([0,0]);
29220 //this.el.dom.style.display='block';
29222 //this.el.appendTo(on_el);
29224 var p = this.getPosition();
29225 var box = this.el.getBox();
29231 var align = this.alignment[placement];
29233 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29235 if(placement == 'top' || placement == 'bottom'){
29237 placement = 'right';
29240 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29241 placement = 'left';
29244 var scroll = Roo.select('body', true).first().getScroll();
29246 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29250 align = this.alignment[placement];
29252 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29256 this.el.alignTo(this.bindEl, align[0],align[1]);
29257 //var arrow = this.el.select('.arrow',true).first();
29258 //arrow.set(align[2],
29260 this.el.addClass(placement);
29261 this.el.addClass("bs-tooltip-"+ placement);
29263 this.el.addClass('in fade show');
29265 this.hoverState = null;
29267 if (this.el.hasClass('fade')) {
29282 //this.el.setXY([0,0]);
29283 this.el.removeClass(['show', 'in']);
29299 * @class Roo.bootstrap.LocationPicker
29300 * @extends Roo.bootstrap.Component
29301 * Bootstrap LocationPicker class
29302 * @cfg {Number} latitude Position when init default 0
29303 * @cfg {Number} longitude Position when init default 0
29304 * @cfg {Number} zoom default 15
29305 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29306 * @cfg {Boolean} mapTypeControl default false
29307 * @cfg {Boolean} disableDoubleClickZoom default false
29308 * @cfg {Boolean} scrollwheel default true
29309 * @cfg {Boolean} streetViewControl default false
29310 * @cfg {Number} radius default 0
29311 * @cfg {String} locationName
29312 * @cfg {Boolean} draggable default true
29313 * @cfg {Boolean} enableAutocomplete default false
29314 * @cfg {Boolean} enableReverseGeocode default true
29315 * @cfg {String} markerTitle
29318 * Create a new LocationPicker
29319 * @param {Object} config The config object
29323 Roo.bootstrap.LocationPicker = function(config){
29325 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29330 * Fires when the picker initialized.
29331 * @param {Roo.bootstrap.LocationPicker} this
29332 * @param {Google Location} location
29336 * @event positionchanged
29337 * Fires when the picker position changed.
29338 * @param {Roo.bootstrap.LocationPicker} this
29339 * @param {Google Location} location
29341 positionchanged : true,
29344 * Fires when the map resize.
29345 * @param {Roo.bootstrap.LocationPicker} this
29350 * Fires when the map show.
29351 * @param {Roo.bootstrap.LocationPicker} this
29356 * Fires when the map hide.
29357 * @param {Roo.bootstrap.LocationPicker} this
29362 * Fires when click the map.
29363 * @param {Roo.bootstrap.LocationPicker} this
29364 * @param {Map event} e
29368 * @event mapRightClick
29369 * Fires when right click the map.
29370 * @param {Roo.bootstrap.LocationPicker} this
29371 * @param {Map event} e
29373 mapRightClick : true,
29375 * @event markerClick
29376 * Fires when click the marker.
29377 * @param {Roo.bootstrap.LocationPicker} this
29378 * @param {Map event} e
29380 markerClick : true,
29382 * @event markerRightClick
29383 * Fires when right click the marker.
29384 * @param {Roo.bootstrap.LocationPicker} this
29385 * @param {Map event} e
29387 markerRightClick : true,
29389 * @event OverlayViewDraw
29390 * Fires when OverlayView Draw
29391 * @param {Roo.bootstrap.LocationPicker} this
29393 OverlayViewDraw : true,
29395 * @event OverlayViewOnAdd
29396 * Fires when OverlayView Draw
29397 * @param {Roo.bootstrap.LocationPicker} this
29399 OverlayViewOnAdd : true,
29401 * @event OverlayViewOnRemove
29402 * Fires when OverlayView Draw
29403 * @param {Roo.bootstrap.LocationPicker} this
29405 OverlayViewOnRemove : true,
29407 * @event OverlayViewShow
29408 * Fires when OverlayView Draw
29409 * @param {Roo.bootstrap.LocationPicker} this
29410 * @param {Pixel} cpx
29412 OverlayViewShow : true,
29414 * @event OverlayViewHide
29415 * Fires when OverlayView Draw
29416 * @param {Roo.bootstrap.LocationPicker} this
29418 OverlayViewHide : true,
29420 * @event loadexception
29421 * Fires when load google lib failed.
29422 * @param {Roo.bootstrap.LocationPicker} this
29424 loadexception : true
29429 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29431 gMapContext: false,
29437 mapTypeControl: false,
29438 disableDoubleClickZoom: false,
29440 streetViewControl: false,
29444 enableAutocomplete: false,
29445 enableReverseGeocode: true,
29448 getAutoCreate: function()
29453 cls: 'roo-location-picker'
29459 initEvents: function(ct, position)
29461 if(!this.el.getWidth() || this.isApplied()){
29465 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29470 initial: function()
29472 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29473 this.fireEvent('loadexception', this);
29477 if(!this.mapTypeId){
29478 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29481 this.gMapContext = this.GMapContext();
29483 this.initOverlayView();
29485 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29489 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29490 _this.setPosition(_this.gMapContext.marker.position);
29493 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29494 _this.fireEvent('mapClick', this, event);
29498 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29499 _this.fireEvent('mapRightClick', this, event);
29503 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29504 _this.fireEvent('markerClick', this, event);
29508 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29509 _this.fireEvent('markerRightClick', this, event);
29513 this.setPosition(this.gMapContext.location);
29515 this.fireEvent('initial', this, this.gMapContext.location);
29518 initOverlayView: function()
29522 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29526 _this.fireEvent('OverlayViewDraw', _this);
29531 _this.fireEvent('OverlayViewOnAdd', _this);
29534 onRemove: function()
29536 _this.fireEvent('OverlayViewOnRemove', _this);
29539 show: function(cpx)
29541 _this.fireEvent('OverlayViewShow', _this, cpx);
29546 _this.fireEvent('OverlayViewHide', _this);
29552 fromLatLngToContainerPixel: function(event)
29554 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29557 isApplied: function()
29559 return this.getGmapContext() == false ? false : true;
29562 getGmapContext: function()
29564 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29567 GMapContext: function()
29569 var position = new google.maps.LatLng(this.latitude, this.longitude);
29571 var _map = new google.maps.Map(this.el.dom, {
29574 mapTypeId: this.mapTypeId,
29575 mapTypeControl: this.mapTypeControl,
29576 disableDoubleClickZoom: this.disableDoubleClickZoom,
29577 scrollwheel: this.scrollwheel,
29578 streetViewControl: this.streetViewControl,
29579 locationName: this.locationName,
29580 draggable: this.draggable,
29581 enableAutocomplete: this.enableAutocomplete,
29582 enableReverseGeocode: this.enableReverseGeocode
29585 var _marker = new google.maps.Marker({
29586 position: position,
29588 title: this.markerTitle,
29589 draggable: this.draggable
29596 location: position,
29597 radius: this.radius,
29598 locationName: this.locationName,
29599 addressComponents: {
29600 formatted_address: null,
29601 addressLine1: null,
29602 addressLine2: null,
29604 streetNumber: null,
29608 stateOrProvince: null
29611 domContainer: this.el.dom,
29612 geodecoder: new google.maps.Geocoder()
29616 drawCircle: function(center, radius, options)
29618 if (this.gMapContext.circle != null) {
29619 this.gMapContext.circle.setMap(null);
29623 options = Roo.apply({}, options, {
29624 strokeColor: "#0000FF",
29625 strokeOpacity: .35,
29627 fillColor: "#0000FF",
29631 options.map = this.gMapContext.map;
29632 options.radius = radius;
29633 options.center = center;
29634 this.gMapContext.circle = new google.maps.Circle(options);
29635 return this.gMapContext.circle;
29641 setPosition: function(location)
29643 this.gMapContext.location = location;
29644 this.gMapContext.marker.setPosition(location);
29645 this.gMapContext.map.panTo(location);
29646 this.drawCircle(location, this.gMapContext.radius, {});
29650 if (this.gMapContext.settings.enableReverseGeocode) {
29651 this.gMapContext.geodecoder.geocode({
29652 latLng: this.gMapContext.location
29653 }, function(results, status) {
29655 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29656 _this.gMapContext.locationName = results[0].formatted_address;
29657 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29659 _this.fireEvent('positionchanged', this, location);
29666 this.fireEvent('positionchanged', this, location);
29671 google.maps.event.trigger(this.gMapContext.map, "resize");
29673 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29675 this.fireEvent('resize', this);
29678 setPositionByLatLng: function(latitude, longitude)
29680 this.setPosition(new google.maps.LatLng(latitude, longitude));
29683 getCurrentPosition: function()
29686 latitude: this.gMapContext.location.lat(),
29687 longitude: this.gMapContext.location.lng()
29691 getAddressName: function()
29693 return this.gMapContext.locationName;
29696 getAddressComponents: function()
29698 return this.gMapContext.addressComponents;
29701 address_component_from_google_geocode: function(address_components)
29705 for (var i = 0; i < address_components.length; i++) {
29706 var component = address_components[i];
29707 if (component.types.indexOf("postal_code") >= 0) {
29708 result.postalCode = component.short_name;
29709 } else if (component.types.indexOf("street_number") >= 0) {
29710 result.streetNumber = component.short_name;
29711 } else if (component.types.indexOf("route") >= 0) {
29712 result.streetName = component.short_name;
29713 } else if (component.types.indexOf("neighborhood") >= 0) {
29714 result.city = component.short_name;
29715 } else if (component.types.indexOf("locality") >= 0) {
29716 result.city = component.short_name;
29717 } else if (component.types.indexOf("sublocality") >= 0) {
29718 result.district = component.short_name;
29719 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29720 result.stateOrProvince = component.short_name;
29721 } else if (component.types.indexOf("country") >= 0) {
29722 result.country = component.short_name;
29726 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29727 result.addressLine2 = "";
29731 setZoomLevel: function(zoom)
29733 this.gMapContext.map.setZoom(zoom);
29746 this.fireEvent('show', this);
29757 this.fireEvent('hide', this);
29762 Roo.apply(Roo.bootstrap.LocationPicker, {
29764 OverlayView : function(map, options)
29766 options = options || {};
29773 * @class Roo.bootstrap.Alert
29774 * @extends Roo.bootstrap.Component
29775 * Bootstrap Alert class - shows an alert area box
29777 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29778 Enter a valid email address
29781 * @cfg {String} title The title of alert
29782 * @cfg {String} html The content of alert
29783 * @cfg {String} weight ( success | info | warning | danger )
29784 * @cfg {String} faicon font-awesomeicon
29787 * Create a new alert
29788 * @param {Object} config The config object
29792 Roo.bootstrap.Alert = function(config){
29793 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29797 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29804 getAutoCreate : function()
29813 cls : 'roo-alert-icon'
29818 cls : 'roo-alert-title',
29823 cls : 'roo-alert-text',
29830 cfg.cn[0].cls += ' fa ' + this.faicon;
29834 cfg.cls += ' alert-' + this.weight;
29840 initEvents: function()
29842 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29845 setTitle : function(str)
29847 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29850 setText : function(str)
29852 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29855 setWeight : function(weight)
29858 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29861 this.weight = weight;
29863 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29866 setIcon : function(icon)
29869 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29872 this.faicon = icon;
29874 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29895 * @class Roo.bootstrap.UploadCropbox
29896 * @extends Roo.bootstrap.Component
29897 * Bootstrap UploadCropbox class
29898 * @cfg {String} emptyText show when image has been loaded
29899 * @cfg {String} rotateNotify show when image too small to rotate
29900 * @cfg {Number} errorTimeout default 3000
29901 * @cfg {Number} minWidth default 300
29902 * @cfg {Number} minHeight default 300
29903 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29904 * @cfg {Boolean} isDocument (true|false) default false
29905 * @cfg {String} url action url
29906 * @cfg {String} paramName default 'imageUpload'
29907 * @cfg {String} method default POST
29908 * @cfg {Boolean} loadMask (true|false) default true
29909 * @cfg {Boolean} loadingText default 'Loading...'
29912 * Create a new UploadCropbox
29913 * @param {Object} config The config object
29916 Roo.bootstrap.UploadCropbox = function(config){
29917 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29921 * @event beforeselectfile
29922 * Fire before select file
29923 * @param {Roo.bootstrap.UploadCropbox} this
29925 "beforeselectfile" : true,
29928 * Fire after initEvent
29929 * @param {Roo.bootstrap.UploadCropbox} this
29934 * Fire after initEvent
29935 * @param {Roo.bootstrap.UploadCropbox} this
29936 * @param {String} data
29941 * Fire when preparing the file data
29942 * @param {Roo.bootstrap.UploadCropbox} this
29943 * @param {Object} file
29948 * Fire when get exception
29949 * @param {Roo.bootstrap.UploadCropbox} this
29950 * @param {XMLHttpRequest} xhr
29952 "exception" : true,
29954 * @event beforeloadcanvas
29955 * Fire before load the canvas
29956 * @param {Roo.bootstrap.UploadCropbox} this
29957 * @param {String} src
29959 "beforeloadcanvas" : true,
29962 * Fire when trash image
29963 * @param {Roo.bootstrap.UploadCropbox} this
29968 * Fire when download the image
29969 * @param {Roo.bootstrap.UploadCropbox} this
29973 * @event footerbuttonclick
29974 * Fire when footerbuttonclick
29975 * @param {Roo.bootstrap.UploadCropbox} this
29976 * @param {String} type
29978 "footerbuttonclick" : true,
29982 * @param {Roo.bootstrap.UploadCropbox} this
29987 * Fire when rotate the image
29988 * @param {Roo.bootstrap.UploadCropbox} this
29989 * @param {String} pos
29994 * Fire when inspect the file
29995 * @param {Roo.bootstrap.UploadCropbox} this
29996 * @param {Object} file
30001 * Fire when xhr upload the file
30002 * @param {Roo.bootstrap.UploadCropbox} this
30003 * @param {Object} data
30008 * Fire when arrange the file data
30009 * @param {Roo.bootstrap.UploadCropbox} this
30010 * @param {Object} formData
30015 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30018 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30020 emptyText : 'Click to upload image',
30021 rotateNotify : 'Image is too small to rotate',
30022 errorTimeout : 3000,
30036 cropType : 'image/jpeg',
30038 canvasLoaded : false,
30039 isDocument : false,
30041 paramName : 'imageUpload',
30043 loadingText : 'Loading...',
30046 getAutoCreate : function()
30050 cls : 'roo-upload-cropbox',
30054 cls : 'roo-upload-cropbox-selector',
30059 cls : 'roo-upload-cropbox-body',
30060 style : 'cursor:pointer',
30064 cls : 'roo-upload-cropbox-preview'
30068 cls : 'roo-upload-cropbox-thumb'
30072 cls : 'roo-upload-cropbox-empty-notify',
30073 html : this.emptyText
30077 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30078 html : this.rotateNotify
30084 cls : 'roo-upload-cropbox-footer',
30087 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30097 onRender : function(ct, position)
30099 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30101 if (this.buttons.length) {
30103 Roo.each(this.buttons, function(bb) {
30105 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30107 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30113 this.maskEl = this.el;
30117 initEvents : function()
30119 this.urlAPI = (window.createObjectURL && window) ||
30120 (window.URL && URL.revokeObjectURL && URL) ||
30121 (window.webkitURL && webkitURL);
30123 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30124 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30126 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30127 this.selectorEl.hide();
30129 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30130 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30132 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30133 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30134 this.thumbEl.hide();
30136 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30137 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30139 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30140 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30141 this.errorEl.hide();
30143 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30144 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30145 this.footerEl.hide();
30147 this.setThumbBoxSize();
30153 this.fireEvent('initial', this);
30160 window.addEventListener("resize", function() { _this.resize(); } );
30162 this.bodyEl.on('click', this.beforeSelectFile, this);
30165 this.bodyEl.on('touchstart', this.onTouchStart, this);
30166 this.bodyEl.on('touchmove', this.onTouchMove, this);
30167 this.bodyEl.on('touchend', this.onTouchEnd, this);
30171 this.bodyEl.on('mousedown', this.onMouseDown, this);
30172 this.bodyEl.on('mousemove', this.onMouseMove, this);
30173 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30174 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30175 Roo.get(document).on('mouseup', this.onMouseUp, this);
30178 this.selectorEl.on('change', this.onFileSelected, this);
30184 this.baseScale = 1;
30186 this.baseRotate = 1;
30187 this.dragable = false;
30188 this.pinching = false;
30191 this.cropData = false;
30192 this.notifyEl.dom.innerHTML = this.emptyText;
30194 this.selectorEl.dom.value = '';
30198 resize : function()
30200 if(this.fireEvent('resize', this) != false){
30201 this.setThumbBoxPosition();
30202 this.setCanvasPosition();
30206 onFooterButtonClick : function(e, el, o, type)
30209 case 'rotate-left' :
30210 this.onRotateLeft(e);
30212 case 'rotate-right' :
30213 this.onRotateRight(e);
30216 this.beforeSelectFile(e);
30231 this.fireEvent('footerbuttonclick', this, type);
30234 beforeSelectFile : function(e)
30236 e.preventDefault();
30238 if(this.fireEvent('beforeselectfile', this) != false){
30239 this.selectorEl.dom.click();
30243 onFileSelected : function(e)
30245 e.preventDefault();
30247 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30251 var file = this.selectorEl.dom.files[0];
30253 if(this.fireEvent('inspect', this, file) != false){
30254 this.prepare(file);
30259 trash : function(e)
30261 this.fireEvent('trash', this);
30264 download : function(e)
30266 this.fireEvent('download', this);
30269 loadCanvas : function(src)
30271 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30275 this.imageEl = document.createElement('img');
30279 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30281 this.imageEl.src = src;
30285 onLoadCanvas : function()
30287 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30288 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30290 this.bodyEl.un('click', this.beforeSelectFile, this);
30292 this.notifyEl.hide();
30293 this.thumbEl.show();
30294 this.footerEl.show();
30296 this.baseRotateLevel();
30298 if(this.isDocument){
30299 this.setThumbBoxSize();
30302 this.setThumbBoxPosition();
30304 this.baseScaleLevel();
30310 this.canvasLoaded = true;
30313 this.maskEl.unmask();
30318 setCanvasPosition : function()
30320 if(!this.canvasEl){
30324 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30325 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30327 this.previewEl.setLeft(pw);
30328 this.previewEl.setTop(ph);
30332 onMouseDown : function(e)
30336 this.dragable = true;
30337 this.pinching = false;
30339 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30340 this.dragable = false;
30344 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30345 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30349 onMouseMove : function(e)
30353 if(!this.canvasLoaded){
30357 if (!this.dragable){
30361 var minX = Math.ceil(this.thumbEl.getLeft(true));
30362 var minY = Math.ceil(this.thumbEl.getTop(true));
30364 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30365 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30367 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30368 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30370 x = x - this.mouseX;
30371 y = y - this.mouseY;
30373 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30374 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30376 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30377 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30379 this.previewEl.setLeft(bgX);
30380 this.previewEl.setTop(bgY);
30382 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30383 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30386 onMouseUp : function(e)
30390 this.dragable = false;
30393 onMouseWheel : function(e)
30397 this.startScale = this.scale;
30399 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30401 if(!this.zoomable()){
30402 this.scale = this.startScale;
30411 zoomable : function()
30413 var minScale = this.thumbEl.getWidth() / this.minWidth;
30415 if(this.minWidth < this.minHeight){
30416 minScale = this.thumbEl.getHeight() / this.minHeight;
30419 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30420 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30424 (this.rotate == 0 || this.rotate == 180) &&
30426 width > this.imageEl.OriginWidth ||
30427 height > this.imageEl.OriginHeight ||
30428 (width < this.minWidth && height < this.minHeight)
30436 (this.rotate == 90 || this.rotate == 270) &&
30438 width > this.imageEl.OriginWidth ||
30439 height > this.imageEl.OriginHeight ||
30440 (width < this.minHeight && height < this.minWidth)
30447 !this.isDocument &&
30448 (this.rotate == 0 || this.rotate == 180) &&
30450 width < this.minWidth ||
30451 width > this.imageEl.OriginWidth ||
30452 height < this.minHeight ||
30453 height > this.imageEl.OriginHeight
30460 !this.isDocument &&
30461 (this.rotate == 90 || this.rotate == 270) &&
30463 width < this.minHeight ||
30464 width > this.imageEl.OriginWidth ||
30465 height < this.minWidth ||
30466 height > this.imageEl.OriginHeight
30476 onRotateLeft : function(e)
30478 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30480 var minScale = this.thumbEl.getWidth() / this.minWidth;
30482 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30483 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30485 this.startScale = this.scale;
30487 while (this.getScaleLevel() < minScale){
30489 this.scale = this.scale + 1;
30491 if(!this.zoomable()){
30496 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30497 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30502 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30509 this.scale = this.startScale;
30511 this.onRotateFail();
30516 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30518 if(this.isDocument){
30519 this.setThumbBoxSize();
30520 this.setThumbBoxPosition();
30521 this.setCanvasPosition();
30526 this.fireEvent('rotate', this, 'left');
30530 onRotateRight : function(e)
30532 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30534 var minScale = this.thumbEl.getWidth() / this.minWidth;
30536 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30537 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30539 this.startScale = this.scale;
30541 while (this.getScaleLevel() < minScale){
30543 this.scale = this.scale + 1;
30545 if(!this.zoomable()){
30550 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30551 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30556 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30563 this.scale = this.startScale;
30565 this.onRotateFail();
30570 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30572 if(this.isDocument){
30573 this.setThumbBoxSize();
30574 this.setThumbBoxPosition();
30575 this.setCanvasPosition();
30580 this.fireEvent('rotate', this, 'right');
30583 onRotateFail : function()
30585 this.errorEl.show(true);
30589 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30594 this.previewEl.dom.innerHTML = '';
30596 var canvasEl = document.createElement("canvas");
30598 var contextEl = canvasEl.getContext("2d");
30600 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30601 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30602 var center = this.imageEl.OriginWidth / 2;
30604 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30605 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30606 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30607 center = this.imageEl.OriginHeight / 2;
30610 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30612 contextEl.translate(center, center);
30613 contextEl.rotate(this.rotate * Math.PI / 180);
30615 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30617 this.canvasEl = document.createElement("canvas");
30619 this.contextEl = this.canvasEl.getContext("2d");
30621 switch (this.rotate) {
30624 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30625 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30627 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30632 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30633 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30635 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30636 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);
30640 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30645 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30646 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30648 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30649 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);
30653 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);
30658 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30659 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30661 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30662 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30666 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);
30673 this.previewEl.appendChild(this.canvasEl);
30675 this.setCanvasPosition();
30680 if(!this.canvasLoaded){
30684 var imageCanvas = document.createElement("canvas");
30686 var imageContext = imageCanvas.getContext("2d");
30688 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30689 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30691 var center = imageCanvas.width / 2;
30693 imageContext.translate(center, center);
30695 imageContext.rotate(this.rotate * Math.PI / 180);
30697 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30699 var canvas = document.createElement("canvas");
30701 var context = canvas.getContext("2d");
30703 canvas.width = this.minWidth;
30704 canvas.height = this.minHeight;
30706 switch (this.rotate) {
30709 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30710 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30712 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30713 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30715 var targetWidth = this.minWidth - 2 * x;
30716 var targetHeight = this.minHeight - 2 * y;
30720 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30721 scale = targetWidth / width;
30724 if(x > 0 && y == 0){
30725 scale = targetHeight / height;
30728 if(x > 0 && y > 0){
30729 scale = targetWidth / width;
30731 if(width < height){
30732 scale = targetHeight / height;
30736 context.scale(scale, scale);
30738 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30739 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30741 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30742 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30744 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30749 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30750 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30752 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30753 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30755 var targetWidth = this.minWidth - 2 * x;
30756 var targetHeight = this.minHeight - 2 * y;
30760 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30761 scale = targetWidth / width;
30764 if(x > 0 && y == 0){
30765 scale = targetHeight / height;
30768 if(x > 0 && y > 0){
30769 scale = targetWidth / width;
30771 if(width < height){
30772 scale = targetHeight / height;
30776 context.scale(scale, scale);
30778 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30779 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30781 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30782 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30784 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30786 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30791 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30792 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30794 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30795 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30797 var targetWidth = this.minWidth - 2 * x;
30798 var targetHeight = this.minHeight - 2 * y;
30802 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30803 scale = targetWidth / width;
30806 if(x > 0 && y == 0){
30807 scale = targetHeight / height;
30810 if(x > 0 && y > 0){
30811 scale = targetWidth / width;
30813 if(width < height){
30814 scale = targetHeight / height;
30818 context.scale(scale, scale);
30820 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30821 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30823 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30824 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30826 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30827 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30829 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30834 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30835 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30837 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30838 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30840 var targetWidth = this.minWidth - 2 * x;
30841 var targetHeight = this.minHeight - 2 * y;
30845 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30846 scale = targetWidth / width;
30849 if(x > 0 && y == 0){
30850 scale = targetHeight / height;
30853 if(x > 0 && y > 0){
30854 scale = targetWidth / width;
30856 if(width < height){
30857 scale = targetHeight / height;
30861 context.scale(scale, scale);
30863 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30864 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30866 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30867 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30869 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30871 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30878 this.cropData = canvas.toDataURL(this.cropType);
30880 if(this.fireEvent('crop', this, this.cropData) !== false){
30881 this.process(this.file, this.cropData);
30888 setThumbBoxSize : function()
30892 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30893 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30894 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30896 this.minWidth = width;
30897 this.minHeight = height;
30899 if(this.rotate == 90 || this.rotate == 270){
30900 this.minWidth = height;
30901 this.minHeight = width;
30906 width = Math.ceil(this.minWidth * height / this.minHeight);
30908 if(this.minWidth > this.minHeight){
30910 height = Math.ceil(this.minHeight * width / this.minWidth);
30913 this.thumbEl.setStyle({
30914 width : width + 'px',
30915 height : height + 'px'
30922 setThumbBoxPosition : function()
30924 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30925 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30927 this.thumbEl.setLeft(x);
30928 this.thumbEl.setTop(y);
30932 baseRotateLevel : function()
30934 this.baseRotate = 1;
30937 typeof(this.exif) != 'undefined' &&
30938 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30939 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30941 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30944 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30948 baseScaleLevel : function()
30952 if(this.isDocument){
30954 if(this.baseRotate == 6 || this.baseRotate == 8){
30956 height = this.thumbEl.getHeight();
30957 this.baseScale = height / this.imageEl.OriginWidth;
30959 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30960 width = this.thumbEl.getWidth();
30961 this.baseScale = width / this.imageEl.OriginHeight;
30967 height = this.thumbEl.getHeight();
30968 this.baseScale = height / this.imageEl.OriginHeight;
30970 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30971 width = this.thumbEl.getWidth();
30972 this.baseScale = width / this.imageEl.OriginWidth;
30978 if(this.baseRotate == 6 || this.baseRotate == 8){
30980 width = this.thumbEl.getHeight();
30981 this.baseScale = width / this.imageEl.OriginHeight;
30983 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30984 height = this.thumbEl.getWidth();
30985 this.baseScale = height / this.imageEl.OriginHeight;
30988 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30989 height = this.thumbEl.getWidth();
30990 this.baseScale = height / this.imageEl.OriginHeight;
30992 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30993 width = this.thumbEl.getHeight();
30994 this.baseScale = width / this.imageEl.OriginWidth;
31001 width = this.thumbEl.getWidth();
31002 this.baseScale = width / this.imageEl.OriginWidth;
31004 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31005 height = this.thumbEl.getHeight();
31006 this.baseScale = height / this.imageEl.OriginHeight;
31009 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31011 height = this.thumbEl.getHeight();
31012 this.baseScale = height / this.imageEl.OriginHeight;
31014 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31015 width = this.thumbEl.getWidth();
31016 this.baseScale = width / this.imageEl.OriginWidth;
31024 getScaleLevel : function()
31026 return this.baseScale * Math.pow(1.1, this.scale);
31029 onTouchStart : function(e)
31031 if(!this.canvasLoaded){
31032 this.beforeSelectFile(e);
31036 var touches = e.browserEvent.touches;
31042 if(touches.length == 1){
31043 this.onMouseDown(e);
31047 if(touches.length != 2){
31053 for(var i = 0, finger; finger = touches[i]; i++){
31054 coords.push(finger.pageX, finger.pageY);
31057 var x = Math.pow(coords[0] - coords[2], 2);
31058 var y = Math.pow(coords[1] - coords[3], 2);
31060 this.startDistance = Math.sqrt(x + y);
31062 this.startScale = this.scale;
31064 this.pinching = true;
31065 this.dragable = false;
31069 onTouchMove : function(e)
31071 if(!this.pinching && !this.dragable){
31075 var touches = e.browserEvent.touches;
31082 this.onMouseMove(e);
31088 for(var i = 0, finger; finger = touches[i]; i++){
31089 coords.push(finger.pageX, finger.pageY);
31092 var x = Math.pow(coords[0] - coords[2], 2);
31093 var y = Math.pow(coords[1] - coords[3], 2);
31095 this.endDistance = Math.sqrt(x + y);
31097 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31099 if(!this.zoomable()){
31100 this.scale = this.startScale;
31108 onTouchEnd : function(e)
31110 this.pinching = false;
31111 this.dragable = false;
31115 process : function(file, crop)
31118 this.maskEl.mask(this.loadingText);
31121 this.xhr = new XMLHttpRequest();
31123 file.xhr = this.xhr;
31125 this.xhr.open(this.method, this.url, true);
31128 "Accept": "application/json",
31129 "Cache-Control": "no-cache",
31130 "X-Requested-With": "XMLHttpRequest"
31133 for (var headerName in headers) {
31134 var headerValue = headers[headerName];
31136 this.xhr.setRequestHeader(headerName, headerValue);
31142 this.xhr.onload = function()
31144 _this.xhrOnLoad(_this.xhr);
31147 this.xhr.onerror = function()
31149 _this.xhrOnError(_this.xhr);
31152 var formData = new FormData();
31154 formData.append('returnHTML', 'NO');
31157 formData.append('crop', crop);
31160 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31161 formData.append(this.paramName, file, file.name);
31164 if(typeof(file.filename) != 'undefined'){
31165 formData.append('filename', file.filename);
31168 if(typeof(file.mimetype) != 'undefined'){
31169 formData.append('mimetype', file.mimetype);
31172 if(this.fireEvent('arrange', this, formData) != false){
31173 this.xhr.send(formData);
31177 xhrOnLoad : function(xhr)
31180 this.maskEl.unmask();
31183 if (xhr.readyState !== 4) {
31184 this.fireEvent('exception', this, xhr);
31188 var response = Roo.decode(xhr.responseText);
31190 if(!response.success){
31191 this.fireEvent('exception', this, xhr);
31195 var response = Roo.decode(xhr.responseText);
31197 this.fireEvent('upload', this, response);
31201 xhrOnError : function()
31204 this.maskEl.unmask();
31207 Roo.log('xhr on error');
31209 var response = Roo.decode(xhr.responseText);
31215 prepare : function(file)
31218 this.maskEl.mask(this.loadingText);
31224 if(typeof(file) === 'string'){
31225 this.loadCanvas(file);
31229 if(!file || !this.urlAPI){
31234 this.cropType = file.type;
31238 if(this.fireEvent('prepare', this, this.file) != false){
31240 var reader = new FileReader();
31242 reader.onload = function (e) {
31243 if (e.target.error) {
31244 Roo.log(e.target.error);
31248 var buffer = e.target.result,
31249 dataView = new DataView(buffer),
31251 maxOffset = dataView.byteLength - 4,
31255 if (dataView.getUint16(0) === 0xffd8) {
31256 while (offset < maxOffset) {
31257 markerBytes = dataView.getUint16(offset);
31259 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31260 markerLength = dataView.getUint16(offset + 2) + 2;
31261 if (offset + markerLength > dataView.byteLength) {
31262 Roo.log('Invalid meta data: Invalid segment size.');
31266 if(markerBytes == 0xffe1){
31267 _this.parseExifData(
31274 offset += markerLength;
31284 var url = _this.urlAPI.createObjectURL(_this.file);
31286 _this.loadCanvas(url);
31291 reader.readAsArrayBuffer(this.file);
31297 parseExifData : function(dataView, offset, length)
31299 var tiffOffset = offset + 10,
31303 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31304 // No Exif data, might be XMP data instead
31308 // Check for the ASCII code for "Exif" (0x45786966):
31309 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31310 // No Exif data, might be XMP data instead
31313 if (tiffOffset + 8 > dataView.byteLength) {
31314 Roo.log('Invalid Exif data: Invalid segment size.');
31317 // Check for the two null bytes:
31318 if (dataView.getUint16(offset + 8) !== 0x0000) {
31319 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31322 // Check the byte alignment:
31323 switch (dataView.getUint16(tiffOffset)) {
31325 littleEndian = true;
31328 littleEndian = false;
31331 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31334 // Check for the TIFF tag marker (0x002A):
31335 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31336 Roo.log('Invalid Exif data: Missing TIFF marker.');
31339 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31340 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31342 this.parseExifTags(
31345 tiffOffset + dirOffset,
31350 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31355 if (dirOffset + 6 > dataView.byteLength) {
31356 Roo.log('Invalid Exif data: Invalid directory offset.');
31359 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31360 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31361 if (dirEndOffset + 4 > dataView.byteLength) {
31362 Roo.log('Invalid Exif data: Invalid directory size.');
31365 for (i = 0; i < tagsNumber; i += 1) {
31369 dirOffset + 2 + 12 * i, // tag offset
31373 // Return the offset to the next directory:
31374 return dataView.getUint32(dirEndOffset, littleEndian);
31377 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31379 var tag = dataView.getUint16(offset, littleEndian);
31381 this.exif[tag] = this.getExifValue(
31385 dataView.getUint16(offset + 2, littleEndian), // tag type
31386 dataView.getUint32(offset + 4, littleEndian), // tag length
31391 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31393 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31402 Roo.log('Invalid Exif data: Invalid tag type.');
31406 tagSize = tagType.size * length;
31407 // Determine if the value is contained in the dataOffset bytes,
31408 // or if the value at the dataOffset is a pointer to the actual data:
31409 dataOffset = tagSize > 4 ?
31410 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31411 if (dataOffset + tagSize > dataView.byteLength) {
31412 Roo.log('Invalid Exif data: Invalid data offset.');
31415 if (length === 1) {
31416 return tagType.getValue(dataView, dataOffset, littleEndian);
31419 for (i = 0; i < length; i += 1) {
31420 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31423 if (tagType.ascii) {
31425 // Concatenate the chars:
31426 for (i = 0; i < values.length; i += 1) {
31428 // Ignore the terminating NULL byte(s):
31429 if (c === '\u0000') {
31441 Roo.apply(Roo.bootstrap.UploadCropbox, {
31443 'Orientation': 0x0112
31447 1: 0, //'top-left',
31449 3: 180, //'bottom-right',
31450 // 4: 'bottom-left',
31452 6: 90, //'right-top',
31453 // 7: 'right-bottom',
31454 8: 270 //'left-bottom'
31458 // byte, 8-bit unsigned int:
31460 getValue: function (dataView, dataOffset) {
31461 return dataView.getUint8(dataOffset);
31465 // ascii, 8-bit byte:
31467 getValue: function (dataView, dataOffset) {
31468 return String.fromCharCode(dataView.getUint8(dataOffset));
31473 // short, 16 bit int:
31475 getValue: function (dataView, dataOffset, littleEndian) {
31476 return dataView.getUint16(dataOffset, littleEndian);
31480 // long, 32 bit int:
31482 getValue: function (dataView, dataOffset, littleEndian) {
31483 return dataView.getUint32(dataOffset, littleEndian);
31487 // rational = two long values, first is numerator, second is denominator:
31489 getValue: function (dataView, dataOffset, littleEndian) {
31490 return dataView.getUint32(dataOffset, littleEndian) /
31491 dataView.getUint32(dataOffset + 4, littleEndian);
31495 // slong, 32 bit signed int:
31497 getValue: function (dataView, dataOffset, littleEndian) {
31498 return dataView.getInt32(dataOffset, littleEndian);
31502 // srational, two slongs, first is numerator, second is denominator:
31504 getValue: function (dataView, dataOffset, littleEndian) {
31505 return dataView.getInt32(dataOffset, littleEndian) /
31506 dataView.getInt32(dataOffset + 4, littleEndian);
31516 cls : 'btn-group roo-upload-cropbox-rotate-left',
31517 action : 'rotate-left',
31521 cls : 'btn btn-default',
31522 html : '<i class="fa fa-undo"></i>'
31528 cls : 'btn-group roo-upload-cropbox-picture',
31529 action : 'picture',
31533 cls : 'btn btn-default',
31534 html : '<i class="fa fa-picture-o"></i>'
31540 cls : 'btn-group roo-upload-cropbox-rotate-right',
31541 action : 'rotate-right',
31545 cls : 'btn btn-default',
31546 html : '<i class="fa fa-repeat"></i>'
31554 cls : 'btn-group roo-upload-cropbox-rotate-left',
31555 action : 'rotate-left',
31559 cls : 'btn btn-default',
31560 html : '<i class="fa fa-undo"></i>'
31566 cls : 'btn-group roo-upload-cropbox-download',
31567 action : 'download',
31571 cls : 'btn btn-default',
31572 html : '<i class="fa fa-download"></i>'
31578 cls : 'btn-group roo-upload-cropbox-crop',
31583 cls : 'btn btn-default',
31584 html : '<i class="fa fa-crop"></i>'
31590 cls : 'btn-group roo-upload-cropbox-trash',
31595 cls : 'btn btn-default',
31596 html : '<i class="fa fa-trash"></i>'
31602 cls : 'btn-group roo-upload-cropbox-rotate-right',
31603 action : 'rotate-right',
31607 cls : 'btn btn-default',
31608 html : '<i class="fa fa-repeat"></i>'
31616 cls : 'btn-group roo-upload-cropbox-rotate-left',
31617 action : 'rotate-left',
31621 cls : 'btn btn-default',
31622 html : '<i class="fa fa-undo"></i>'
31628 cls : 'btn-group roo-upload-cropbox-rotate-right',
31629 action : 'rotate-right',
31633 cls : 'btn btn-default',
31634 html : '<i class="fa fa-repeat"></i>'
31647 * @class Roo.bootstrap.DocumentManager
31648 * @extends Roo.bootstrap.Component
31649 * Bootstrap DocumentManager class
31650 * @cfg {String} paramName default 'imageUpload'
31651 * @cfg {String} toolTipName default 'filename'
31652 * @cfg {String} method default POST
31653 * @cfg {String} url action url
31654 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31655 * @cfg {Boolean} multiple multiple upload default true
31656 * @cfg {Number} thumbSize default 300
31657 * @cfg {String} fieldLabel
31658 * @cfg {Number} labelWidth default 4
31659 * @cfg {String} labelAlign (left|top) default left
31660 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31661 * @cfg {Number} labellg set the width of label (1-12)
31662 * @cfg {Number} labelmd set the width of label (1-12)
31663 * @cfg {Number} labelsm set the width of label (1-12)
31664 * @cfg {Number} labelxs set the width of label (1-12)
31667 * Create a new DocumentManager
31668 * @param {Object} config The config object
31671 Roo.bootstrap.DocumentManager = function(config){
31672 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31675 this.delegates = [];
31680 * Fire when initial the DocumentManager
31681 * @param {Roo.bootstrap.DocumentManager} this
31686 * inspect selected file
31687 * @param {Roo.bootstrap.DocumentManager} this
31688 * @param {File} file
31693 * Fire when xhr load exception
31694 * @param {Roo.bootstrap.DocumentManager} this
31695 * @param {XMLHttpRequest} xhr
31697 "exception" : true,
31699 * @event afterupload
31700 * Fire when xhr load exception
31701 * @param {Roo.bootstrap.DocumentManager} this
31702 * @param {XMLHttpRequest} xhr
31704 "afterupload" : true,
31707 * prepare the form data
31708 * @param {Roo.bootstrap.DocumentManager} this
31709 * @param {Object} formData
31714 * Fire when remove the file
31715 * @param {Roo.bootstrap.DocumentManager} this
31716 * @param {Object} file
31721 * Fire after refresh the file
31722 * @param {Roo.bootstrap.DocumentManager} this
31727 * Fire after click the image
31728 * @param {Roo.bootstrap.DocumentManager} this
31729 * @param {Object} file
31734 * Fire when upload a image and editable set to true
31735 * @param {Roo.bootstrap.DocumentManager} this
31736 * @param {Object} file
31740 * @event beforeselectfile
31741 * Fire before select file
31742 * @param {Roo.bootstrap.DocumentManager} this
31744 "beforeselectfile" : true,
31747 * Fire before process file
31748 * @param {Roo.bootstrap.DocumentManager} this
31749 * @param {Object} file
31753 * @event previewrendered
31754 * Fire when preview rendered
31755 * @param {Roo.bootstrap.DocumentManager} this
31756 * @param {Object} file
31758 "previewrendered" : true,
31761 "previewResize" : true
31766 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31775 paramName : 'imageUpload',
31776 toolTipName : 'filename',
31779 labelAlign : 'left',
31789 getAutoCreate : function()
31791 var managerWidget = {
31793 cls : 'roo-document-manager',
31797 cls : 'roo-document-manager-selector',
31802 cls : 'roo-document-manager-uploader',
31806 cls : 'roo-document-manager-upload-btn',
31807 html : '<i class="fa fa-plus"></i>'
31818 cls : 'column col-md-12',
31823 if(this.fieldLabel.length){
31828 cls : 'column col-md-12',
31829 html : this.fieldLabel
31833 cls : 'column col-md-12',
31838 if(this.labelAlign == 'left'){
31843 html : this.fieldLabel
31852 if(this.labelWidth > 12){
31853 content[0].style = "width: " + this.labelWidth + 'px';
31856 if(this.labelWidth < 13 && this.labelmd == 0){
31857 this.labelmd = this.labelWidth;
31860 if(this.labellg > 0){
31861 content[0].cls += ' col-lg-' + this.labellg;
31862 content[1].cls += ' col-lg-' + (12 - this.labellg);
31865 if(this.labelmd > 0){
31866 content[0].cls += ' col-md-' + this.labelmd;
31867 content[1].cls += ' col-md-' + (12 - this.labelmd);
31870 if(this.labelsm > 0){
31871 content[0].cls += ' col-sm-' + this.labelsm;
31872 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31875 if(this.labelxs > 0){
31876 content[0].cls += ' col-xs-' + this.labelxs;
31877 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31885 cls : 'row clearfix',
31893 initEvents : function()
31895 this.managerEl = this.el.select('.roo-document-manager', true).first();
31896 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31898 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31899 this.selectorEl.hide();
31902 this.selectorEl.attr('multiple', 'multiple');
31905 this.selectorEl.on('change', this.onFileSelected, this);
31907 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31908 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31910 this.uploader.on('click', this.onUploaderClick, this);
31912 this.renderProgressDialog();
31916 window.addEventListener("resize", function() { _this.refresh(); } );
31918 this.fireEvent('initial', this);
31921 renderProgressDialog : function()
31925 this.progressDialog = new Roo.bootstrap.Modal({
31926 cls : 'roo-document-manager-progress-dialog',
31927 allow_close : false,
31938 btnclick : function() {
31939 _this.uploadCancel();
31945 this.progressDialog.render(Roo.get(document.body));
31947 this.progress = new Roo.bootstrap.Progress({
31948 cls : 'roo-document-manager-progress',
31953 this.progress.render(this.progressDialog.getChildContainer());
31955 this.progressBar = new Roo.bootstrap.ProgressBar({
31956 cls : 'roo-document-manager-progress-bar',
31959 aria_valuemax : 12,
31963 this.progressBar.render(this.progress.getChildContainer());
31966 onUploaderClick : function(e)
31968 e.preventDefault();
31970 if(this.fireEvent('beforeselectfile', this) != false){
31971 this.selectorEl.dom.click();
31976 onFileSelected : function(e)
31978 e.preventDefault();
31980 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31984 Roo.each(this.selectorEl.dom.files, function(file){
31985 if(this.fireEvent('inspect', this, file) != false){
31986 this.files.push(file);
31996 this.selectorEl.dom.value = '';
31998 if(!this.files || !this.files.length){
32002 if(this.boxes > 0 && this.files.length > this.boxes){
32003 this.files = this.files.slice(0, this.boxes);
32006 this.uploader.show();
32008 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32009 this.uploader.hide();
32018 Roo.each(this.files, function(file){
32020 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32021 var f = this.renderPreview(file);
32026 if(file.type.indexOf('image') != -1){
32027 this.delegates.push(
32029 _this.process(file);
32030 }).createDelegate(this)
32038 _this.process(file);
32039 }).createDelegate(this)
32044 this.files = files;
32046 this.delegates = this.delegates.concat(docs);
32048 if(!this.delegates.length){
32053 this.progressBar.aria_valuemax = this.delegates.length;
32060 arrange : function()
32062 if(!this.delegates.length){
32063 this.progressDialog.hide();
32068 var delegate = this.delegates.shift();
32070 this.progressDialog.show();
32072 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32074 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32079 refresh : function()
32081 this.uploader.show();
32083 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32084 this.uploader.hide();
32087 Roo.isTouch ? this.closable(false) : this.closable(true);
32089 this.fireEvent('refresh', this);
32092 onRemove : function(e, el, o)
32094 e.preventDefault();
32096 this.fireEvent('remove', this, o);
32100 remove : function(o)
32104 Roo.each(this.files, function(file){
32105 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32114 this.files = files;
32121 Roo.each(this.files, function(file){
32126 file.target.remove();
32135 onClick : function(e, el, o)
32137 e.preventDefault();
32139 this.fireEvent('click', this, o);
32143 closable : function(closable)
32145 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32147 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32159 xhrOnLoad : function(xhr)
32161 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32165 if (xhr.readyState !== 4) {
32167 this.fireEvent('exception', this, xhr);
32171 var response = Roo.decode(xhr.responseText);
32173 if(!response.success){
32175 this.fireEvent('exception', this, xhr);
32179 var file = this.renderPreview(response.data);
32181 this.files.push(file);
32185 this.fireEvent('afterupload', this, xhr);
32189 xhrOnError : function(xhr)
32191 Roo.log('xhr on error');
32193 var response = Roo.decode(xhr.responseText);
32200 process : function(file)
32202 if(this.fireEvent('process', this, file) !== false){
32203 if(this.editable && file.type.indexOf('image') != -1){
32204 this.fireEvent('edit', this, file);
32208 this.uploadStart(file, false);
32215 uploadStart : function(file, crop)
32217 this.xhr = new XMLHttpRequest();
32219 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32224 file.xhr = this.xhr;
32226 this.managerEl.createChild({
32228 cls : 'roo-document-manager-loading',
32232 tooltip : file.name,
32233 cls : 'roo-document-manager-thumb',
32234 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32240 this.xhr.open(this.method, this.url, true);
32243 "Accept": "application/json",
32244 "Cache-Control": "no-cache",
32245 "X-Requested-With": "XMLHttpRequest"
32248 for (var headerName in headers) {
32249 var headerValue = headers[headerName];
32251 this.xhr.setRequestHeader(headerName, headerValue);
32257 this.xhr.onload = function()
32259 _this.xhrOnLoad(_this.xhr);
32262 this.xhr.onerror = function()
32264 _this.xhrOnError(_this.xhr);
32267 var formData = new FormData();
32269 formData.append('returnHTML', 'NO');
32272 formData.append('crop', crop);
32275 formData.append(this.paramName, file, file.name);
32282 if(this.fireEvent('prepare', this, formData, options) != false){
32284 if(options.manually){
32288 this.xhr.send(formData);
32292 this.uploadCancel();
32295 uploadCancel : function()
32301 this.delegates = [];
32303 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32310 renderPreview : function(file)
32312 if(typeof(file.target) != 'undefined' && file.target){
32316 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32318 var previewEl = this.managerEl.createChild({
32320 cls : 'roo-document-manager-preview',
32324 tooltip : file[this.toolTipName],
32325 cls : 'roo-document-manager-thumb',
32326 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32331 html : '<i class="fa fa-times-circle"></i>'
32336 var close = previewEl.select('button.close', true).first();
32338 close.on('click', this.onRemove, this, file);
32340 file.target = previewEl;
32342 var image = previewEl.select('img', true).first();
32346 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32348 image.on('click', this.onClick, this, file);
32350 this.fireEvent('previewrendered', this, file);
32356 onPreviewLoad : function(file, image)
32358 if(typeof(file.target) == 'undefined' || !file.target){
32362 var width = image.dom.naturalWidth || image.dom.width;
32363 var height = image.dom.naturalHeight || image.dom.height;
32365 if(!this.previewResize) {
32369 if(width > height){
32370 file.target.addClass('wide');
32374 file.target.addClass('tall');
32379 uploadFromSource : function(file, crop)
32381 this.xhr = new XMLHttpRequest();
32383 this.managerEl.createChild({
32385 cls : 'roo-document-manager-loading',
32389 tooltip : file.name,
32390 cls : 'roo-document-manager-thumb',
32391 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32397 this.xhr.open(this.method, this.url, true);
32400 "Accept": "application/json",
32401 "Cache-Control": "no-cache",
32402 "X-Requested-With": "XMLHttpRequest"
32405 for (var headerName in headers) {
32406 var headerValue = headers[headerName];
32408 this.xhr.setRequestHeader(headerName, headerValue);
32414 this.xhr.onload = function()
32416 _this.xhrOnLoad(_this.xhr);
32419 this.xhr.onerror = function()
32421 _this.xhrOnError(_this.xhr);
32424 var formData = new FormData();
32426 formData.append('returnHTML', 'NO');
32428 formData.append('crop', crop);
32430 if(typeof(file.filename) != 'undefined'){
32431 formData.append('filename', file.filename);
32434 if(typeof(file.mimetype) != 'undefined'){
32435 formData.append('mimetype', file.mimetype);
32440 if(this.fireEvent('prepare', this, formData) != false){
32441 this.xhr.send(formData);
32451 * @class Roo.bootstrap.DocumentViewer
32452 * @extends Roo.bootstrap.Component
32453 * Bootstrap DocumentViewer class
32454 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32455 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32458 * Create a new DocumentViewer
32459 * @param {Object} config The config object
32462 Roo.bootstrap.DocumentViewer = function(config){
32463 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32468 * Fire after initEvent
32469 * @param {Roo.bootstrap.DocumentViewer} this
32475 * @param {Roo.bootstrap.DocumentViewer} this
32480 * Fire after download button
32481 * @param {Roo.bootstrap.DocumentViewer} this
32486 * Fire after trash button
32487 * @param {Roo.bootstrap.DocumentViewer} this
32494 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32496 showDownload : true,
32500 getAutoCreate : function()
32504 cls : 'roo-document-viewer',
32508 cls : 'roo-document-viewer-body',
32512 cls : 'roo-document-viewer-thumb',
32516 cls : 'roo-document-viewer-image'
32524 cls : 'roo-document-viewer-footer',
32527 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32531 cls : 'btn-group roo-document-viewer-download',
32535 cls : 'btn btn-default',
32536 html : '<i class="fa fa-download"></i>'
32542 cls : 'btn-group roo-document-viewer-trash',
32546 cls : 'btn btn-default',
32547 html : '<i class="fa fa-trash"></i>'
32560 initEvents : function()
32562 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32563 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32565 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32566 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32568 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32569 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32571 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32572 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32574 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32575 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32577 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32578 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32580 this.bodyEl.on('click', this.onClick, this);
32581 this.downloadBtn.on('click', this.onDownload, this);
32582 this.trashBtn.on('click', this.onTrash, this);
32584 this.downloadBtn.hide();
32585 this.trashBtn.hide();
32587 if(this.showDownload){
32588 this.downloadBtn.show();
32591 if(this.showTrash){
32592 this.trashBtn.show();
32595 if(!this.showDownload && !this.showTrash) {
32596 this.footerEl.hide();
32601 initial : function()
32603 this.fireEvent('initial', this);
32607 onClick : function(e)
32609 e.preventDefault();
32611 this.fireEvent('click', this);
32614 onDownload : function(e)
32616 e.preventDefault();
32618 this.fireEvent('download', this);
32621 onTrash : function(e)
32623 e.preventDefault();
32625 this.fireEvent('trash', this);
32637 * @class Roo.bootstrap.NavProgressBar
32638 * @extends Roo.bootstrap.Component
32639 * Bootstrap NavProgressBar class
32642 * Create a new nav progress bar
32643 * @param {Object} config The config object
32646 Roo.bootstrap.NavProgressBar = function(config){
32647 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32649 this.bullets = this.bullets || [];
32651 // Roo.bootstrap.NavProgressBar.register(this);
32655 * Fires when the active item changes
32656 * @param {Roo.bootstrap.NavProgressBar} this
32657 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32658 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32665 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32670 getAutoCreate : function()
32672 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32676 cls : 'roo-navigation-bar-group',
32680 cls : 'roo-navigation-top-bar'
32684 cls : 'roo-navigation-bullets-bar',
32688 cls : 'roo-navigation-bar'
32695 cls : 'roo-navigation-bottom-bar'
32705 initEvents: function()
32710 onRender : function(ct, position)
32712 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32714 if(this.bullets.length){
32715 Roo.each(this.bullets, function(b){
32724 addItem : function(cfg)
32726 var item = new Roo.bootstrap.NavProgressItem(cfg);
32728 item.parentId = this.id;
32729 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32732 var top = new Roo.bootstrap.Element({
32734 cls : 'roo-navigation-bar-text'
32737 var bottom = new Roo.bootstrap.Element({
32739 cls : 'roo-navigation-bar-text'
32742 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32743 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32745 var topText = new Roo.bootstrap.Element({
32747 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32750 var bottomText = new Roo.bootstrap.Element({
32752 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32755 topText.onRender(top.el, null);
32756 bottomText.onRender(bottom.el, null);
32759 item.bottomEl = bottom;
32762 this.barItems.push(item);
32767 getActive : function()
32769 var active = false;
32771 Roo.each(this.barItems, function(v){
32773 if (!v.isActive()) {
32785 setActiveItem : function(item)
32789 Roo.each(this.barItems, function(v){
32790 if (v.rid == item.rid) {
32794 if (v.isActive()) {
32795 v.setActive(false);
32800 item.setActive(true);
32802 this.fireEvent('changed', this, item, prev);
32805 getBarItem: function(rid)
32809 Roo.each(this.barItems, function(e) {
32810 if (e.rid != rid) {
32821 indexOfItem : function(item)
32825 Roo.each(this.barItems, function(v, i){
32827 if (v.rid != item.rid) {
32838 setActiveNext : function()
32840 var i = this.indexOfItem(this.getActive());
32842 if (i > this.barItems.length) {
32846 this.setActiveItem(this.barItems[i+1]);
32849 setActivePrev : function()
32851 var i = this.indexOfItem(this.getActive());
32857 this.setActiveItem(this.barItems[i-1]);
32860 format : function()
32862 if(!this.barItems.length){
32866 var width = 100 / this.barItems.length;
32868 Roo.each(this.barItems, function(i){
32869 i.el.setStyle('width', width + '%');
32870 i.topEl.el.setStyle('width', width + '%');
32871 i.bottomEl.el.setStyle('width', width + '%');
32880 * Nav Progress Item
32885 * @class Roo.bootstrap.NavProgressItem
32886 * @extends Roo.bootstrap.Component
32887 * Bootstrap NavProgressItem class
32888 * @cfg {String} rid the reference id
32889 * @cfg {Boolean} active (true|false) Is item active default false
32890 * @cfg {Boolean} disabled (true|false) Is item active default false
32891 * @cfg {String} html
32892 * @cfg {String} position (top|bottom) text position default bottom
32893 * @cfg {String} icon show icon instead of number
32896 * Create a new NavProgressItem
32897 * @param {Object} config The config object
32899 Roo.bootstrap.NavProgressItem = function(config){
32900 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32905 * The raw click event for the entire grid.
32906 * @param {Roo.bootstrap.NavProgressItem} this
32907 * @param {Roo.EventObject} e
32914 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32920 position : 'bottom',
32923 getAutoCreate : function()
32925 var iconCls = 'roo-navigation-bar-item-icon';
32927 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32931 cls: 'roo-navigation-bar-item',
32941 cfg.cls += ' active';
32944 cfg.cls += ' disabled';
32950 disable : function()
32952 this.setDisabled(true);
32955 enable : function()
32957 this.setDisabled(false);
32960 initEvents: function()
32962 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32964 this.iconEl.on('click', this.onClick, this);
32967 onClick : function(e)
32969 e.preventDefault();
32975 if(this.fireEvent('click', this, e) === false){
32979 this.parent().setActiveItem(this);
32982 isActive: function ()
32984 return this.active;
32987 setActive : function(state)
32989 if(this.active == state){
32993 this.active = state;
32996 this.el.addClass('active');
33000 this.el.removeClass('active');
33005 setDisabled : function(state)
33007 if(this.disabled == state){
33011 this.disabled = state;
33014 this.el.addClass('disabled');
33018 this.el.removeClass('disabled');
33021 tooltipEl : function()
33023 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33036 * @class Roo.bootstrap.FieldLabel
33037 * @extends Roo.bootstrap.Component
33038 * Bootstrap FieldLabel class
33039 * @cfg {String} html contents of the element
33040 * @cfg {String} tag tag of the element default label
33041 * @cfg {String} cls class of the element
33042 * @cfg {String} target label target
33043 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33044 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33045 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33046 * @cfg {String} iconTooltip default "This field is required"
33047 * @cfg {String} indicatorpos (left|right) default left
33050 * Create a new FieldLabel
33051 * @param {Object} config The config object
33054 Roo.bootstrap.FieldLabel = function(config){
33055 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33060 * Fires after the field has been marked as invalid.
33061 * @param {Roo.form.FieldLabel} this
33062 * @param {String} msg The validation message
33067 * Fires after the field has been validated with no errors.
33068 * @param {Roo.form.FieldLabel} this
33074 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33081 invalidClass : 'has-warning',
33082 validClass : 'has-success',
33083 iconTooltip : 'This field is required',
33084 indicatorpos : 'left',
33086 getAutoCreate : function(){
33089 if (!this.allowBlank) {
33095 cls : 'roo-bootstrap-field-label ' + this.cls,
33100 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33101 tooltip : this.iconTooltip
33110 if(this.indicatorpos == 'right'){
33113 cls : 'roo-bootstrap-field-label ' + this.cls,
33122 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33123 tooltip : this.iconTooltip
33132 initEvents: function()
33134 Roo.bootstrap.Element.superclass.initEvents.call(this);
33136 this.indicator = this.indicatorEl();
33138 if(this.indicator){
33139 this.indicator.removeClass('visible');
33140 this.indicator.addClass('invisible');
33143 Roo.bootstrap.FieldLabel.register(this);
33146 indicatorEl : function()
33148 var indicator = this.el.select('i.roo-required-indicator',true).first();
33159 * Mark this field as valid
33161 markValid : function()
33163 if(this.indicator){
33164 this.indicator.removeClass('visible');
33165 this.indicator.addClass('invisible');
33167 if (Roo.bootstrap.version == 3) {
33168 this.el.removeClass(this.invalidClass);
33169 this.el.addClass(this.validClass);
33171 this.el.removeClass('is-invalid');
33172 this.el.addClass('is-valid');
33176 this.fireEvent('valid', this);
33180 * Mark this field as invalid
33181 * @param {String} msg The validation message
33183 markInvalid : function(msg)
33185 if(this.indicator){
33186 this.indicator.removeClass('invisible');
33187 this.indicator.addClass('visible');
33189 if (Roo.bootstrap.version == 3) {
33190 this.el.removeClass(this.validClass);
33191 this.el.addClass(this.invalidClass);
33193 this.el.removeClass('is-valid');
33194 this.el.addClass('is-invalid');
33198 this.fireEvent('invalid', this, msg);
33204 Roo.apply(Roo.bootstrap.FieldLabel, {
33209 * register a FieldLabel Group
33210 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33212 register : function(label)
33214 if(this.groups.hasOwnProperty(label.target)){
33218 this.groups[label.target] = label;
33222 * fetch a FieldLabel Group based on the target
33223 * @param {string} target
33224 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33226 get: function(target) {
33227 if (typeof(this.groups[target]) == 'undefined') {
33231 return this.groups[target] ;
33240 * page DateSplitField.
33246 * @class Roo.bootstrap.DateSplitField
33247 * @extends Roo.bootstrap.Component
33248 * Bootstrap DateSplitField class
33249 * @cfg {string} fieldLabel - the label associated
33250 * @cfg {Number} labelWidth set the width of label (0-12)
33251 * @cfg {String} labelAlign (top|left)
33252 * @cfg {Boolean} dayAllowBlank (true|false) default false
33253 * @cfg {Boolean} monthAllowBlank (true|false) default false
33254 * @cfg {Boolean} yearAllowBlank (true|false) default false
33255 * @cfg {string} dayPlaceholder
33256 * @cfg {string} monthPlaceholder
33257 * @cfg {string} yearPlaceholder
33258 * @cfg {string} dayFormat default 'd'
33259 * @cfg {string} monthFormat default 'm'
33260 * @cfg {string} yearFormat default 'Y'
33261 * @cfg {Number} labellg set the width of label (1-12)
33262 * @cfg {Number} labelmd set the width of label (1-12)
33263 * @cfg {Number} labelsm set the width of label (1-12)
33264 * @cfg {Number} labelxs set the width of label (1-12)
33268 * Create a new DateSplitField
33269 * @param {Object} config The config object
33272 Roo.bootstrap.DateSplitField = function(config){
33273 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33279 * getting the data of years
33280 * @param {Roo.bootstrap.DateSplitField} this
33281 * @param {Object} years
33286 * getting the data of days
33287 * @param {Roo.bootstrap.DateSplitField} this
33288 * @param {Object} days
33293 * Fires after the field has been marked as invalid.
33294 * @param {Roo.form.Field} this
33295 * @param {String} msg The validation message
33300 * Fires after the field has been validated with no errors.
33301 * @param {Roo.form.Field} this
33307 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33310 labelAlign : 'top',
33312 dayAllowBlank : false,
33313 monthAllowBlank : false,
33314 yearAllowBlank : false,
33315 dayPlaceholder : '',
33316 monthPlaceholder : '',
33317 yearPlaceholder : '',
33321 isFormField : true,
33327 getAutoCreate : function()
33331 cls : 'row roo-date-split-field-group',
33336 cls : 'form-hidden-field roo-date-split-field-group-value',
33342 var labelCls = 'col-md-12';
33343 var contentCls = 'col-md-4';
33345 if(this.fieldLabel){
33349 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33353 html : this.fieldLabel
33358 if(this.labelAlign == 'left'){
33360 if(this.labelWidth > 12){
33361 label.style = "width: " + this.labelWidth + 'px';
33364 if(this.labelWidth < 13 && this.labelmd == 0){
33365 this.labelmd = this.labelWidth;
33368 if(this.labellg > 0){
33369 labelCls = ' col-lg-' + this.labellg;
33370 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33373 if(this.labelmd > 0){
33374 labelCls = ' col-md-' + this.labelmd;
33375 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33378 if(this.labelsm > 0){
33379 labelCls = ' col-sm-' + this.labelsm;
33380 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33383 if(this.labelxs > 0){
33384 labelCls = ' col-xs-' + this.labelxs;
33385 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33389 label.cls += ' ' + labelCls;
33391 cfg.cn.push(label);
33394 Roo.each(['day', 'month', 'year'], function(t){
33397 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33404 inputEl: function ()
33406 return this.el.select('.roo-date-split-field-group-value', true).first();
33409 onRender : function(ct, position)
33413 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33415 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33417 this.dayField = new Roo.bootstrap.ComboBox({
33418 allowBlank : this.dayAllowBlank,
33419 alwaysQuery : true,
33420 displayField : 'value',
33423 forceSelection : true,
33425 placeholder : this.dayPlaceholder,
33426 selectOnFocus : true,
33427 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33428 triggerAction : 'all',
33430 valueField : 'value',
33431 store : new Roo.data.SimpleStore({
33432 data : (function() {
33434 _this.fireEvent('days', _this, days);
33437 fields : [ 'value' ]
33440 select : function (_self, record, index)
33442 _this.setValue(_this.getValue());
33447 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33449 this.monthField = new Roo.bootstrap.MonthField({
33450 after : '<i class=\"fa fa-calendar\"></i>',
33451 allowBlank : this.monthAllowBlank,
33452 placeholder : this.monthPlaceholder,
33455 render : function (_self)
33457 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33458 e.preventDefault();
33462 select : function (_self, oldvalue, newvalue)
33464 _this.setValue(_this.getValue());
33469 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33471 this.yearField = new Roo.bootstrap.ComboBox({
33472 allowBlank : this.yearAllowBlank,
33473 alwaysQuery : true,
33474 displayField : 'value',
33477 forceSelection : true,
33479 placeholder : this.yearPlaceholder,
33480 selectOnFocus : true,
33481 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33482 triggerAction : 'all',
33484 valueField : 'value',
33485 store : new Roo.data.SimpleStore({
33486 data : (function() {
33488 _this.fireEvent('years', _this, years);
33491 fields : [ 'value' ]
33494 select : function (_self, record, index)
33496 _this.setValue(_this.getValue());
33501 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33504 setValue : function(v, format)
33506 this.inputEl.dom.value = v;
33508 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33510 var d = Date.parseDate(v, f);
33517 this.setDay(d.format(this.dayFormat));
33518 this.setMonth(d.format(this.monthFormat));
33519 this.setYear(d.format(this.yearFormat));
33526 setDay : function(v)
33528 this.dayField.setValue(v);
33529 this.inputEl.dom.value = this.getValue();
33534 setMonth : function(v)
33536 this.monthField.setValue(v, true);
33537 this.inputEl.dom.value = this.getValue();
33542 setYear : function(v)
33544 this.yearField.setValue(v);
33545 this.inputEl.dom.value = this.getValue();
33550 getDay : function()
33552 return this.dayField.getValue();
33555 getMonth : function()
33557 return this.monthField.getValue();
33560 getYear : function()
33562 return this.yearField.getValue();
33565 getValue : function()
33567 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33569 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33579 this.inputEl.dom.value = '';
33584 validate : function()
33586 var d = this.dayField.validate();
33587 var m = this.monthField.validate();
33588 var y = this.yearField.validate();
33593 (!this.dayAllowBlank && !d) ||
33594 (!this.monthAllowBlank && !m) ||
33595 (!this.yearAllowBlank && !y)
33600 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33609 this.markInvalid();
33614 markValid : function()
33617 var label = this.el.select('label', true).first();
33618 var icon = this.el.select('i.fa-star', true).first();
33624 this.fireEvent('valid', this);
33628 * Mark this field as invalid
33629 * @param {String} msg The validation message
33631 markInvalid : function(msg)
33634 var label = this.el.select('label', true).first();
33635 var icon = this.el.select('i.fa-star', true).first();
33637 if(label && !icon){
33638 this.el.select('.roo-date-split-field-label', true).createChild({
33640 cls : 'text-danger fa fa-lg fa-star',
33641 tooltip : 'This field is required',
33642 style : 'margin-right:5px;'
33646 this.fireEvent('invalid', this, msg);
33649 clearInvalid : function()
33651 var label = this.el.select('label', true).first();
33652 var icon = this.el.select('i.fa-star', true).first();
33658 this.fireEvent('valid', this);
33661 getName: function()
33671 * http://masonry.desandro.com
33673 * The idea is to render all the bricks based on vertical width...
33675 * The original code extends 'outlayer' - we might need to use that....
33681 * @class Roo.bootstrap.LayoutMasonry
33682 * @extends Roo.bootstrap.Component
33683 * Bootstrap Layout Masonry class
33686 * Create a new Element
33687 * @param {Object} config The config object
33690 Roo.bootstrap.LayoutMasonry = function(config){
33692 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33696 Roo.bootstrap.LayoutMasonry.register(this);
33702 * Fire after layout the items
33703 * @param {Roo.bootstrap.LayoutMasonry} this
33704 * @param {Roo.EventObject} e
33711 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33714 * @cfg {Boolean} isLayoutInstant = no animation?
33716 isLayoutInstant : false, // needed?
33719 * @cfg {Number} boxWidth width of the columns
33724 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33729 * @cfg {Number} padWidth padding below box..
33734 * @cfg {Number} gutter gutter width..
33739 * @cfg {Number} maxCols maximum number of columns
33745 * @cfg {Boolean} isAutoInitial defalut true
33747 isAutoInitial : true,
33752 * @cfg {Boolean} isHorizontal defalut false
33754 isHorizontal : false,
33756 currentSize : null,
33762 bricks: null, //CompositeElement
33766 _isLayoutInited : false,
33768 // isAlternative : false, // only use for vertical layout...
33771 * @cfg {Number} alternativePadWidth padding below box..
33773 alternativePadWidth : 50,
33775 selectedBrick : [],
33777 getAutoCreate : function(){
33779 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33783 cls: 'blog-masonary-wrapper ' + this.cls,
33785 cls : 'mas-boxes masonary'
33792 getChildContainer: function( )
33794 if (this.boxesEl) {
33795 return this.boxesEl;
33798 this.boxesEl = this.el.select('.mas-boxes').first();
33800 return this.boxesEl;
33804 initEvents : function()
33808 if(this.isAutoInitial){
33809 Roo.log('hook children rendered');
33810 this.on('childrenrendered', function() {
33811 Roo.log('children rendered');
33817 initial : function()
33819 this.selectedBrick = [];
33821 this.currentSize = this.el.getBox(true);
33823 Roo.EventManager.onWindowResize(this.resize, this);
33825 if(!this.isAutoInitial){
33833 //this.layout.defer(500,this);
33837 resize : function()
33839 var cs = this.el.getBox(true);
33842 this.currentSize.width == cs.width &&
33843 this.currentSize.x == cs.x &&
33844 this.currentSize.height == cs.height &&
33845 this.currentSize.y == cs.y
33847 Roo.log("no change in with or X or Y");
33851 this.currentSize = cs;
33857 layout : function()
33859 this._resetLayout();
33861 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33863 this.layoutItems( isInstant );
33865 this._isLayoutInited = true;
33867 this.fireEvent('layout', this);
33871 _resetLayout : function()
33873 if(this.isHorizontal){
33874 this.horizontalMeasureColumns();
33878 this.verticalMeasureColumns();
33882 verticalMeasureColumns : function()
33884 this.getContainerWidth();
33886 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33887 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33891 var boxWidth = this.boxWidth + this.padWidth;
33893 if(this.containerWidth < this.boxWidth){
33894 boxWidth = this.containerWidth
33897 var containerWidth = this.containerWidth;
33899 var cols = Math.floor(containerWidth / boxWidth);
33901 this.cols = Math.max( cols, 1 );
33903 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33905 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33907 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33909 this.colWidth = boxWidth + avail - this.padWidth;
33911 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33912 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33915 horizontalMeasureColumns : function()
33917 this.getContainerWidth();
33919 var boxWidth = this.boxWidth;
33921 if(this.containerWidth < boxWidth){
33922 boxWidth = this.containerWidth;
33925 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33927 this.el.setHeight(boxWidth);
33931 getContainerWidth : function()
33933 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33936 layoutItems : function( isInstant )
33938 Roo.log(this.bricks);
33940 var items = Roo.apply([], this.bricks);
33942 if(this.isHorizontal){
33943 this._horizontalLayoutItems( items , isInstant );
33947 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33948 // this._verticalAlternativeLayoutItems( items , isInstant );
33952 this._verticalLayoutItems( items , isInstant );
33956 _verticalLayoutItems : function ( items , isInstant)
33958 if ( !items || !items.length ) {
33963 ['xs', 'xs', 'xs', 'tall'],
33964 ['xs', 'xs', 'tall'],
33965 ['xs', 'xs', 'sm'],
33966 ['xs', 'xs', 'xs'],
33972 ['sm', 'xs', 'xs'],
33976 ['tall', 'xs', 'xs', 'xs'],
33977 ['tall', 'xs', 'xs'],
33989 Roo.each(items, function(item, k){
33991 switch (item.size) {
33992 // these layouts take up a full box,
34003 boxes.push([item]);
34026 var filterPattern = function(box, length)
34034 var pattern = box.slice(0, length);
34038 Roo.each(pattern, function(i){
34039 format.push(i.size);
34042 Roo.each(standard, function(s){
34044 if(String(s) != String(format)){
34053 if(!match && length == 1){
34058 filterPattern(box, length - 1);
34062 queue.push(pattern);
34064 box = box.slice(length, box.length);
34066 filterPattern(box, 4);
34072 Roo.each(boxes, function(box, k){
34078 if(box.length == 1){
34083 filterPattern(box, 4);
34087 this._processVerticalLayoutQueue( queue, isInstant );
34091 // _verticalAlternativeLayoutItems : function( items , isInstant )
34093 // if ( !items || !items.length ) {
34097 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34101 _horizontalLayoutItems : function ( items , isInstant)
34103 if ( !items || !items.length || items.length < 3) {
34109 var eItems = items.slice(0, 3);
34111 items = items.slice(3, items.length);
34114 ['xs', 'xs', 'xs', 'wide'],
34115 ['xs', 'xs', 'wide'],
34116 ['xs', 'xs', 'sm'],
34117 ['xs', 'xs', 'xs'],
34123 ['sm', 'xs', 'xs'],
34127 ['wide', 'xs', 'xs', 'xs'],
34128 ['wide', 'xs', 'xs'],
34141 Roo.each(items, function(item, k){
34143 switch (item.size) {
34154 boxes.push([item]);
34178 var filterPattern = function(box, length)
34186 var pattern = box.slice(0, length);
34190 Roo.each(pattern, function(i){
34191 format.push(i.size);
34194 Roo.each(standard, function(s){
34196 if(String(s) != String(format)){
34205 if(!match && length == 1){
34210 filterPattern(box, length - 1);
34214 queue.push(pattern);
34216 box = box.slice(length, box.length);
34218 filterPattern(box, 4);
34224 Roo.each(boxes, function(box, k){
34230 if(box.length == 1){
34235 filterPattern(box, 4);
34242 var pos = this.el.getBox(true);
34246 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34248 var hit_end = false;
34250 Roo.each(queue, function(box){
34254 Roo.each(box, function(b){
34256 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34266 Roo.each(box, function(b){
34268 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34271 mx = Math.max(mx, b.x);
34275 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34279 Roo.each(box, function(b){
34281 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34295 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34298 /** Sets position of item in DOM
34299 * @param {Element} item
34300 * @param {Number} x - horizontal position
34301 * @param {Number} y - vertical position
34302 * @param {Boolean} isInstant - disables transitions
34304 _processVerticalLayoutQueue : function( queue, isInstant )
34306 var pos = this.el.getBox(true);
34311 for (var i = 0; i < this.cols; i++){
34315 Roo.each(queue, function(box, k){
34317 var col = k % this.cols;
34319 Roo.each(box, function(b,kk){
34321 b.el.position('absolute');
34323 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34324 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34326 if(b.size == 'md-left' || b.size == 'md-right'){
34327 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34328 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34331 b.el.setWidth(width);
34332 b.el.setHeight(height);
34334 b.el.select('iframe',true).setSize(width,height);
34338 for (var i = 0; i < this.cols; i++){
34340 if(maxY[i] < maxY[col]){
34345 col = Math.min(col, i);
34349 x = pos.x + col * (this.colWidth + this.padWidth);
34353 var positions = [];
34355 switch (box.length){
34357 positions = this.getVerticalOneBoxColPositions(x, y, box);
34360 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34363 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34366 positions = this.getVerticalFourBoxColPositions(x, y, box);
34372 Roo.each(box, function(b,kk){
34374 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34376 var sz = b.el.getSize();
34378 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34386 for (var i = 0; i < this.cols; i++){
34387 mY = Math.max(mY, maxY[i]);
34390 this.el.setHeight(mY - pos.y);
34394 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34396 // var pos = this.el.getBox(true);
34399 // var maxX = pos.right;
34401 // var maxHeight = 0;
34403 // Roo.each(items, function(item, k){
34407 // item.el.position('absolute');
34409 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34411 // item.el.setWidth(width);
34413 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34415 // item.el.setHeight(height);
34418 // item.el.setXY([x, y], isInstant ? false : true);
34420 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34423 // y = y + height + this.alternativePadWidth;
34425 // maxHeight = maxHeight + height + this.alternativePadWidth;
34429 // this.el.setHeight(maxHeight);
34433 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34435 var pos = this.el.getBox(true);
34440 var maxX = pos.right;
34442 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34444 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34446 Roo.each(queue, function(box, k){
34448 Roo.each(box, function(b, kk){
34450 b.el.position('absolute');
34452 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34453 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34455 if(b.size == 'md-left' || b.size == 'md-right'){
34456 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34457 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34460 b.el.setWidth(width);
34461 b.el.setHeight(height);
34469 var positions = [];
34471 switch (box.length){
34473 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34476 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34479 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34482 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34488 Roo.each(box, function(b,kk){
34490 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34492 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34500 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34502 Roo.each(eItems, function(b,k){
34504 b.size = (k == 0) ? 'sm' : 'xs';
34505 b.x = (k == 0) ? 2 : 1;
34506 b.y = (k == 0) ? 2 : 1;
34508 b.el.position('absolute');
34510 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34512 b.el.setWidth(width);
34514 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34516 b.el.setHeight(height);
34520 var positions = [];
34523 x : maxX - this.unitWidth * 2 - this.gutter,
34528 x : maxX - this.unitWidth,
34529 y : minY + (this.unitWidth + this.gutter) * 2
34533 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34537 Roo.each(eItems, function(b,k){
34539 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34545 getVerticalOneBoxColPositions : function(x, y, box)
34549 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34551 if(box[0].size == 'md-left'){
34555 if(box[0].size == 'md-right'){
34560 x : x + (this.unitWidth + this.gutter) * rand,
34567 getVerticalTwoBoxColPositions : function(x, y, box)
34571 if(box[0].size == 'xs'){
34575 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34579 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34593 x : x + (this.unitWidth + this.gutter) * 2,
34594 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34601 getVerticalThreeBoxColPositions : function(x, y, box)
34605 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34613 x : x + (this.unitWidth + this.gutter) * 1,
34618 x : x + (this.unitWidth + this.gutter) * 2,
34626 if(box[0].size == 'xs' && box[1].size == 'xs'){
34635 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34639 x : x + (this.unitWidth + this.gutter) * 1,
34653 x : x + (this.unitWidth + this.gutter) * 2,
34658 x : x + (this.unitWidth + this.gutter) * 2,
34659 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34666 getVerticalFourBoxColPositions : function(x, y, box)
34670 if(box[0].size == 'xs'){
34679 y : y + (this.unitHeight + this.gutter) * 1
34684 y : y + (this.unitHeight + this.gutter) * 2
34688 x : x + (this.unitWidth + this.gutter) * 1,
34702 x : x + (this.unitWidth + this.gutter) * 2,
34707 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34708 y : y + (this.unitHeight + this.gutter) * 1
34712 x : x + (this.unitWidth + this.gutter) * 2,
34713 y : y + (this.unitWidth + this.gutter) * 2
34720 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34724 if(box[0].size == 'md-left'){
34726 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34733 if(box[0].size == 'md-right'){
34735 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34736 y : minY + (this.unitWidth + this.gutter) * 1
34742 var rand = Math.floor(Math.random() * (4 - box[0].y));
34745 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34746 y : minY + (this.unitWidth + this.gutter) * rand
34753 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34757 if(box[0].size == 'xs'){
34760 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34765 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34766 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34774 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34779 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34780 y : minY + (this.unitWidth + this.gutter) * 2
34787 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34791 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34794 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34799 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34800 y : minY + (this.unitWidth + this.gutter) * 1
34804 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34805 y : minY + (this.unitWidth + this.gutter) * 2
34812 if(box[0].size == 'xs' && box[1].size == 'xs'){
34815 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34820 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34825 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34826 y : minY + (this.unitWidth + this.gutter) * 1
34834 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34839 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34840 y : minY + (this.unitWidth + this.gutter) * 2
34844 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34845 y : minY + (this.unitWidth + this.gutter) * 2
34852 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34856 if(box[0].size == 'xs'){
34859 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34864 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34869 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),
34874 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34875 y : minY + (this.unitWidth + this.gutter) * 1
34883 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34888 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34889 y : minY + (this.unitWidth + this.gutter) * 2
34893 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34894 y : minY + (this.unitWidth + this.gutter) * 2
34898 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),
34899 y : minY + (this.unitWidth + this.gutter) * 2
34907 * remove a Masonry Brick
34908 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34910 removeBrick : function(brick_id)
34916 for (var i = 0; i<this.bricks.length; i++) {
34917 if (this.bricks[i].id == brick_id) {
34918 this.bricks.splice(i,1);
34919 this.el.dom.removeChild(Roo.get(brick_id).dom);
34926 * adds a Masonry Brick
34927 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34929 addBrick : function(cfg)
34931 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34932 //this.register(cn);
34933 cn.parentId = this.id;
34934 cn.render(this.el);
34939 * register a Masonry Brick
34940 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34943 register : function(brick)
34945 this.bricks.push(brick);
34946 brick.masonryId = this.id;
34950 * clear all the Masonry Brick
34952 clearAll : function()
34955 //this.getChildContainer().dom.innerHTML = "";
34956 this.el.dom.innerHTML = '';
34959 getSelected : function()
34961 if (!this.selectedBrick) {
34965 return this.selectedBrick;
34969 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34973 * register a Masonry Layout
34974 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34977 register : function(layout)
34979 this.groups[layout.id] = layout;
34982 * fetch a Masonry Layout based on the masonry layout ID
34983 * @param {string} the masonry layout to add
34984 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34987 get: function(layout_id) {
34988 if (typeof(this.groups[layout_id]) == 'undefined') {
34991 return this.groups[layout_id] ;
35003 * http://masonry.desandro.com
35005 * The idea is to render all the bricks based on vertical width...
35007 * The original code extends 'outlayer' - we might need to use that....
35013 * @class Roo.bootstrap.LayoutMasonryAuto
35014 * @extends Roo.bootstrap.Component
35015 * Bootstrap Layout Masonry class
35018 * Create a new Element
35019 * @param {Object} config The config object
35022 Roo.bootstrap.LayoutMasonryAuto = function(config){
35023 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35026 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35029 * @cfg {Boolean} isFitWidth - resize the width..
35031 isFitWidth : false, // options..
35033 * @cfg {Boolean} isOriginLeft = left align?
35035 isOriginLeft : true,
35037 * @cfg {Boolean} isOriginTop = top align?
35039 isOriginTop : false,
35041 * @cfg {Boolean} isLayoutInstant = no animation?
35043 isLayoutInstant : false, // needed?
35045 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35047 isResizingContainer : true,
35049 * @cfg {Number} columnWidth width of the columns
35055 * @cfg {Number} maxCols maximum number of columns
35060 * @cfg {Number} padHeight padding below box..
35066 * @cfg {Boolean} isAutoInitial defalut true
35069 isAutoInitial : true,
35075 initialColumnWidth : 0,
35076 currentSize : null,
35078 colYs : null, // array.
35085 bricks: null, //CompositeElement
35086 cols : 0, // array?
35087 // element : null, // wrapped now this.el
35088 _isLayoutInited : null,
35091 getAutoCreate : function(){
35095 cls: 'blog-masonary-wrapper ' + this.cls,
35097 cls : 'mas-boxes masonary'
35104 getChildContainer: function( )
35106 if (this.boxesEl) {
35107 return this.boxesEl;
35110 this.boxesEl = this.el.select('.mas-boxes').first();
35112 return this.boxesEl;
35116 initEvents : function()
35120 if(this.isAutoInitial){
35121 Roo.log('hook children rendered');
35122 this.on('childrenrendered', function() {
35123 Roo.log('children rendered');
35130 initial : function()
35132 this.reloadItems();
35134 this.currentSize = this.el.getBox(true);
35136 /// was window resize... - let's see if this works..
35137 Roo.EventManager.onWindowResize(this.resize, this);
35139 if(!this.isAutoInitial){
35144 this.layout.defer(500,this);
35147 reloadItems: function()
35149 this.bricks = this.el.select('.masonry-brick', true);
35151 this.bricks.each(function(b) {
35152 //Roo.log(b.getSize());
35153 if (!b.attr('originalwidth')) {
35154 b.attr('originalwidth', b.getSize().width);
35159 Roo.log(this.bricks.elements.length);
35162 resize : function()
35165 var cs = this.el.getBox(true);
35167 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35168 Roo.log("no change in with or X");
35171 this.currentSize = cs;
35175 layout : function()
35178 this._resetLayout();
35179 //this._manageStamps();
35181 // don't animate first layout
35182 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35183 this.layoutItems( isInstant );
35185 // flag for initalized
35186 this._isLayoutInited = true;
35189 layoutItems : function( isInstant )
35191 //var items = this._getItemsForLayout( this.items );
35192 // original code supports filtering layout items.. we just ignore it..
35194 this._layoutItems( this.bricks , isInstant );
35196 this._postLayout();
35198 _layoutItems : function ( items , isInstant)
35200 //this.fireEvent( 'layout', this, items );
35203 if ( !items || !items.elements.length ) {
35204 // no items, emit event with empty array
35209 items.each(function(item) {
35210 Roo.log("layout item");
35212 // get x/y object from method
35213 var position = this._getItemLayoutPosition( item );
35215 position.item = item;
35216 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35217 queue.push( position );
35220 this._processLayoutQueue( queue );
35222 /** Sets position of item in DOM
35223 * @param {Element} item
35224 * @param {Number} x - horizontal position
35225 * @param {Number} y - vertical position
35226 * @param {Boolean} isInstant - disables transitions
35228 _processLayoutQueue : function( queue )
35230 for ( var i=0, len = queue.length; i < len; i++ ) {
35231 var obj = queue[i];
35232 obj.item.position('absolute');
35233 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35239 * Any logic you want to do after each layout,
35240 * i.e. size the container
35242 _postLayout : function()
35244 this.resizeContainer();
35247 resizeContainer : function()
35249 if ( !this.isResizingContainer ) {
35252 var size = this._getContainerSize();
35254 this.el.setSize(size.width,size.height);
35255 this.boxesEl.setSize(size.width,size.height);
35261 _resetLayout : function()
35263 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35264 this.colWidth = this.el.getWidth();
35265 //this.gutter = this.el.getWidth();
35267 this.measureColumns();
35273 this.colYs.push( 0 );
35279 measureColumns : function()
35281 this.getContainerWidth();
35282 // if columnWidth is 0, default to outerWidth of first item
35283 if ( !this.columnWidth ) {
35284 var firstItem = this.bricks.first();
35285 Roo.log(firstItem);
35286 this.columnWidth = this.containerWidth;
35287 if (firstItem && firstItem.attr('originalwidth') ) {
35288 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35290 // columnWidth fall back to item of first element
35291 Roo.log("set column width?");
35292 this.initialColumnWidth = this.columnWidth ;
35294 // if first elem has no width, default to size of container
35299 if (this.initialColumnWidth) {
35300 this.columnWidth = this.initialColumnWidth;
35305 // column width is fixed at the top - however if container width get's smaller we should
35308 // this bit calcs how man columns..
35310 var columnWidth = this.columnWidth += this.gutter;
35312 // calculate columns
35313 var containerWidth = this.containerWidth + this.gutter;
35315 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35316 // fix rounding errors, typically with gutters
35317 var excess = columnWidth - containerWidth % columnWidth;
35320 // if overshoot is less than a pixel, round up, otherwise floor it
35321 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35322 cols = Math[ mathMethod ]( cols );
35323 this.cols = Math.max( cols, 1 );
35324 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35326 // padding positioning..
35327 var totalColWidth = this.cols * this.columnWidth;
35328 var padavail = this.containerWidth - totalColWidth;
35329 // so for 2 columns - we need 3 'pads'
35331 var padNeeded = (1+this.cols) * this.padWidth;
35333 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35335 this.columnWidth += padExtra
35336 //this.padWidth = Math.floor(padavail / ( this.cols));
35338 // adjust colum width so that padding is fixed??
35340 // we have 3 columns ... total = width * 3
35341 // we have X left over... that should be used by
35343 //if (this.expandC) {
35351 getContainerWidth : function()
35353 /* // container is parent if fit width
35354 var container = this.isFitWidth ? this.element.parentNode : this.element;
35355 // check that this.size and size are there
35356 // IE8 triggers resize on body size change, so they might not be
35358 var size = getSize( container ); //FIXME
35359 this.containerWidth = size && size.innerWidth; //FIXME
35362 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35366 _getItemLayoutPosition : function( item ) // what is item?
35368 // we resize the item to our columnWidth..
35370 item.setWidth(this.columnWidth);
35371 item.autoBoxAdjust = false;
35373 var sz = item.getSize();
35375 // how many columns does this brick span
35376 var remainder = this.containerWidth % this.columnWidth;
35378 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35379 // round if off by 1 pixel, otherwise use ceil
35380 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35381 colSpan = Math.min( colSpan, this.cols );
35383 // normally this should be '1' as we dont' currently allow multi width columns..
35385 var colGroup = this._getColGroup( colSpan );
35386 // get the minimum Y value from the columns
35387 var minimumY = Math.min.apply( Math, colGroup );
35388 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35390 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35392 // position the brick
35394 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35395 y: this.currentSize.y + minimumY + this.padHeight
35399 // apply setHeight to necessary columns
35400 var setHeight = minimumY + sz.height + this.padHeight;
35401 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35403 var setSpan = this.cols + 1 - colGroup.length;
35404 for ( var i = 0; i < setSpan; i++ ) {
35405 this.colYs[ shortColIndex + i ] = setHeight ;
35412 * @param {Number} colSpan - number of columns the element spans
35413 * @returns {Array} colGroup
35415 _getColGroup : function( colSpan )
35417 if ( colSpan < 2 ) {
35418 // if brick spans only one column, use all the column Ys
35423 // how many different places could this brick fit horizontally
35424 var groupCount = this.cols + 1 - colSpan;
35425 // for each group potential horizontal position
35426 for ( var i = 0; i < groupCount; i++ ) {
35427 // make an array of colY values for that one group
35428 var groupColYs = this.colYs.slice( i, i + colSpan );
35429 // and get the max value of the array
35430 colGroup[i] = Math.max.apply( Math, groupColYs );
35435 _manageStamp : function( stamp )
35437 var stampSize = stamp.getSize();
35438 var offset = stamp.getBox();
35439 // get the columns that this stamp affects
35440 var firstX = this.isOriginLeft ? offset.x : offset.right;
35441 var lastX = firstX + stampSize.width;
35442 var firstCol = Math.floor( firstX / this.columnWidth );
35443 firstCol = Math.max( 0, firstCol );
35445 var lastCol = Math.floor( lastX / this.columnWidth );
35446 // lastCol should not go over if multiple of columnWidth #425
35447 lastCol -= lastX % this.columnWidth ? 0 : 1;
35448 lastCol = Math.min( this.cols - 1, lastCol );
35450 // set colYs to bottom of the stamp
35451 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35454 for ( var i = firstCol; i <= lastCol; i++ ) {
35455 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35460 _getContainerSize : function()
35462 this.maxY = Math.max.apply( Math, this.colYs );
35467 if ( this.isFitWidth ) {
35468 size.width = this._getContainerFitWidth();
35474 _getContainerFitWidth : function()
35476 var unusedCols = 0;
35477 // count unused columns
35480 if ( this.colYs[i] !== 0 ) {
35485 // fit container to columns that have been used
35486 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35489 needsResizeLayout : function()
35491 var previousWidth = this.containerWidth;
35492 this.getContainerWidth();
35493 return previousWidth !== this.containerWidth;
35508 * @class Roo.bootstrap.MasonryBrick
35509 * @extends Roo.bootstrap.Component
35510 * Bootstrap MasonryBrick class
35513 * Create a new MasonryBrick
35514 * @param {Object} config The config object
35517 Roo.bootstrap.MasonryBrick = function(config){
35519 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35521 Roo.bootstrap.MasonryBrick.register(this);
35527 * When a MasonryBrick is clcik
35528 * @param {Roo.bootstrap.MasonryBrick} this
35529 * @param {Roo.EventObject} e
35535 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35538 * @cfg {String} title
35542 * @cfg {String} html
35546 * @cfg {String} bgimage
35550 * @cfg {String} videourl
35554 * @cfg {String} cls
35558 * @cfg {String} href
35562 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35567 * @cfg {String} placetitle (center|bottom)
35572 * @cfg {Boolean} isFitContainer defalut true
35574 isFitContainer : true,
35577 * @cfg {Boolean} preventDefault defalut false
35579 preventDefault : false,
35582 * @cfg {Boolean} inverse defalut false
35584 maskInverse : false,
35586 getAutoCreate : function()
35588 if(!this.isFitContainer){
35589 return this.getSplitAutoCreate();
35592 var cls = 'masonry-brick masonry-brick-full';
35594 if(this.href.length){
35595 cls += ' masonry-brick-link';
35598 if(this.bgimage.length){
35599 cls += ' masonry-brick-image';
35602 if(this.maskInverse){
35603 cls += ' mask-inverse';
35606 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35607 cls += ' enable-mask';
35611 cls += ' masonry-' + this.size + '-brick';
35614 if(this.placetitle.length){
35616 switch (this.placetitle) {
35618 cls += ' masonry-center-title';
35621 cls += ' masonry-bottom-title';
35628 if(!this.html.length && !this.bgimage.length){
35629 cls += ' masonry-center-title';
35632 if(!this.html.length && this.bgimage.length){
35633 cls += ' masonry-bottom-title';
35638 cls += ' ' + this.cls;
35642 tag: (this.href.length) ? 'a' : 'div',
35647 cls: 'masonry-brick-mask'
35651 cls: 'masonry-brick-paragraph',
35657 if(this.href.length){
35658 cfg.href = this.href;
35661 var cn = cfg.cn[1].cn;
35663 if(this.title.length){
35666 cls: 'masonry-brick-title',
35671 if(this.html.length){
35674 cls: 'masonry-brick-text',
35679 if (!this.title.length && !this.html.length) {
35680 cfg.cn[1].cls += ' hide';
35683 if(this.bgimage.length){
35686 cls: 'masonry-brick-image-view',
35691 if(this.videourl.length){
35692 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35693 // youtube support only?
35696 cls: 'masonry-brick-image-view',
35699 allowfullscreen : true
35707 getSplitAutoCreate : function()
35709 var cls = 'masonry-brick masonry-brick-split';
35711 if(this.href.length){
35712 cls += ' masonry-brick-link';
35715 if(this.bgimage.length){
35716 cls += ' masonry-brick-image';
35720 cls += ' masonry-' + this.size + '-brick';
35723 switch (this.placetitle) {
35725 cls += ' masonry-center-title';
35728 cls += ' masonry-bottom-title';
35731 if(!this.bgimage.length){
35732 cls += ' masonry-center-title';
35735 if(this.bgimage.length){
35736 cls += ' masonry-bottom-title';
35742 cls += ' ' + this.cls;
35746 tag: (this.href.length) ? 'a' : 'div',
35751 cls: 'masonry-brick-split-head',
35755 cls: 'masonry-brick-paragraph',
35762 cls: 'masonry-brick-split-body',
35768 if(this.href.length){
35769 cfg.href = this.href;
35772 if(this.title.length){
35773 cfg.cn[0].cn[0].cn.push({
35775 cls: 'masonry-brick-title',
35780 if(this.html.length){
35781 cfg.cn[1].cn.push({
35783 cls: 'masonry-brick-text',
35788 if(this.bgimage.length){
35789 cfg.cn[0].cn.push({
35791 cls: 'masonry-brick-image-view',
35796 if(this.videourl.length){
35797 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35798 // youtube support only?
35799 cfg.cn[0].cn.cn.push({
35801 cls: 'masonry-brick-image-view',
35804 allowfullscreen : true
35811 initEvents: function()
35813 switch (this.size) {
35846 this.el.on('touchstart', this.onTouchStart, this);
35847 this.el.on('touchmove', this.onTouchMove, this);
35848 this.el.on('touchend', this.onTouchEnd, this);
35849 this.el.on('contextmenu', this.onContextMenu, this);
35851 this.el.on('mouseenter' ,this.enter, this);
35852 this.el.on('mouseleave', this.leave, this);
35853 this.el.on('click', this.onClick, this);
35856 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35857 this.parent().bricks.push(this);
35862 onClick: function(e, el)
35864 var time = this.endTimer - this.startTimer;
35865 // Roo.log(e.preventDefault());
35868 e.preventDefault();
35873 if(!this.preventDefault){
35877 e.preventDefault();
35879 if (this.activeClass != '') {
35880 this.selectBrick();
35883 this.fireEvent('click', this, e);
35886 enter: function(e, el)
35888 e.preventDefault();
35890 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35894 if(this.bgimage.length && this.html.length){
35895 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35899 leave: function(e, el)
35901 e.preventDefault();
35903 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35907 if(this.bgimage.length && this.html.length){
35908 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35912 onTouchStart: function(e, el)
35914 // e.preventDefault();
35916 this.touchmoved = false;
35918 if(!this.isFitContainer){
35922 if(!this.bgimage.length || !this.html.length){
35926 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35928 this.timer = new Date().getTime();
35932 onTouchMove: function(e, el)
35934 this.touchmoved = true;
35937 onContextMenu : function(e,el)
35939 e.preventDefault();
35940 e.stopPropagation();
35944 onTouchEnd: function(e, el)
35946 // e.preventDefault();
35948 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35955 if(!this.bgimage.length || !this.html.length){
35957 if(this.href.length){
35958 window.location.href = this.href;
35964 if(!this.isFitContainer){
35968 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35970 window.location.href = this.href;
35973 //selection on single brick only
35974 selectBrick : function() {
35976 if (!this.parentId) {
35980 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35981 var index = m.selectedBrick.indexOf(this.id);
35984 m.selectedBrick.splice(index,1);
35985 this.el.removeClass(this.activeClass);
35989 for(var i = 0; i < m.selectedBrick.length; i++) {
35990 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35991 b.el.removeClass(b.activeClass);
35994 m.selectedBrick = [];
35996 m.selectedBrick.push(this.id);
35997 this.el.addClass(this.activeClass);
36001 isSelected : function(){
36002 return this.el.hasClass(this.activeClass);
36007 Roo.apply(Roo.bootstrap.MasonryBrick, {
36010 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36012 * register a Masonry Brick
36013 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36016 register : function(brick)
36018 //this.groups[brick.id] = brick;
36019 this.groups.add(brick.id, brick);
36022 * fetch a masonry brick based on the masonry brick ID
36023 * @param {string} the masonry brick to add
36024 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36027 get: function(brick_id)
36029 // if (typeof(this.groups[brick_id]) == 'undefined') {
36032 // return this.groups[brick_id] ;
36034 if(this.groups.key(brick_id)) {
36035 return this.groups.key(brick_id);
36053 * @class Roo.bootstrap.Brick
36054 * @extends Roo.bootstrap.Component
36055 * Bootstrap Brick class
36058 * Create a new Brick
36059 * @param {Object} config The config object
36062 Roo.bootstrap.Brick = function(config){
36063 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36069 * When a Brick is click
36070 * @param {Roo.bootstrap.Brick} this
36071 * @param {Roo.EventObject} e
36077 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36080 * @cfg {String} title
36084 * @cfg {String} html
36088 * @cfg {String} bgimage
36092 * @cfg {String} cls
36096 * @cfg {String} href
36100 * @cfg {String} video
36104 * @cfg {Boolean} square
36108 getAutoCreate : function()
36110 var cls = 'roo-brick';
36112 if(this.href.length){
36113 cls += ' roo-brick-link';
36116 if(this.bgimage.length){
36117 cls += ' roo-brick-image';
36120 if(!this.html.length && !this.bgimage.length){
36121 cls += ' roo-brick-center-title';
36124 if(!this.html.length && this.bgimage.length){
36125 cls += ' roo-brick-bottom-title';
36129 cls += ' ' + this.cls;
36133 tag: (this.href.length) ? 'a' : 'div',
36138 cls: 'roo-brick-paragraph',
36144 if(this.href.length){
36145 cfg.href = this.href;
36148 var cn = cfg.cn[0].cn;
36150 if(this.title.length){
36153 cls: 'roo-brick-title',
36158 if(this.html.length){
36161 cls: 'roo-brick-text',
36168 if(this.bgimage.length){
36171 cls: 'roo-brick-image-view',
36179 initEvents: function()
36181 if(this.title.length || this.html.length){
36182 this.el.on('mouseenter' ,this.enter, this);
36183 this.el.on('mouseleave', this.leave, this);
36186 Roo.EventManager.onWindowResize(this.resize, this);
36188 if(this.bgimage.length){
36189 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36190 this.imageEl.on('load', this.onImageLoad, this);
36197 onImageLoad : function()
36202 resize : function()
36204 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36206 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36208 if(this.bgimage.length){
36209 var image = this.el.select('.roo-brick-image-view', true).first();
36211 image.setWidth(paragraph.getWidth());
36214 image.setHeight(paragraph.getWidth());
36217 this.el.setHeight(image.getHeight());
36218 paragraph.setHeight(image.getHeight());
36224 enter: function(e, el)
36226 e.preventDefault();
36228 if(this.bgimage.length){
36229 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36230 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36234 leave: function(e, el)
36236 e.preventDefault();
36238 if(this.bgimage.length){
36239 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36240 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36255 * @class Roo.bootstrap.NumberField
36256 * @extends Roo.bootstrap.Input
36257 * Bootstrap NumberField class
36263 * Create a new NumberField
36264 * @param {Object} config The config object
36267 Roo.bootstrap.NumberField = function(config){
36268 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36271 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36274 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36276 allowDecimals : true,
36278 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36280 decimalSeparator : ".",
36282 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36284 decimalPrecision : 2,
36286 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36288 allowNegative : true,
36291 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36295 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36297 minValue : Number.NEGATIVE_INFINITY,
36299 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36301 maxValue : Number.MAX_VALUE,
36303 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36305 minText : "The minimum value for this field is {0}",
36307 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36309 maxText : "The maximum value for this field is {0}",
36311 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36312 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36314 nanText : "{0} is not a valid number",
36316 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36318 thousandsDelimiter : false,
36320 * @cfg {String} valueAlign alignment of value
36322 valueAlign : "left",
36324 getAutoCreate : function()
36326 var hiddenInput = {
36330 cls: 'hidden-number-input'
36334 hiddenInput.name = this.name;
36339 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36341 this.name = hiddenInput.name;
36343 if(cfg.cn.length > 0) {
36344 cfg.cn.push(hiddenInput);
36351 initEvents : function()
36353 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36355 var allowed = "0123456789";
36357 if(this.allowDecimals){
36358 allowed += this.decimalSeparator;
36361 if(this.allowNegative){
36365 if(this.thousandsDelimiter) {
36369 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36371 var keyPress = function(e){
36373 var k = e.getKey();
36375 var c = e.getCharCode();
36378 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36379 allowed.indexOf(String.fromCharCode(c)) === -1
36385 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36389 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36394 this.el.on("keypress", keyPress, this);
36397 validateValue : function(value)
36400 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36404 var num = this.parseValue(value);
36407 this.markInvalid(String.format(this.nanText, value));
36411 if(num < this.minValue){
36412 this.markInvalid(String.format(this.minText, this.minValue));
36416 if(num > this.maxValue){
36417 this.markInvalid(String.format(this.maxText, this.maxValue));
36424 getValue : function()
36426 var v = this.hiddenEl().getValue();
36428 return this.fixPrecision(this.parseValue(v));
36431 parseValue : function(value)
36433 if(this.thousandsDelimiter) {
36435 r = new RegExp(",", "g");
36436 value = value.replace(r, "");
36439 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36440 return isNaN(value) ? '' : value;
36443 fixPrecision : function(value)
36445 if(this.thousandsDelimiter) {
36447 r = new RegExp(",", "g");
36448 value = value.replace(r, "");
36451 var nan = isNaN(value);
36453 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36454 return nan ? '' : value;
36456 return parseFloat(value).toFixed(this.decimalPrecision);
36459 setValue : function(v)
36461 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36467 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36469 this.inputEl().dom.value = (v == '') ? '' :
36470 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36472 if(!this.allowZero && v === '0') {
36473 this.hiddenEl().dom.value = '';
36474 this.inputEl().dom.value = '';
36481 decimalPrecisionFcn : function(v)
36483 return Math.floor(v);
36486 beforeBlur : function()
36488 var v = this.parseValue(this.getRawValue());
36490 if(v || v === 0 || v === ''){
36495 hiddenEl : function()
36497 return this.el.select('input.hidden-number-input',true).first();
36509 * @class Roo.bootstrap.DocumentSlider
36510 * @extends Roo.bootstrap.Component
36511 * Bootstrap DocumentSlider class
36514 * Create a new DocumentViewer
36515 * @param {Object} config The config object
36518 Roo.bootstrap.DocumentSlider = function(config){
36519 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36526 * Fire after initEvent
36527 * @param {Roo.bootstrap.DocumentSlider} this
36532 * Fire after update
36533 * @param {Roo.bootstrap.DocumentSlider} this
36539 * @param {Roo.bootstrap.DocumentSlider} this
36545 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36551 getAutoCreate : function()
36555 cls : 'roo-document-slider',
36559 cls : 'roo-document-slider-header',
36563 cls : 'roo-document-slider-header-title'
36569 cls : 'roo-document-slider-body',
36573 cls : 'roo-document-slider-prev',
36577 cls : 'fa fa-chevron-left'
36583 cls : 'roo-document-slider-thumb',
36587 cls : 'roo-document-slider-image'
36593 cls : 'roo-document-slider-next',
36597 cls : 'fa fa-chevron-right'
36609 initEvents : function()
36611 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36612 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36614 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36615 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36617 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36618 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36620 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36621 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36623 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36624 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36626 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36627 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36629 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36630 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36632 this.thumbEl.on('click', this.onClick, this);
36634 this.prevIndicator.on('click', this.prev, this);
36636 this.nextIndicator.on('click', this.next, this);
36640 initial : function()
36642 if(this.files.length){
36643 this.indicator = 1;
36647 this.fireEvent('initial', this);
36650 update : function()
36652 this.imageEl.attr('src', this.files[this.indicator - 1]);
36654 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36656 this.prevIndicator.show();
36658 if(this.indicator == 1){
36659 this.prevIndicator.hide();
36662 this.nextIndicator.show();
36664 if(this.indicator == this.files.length){
36665 this.nextIndicator.hide();
36668 this.thumbEl.scrollTo('top');
36670 this.fireEvent('update', this);
36673 onClick : function(e)
36675 e.preventDefault();
36677 this.fireEvent('click', this);
36682 e.preventDefault();
36684 this.indicator = Math.max(1, this.indicator - 1);
36691 e.preventDefault();
36693 this.indicator = Math.min(this.files.length, this.indicator + 1);
36707 * @class Roo.bootstrap.RadioSet
36708 * @extends Roo.bootstrap.Input
36709 * Bootstrap RadioSet class
36710 * @cfg {String} indicatorpos (left|right) default left
36711 * @cfg {Boolean} inline (true|false) inline the element (default true)
36712 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36714 * Create a new RadioSet
36715 * @param {Object} config The config object
36718 Roo.bootstrap.RadioSet = function(config){
36720 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36724 Roo.bootstrap.RadioSet.register(this);
36729 * Fires when the element is checked or unchecked.
36730 * @param {Roo.bootstrap.RadioSet} this This radio
36731 * @param {Roo.bootstrap.Radio} item The checked item
36736 * Fires when the element is click.
36737 * @param {Roo.bootstrap.RadioSet} this This radio set
36738 * @param {Roo.bootstrap.Radio} item The checked item
36739 * @param {Roo.EventObject} e The event object
36746 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36754 indicatorpos : 'left',
36756 getAutoCreate : function()
36760 cls : 'roo-radio-set-label',
36764 html : this.fieldLabel
36768 if (Roo.bootstrap.version == 3) {
36771 if(this.indicatorpos == 'left'){
36774 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36775 tooltip : 'This field is required'
36780 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36781 tooltip : 'This field is required'
36787 cls : 'roo-radio-set-items'
36790 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36792 if (align === 'left' && this.fieldLabel.length) {
36795 cls : "roo-radio-set-right",
36801 if(this.labelWidth > 12){
36802 label.style = "width: " + this.labelWidth + 'px';
36805 if(this.labelWidth < 13 && this.labelmd == 0){
36806 this.labelmd = this.labelWidth;
36809 if(this.labellg > 0){
36810 label.cls += ' col-lg-' + this.labellg;
36811 items.cls += ' col-lg-' + (12 - this.labellg);
36814 if(this.labelmd > 0){
36815 label.cls += ' col-md-' + this.labelmd;
36816 items.cls += ' col-md-' + (12 - this.labelmd);
36819 if(this.labelsm > 0){
36820 label.cls += ' col-sm-' + this.labelsm;
36821 items.cls += ' col-sm-' + (12 - this.labelsm);
36824 if(this.labelxs > 0){
36825 label.cls += ' col-xs-' + this.labelxs;
36826 items.cls += ' col-xs-' + (12 - this.labelxs);
36832 cls : 'roo-radio-set',
36836 cls : 'roo-radio-set-input',
36839 value : this.value ? this.value : ''
36846 if(this.weight.length){
36847 cfg.cls += ' roo-radio-' + this.weight;
36851 cfg.cls += ' roo-radio-set-inline';
36855 ['xs','sm','md','lg'].map(function(size){
36856 if (settings[size]) {
36857 cfg.cls += ' col-' + size + '-' + settings[size];
36865 initEvents : function()
36867 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36868 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36870 if(!this.fieldLabel.length){
36871 this.labelEl.hide();
36874 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36875 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36877 this.indicator = this.indicatorEl();
36879 if(this.indicator){
36880 this.indicator.addClass('invisible');
36883 this.originalValue = this.getValue();
36887 inputEl: function ()
36889 return this.el.select('.roo-radio-set-input', true).first();
36892 getChildContainer : function()
36894 return this.itemsEl;
36897 register : function(item)
36899 this.radioes.push(item);
36903 validate : function()
36905 if(this.getVisibilityEl().hasClass('hidden')){
36911 Roo.each(this.radioes, function(i){
36920 if(this.allowBlank) {
36924 if(this.disabled || valid){
36929 this.markInvalid();
36934 markValid : function()
36936 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36937 this.indicatorEl().removeClass('visible');
36938 this.indicatorEl().addClass('invisible');
36942 if (Roo.bootstrap.version == 3) {
36943 this.el.removeClass([this.invalidClass, this.validClass]);
36944 this.el.addClass(this.validClass);
36946 this.el.removeClass(['is-invalid','is-valid']);
36947 this.el.addClass(['is-valid']);
36949 this.fireEvent('valid', this);
36952 markInvalid : function(msg)
36954 if(this.allowBlank || this.disabled){
36958 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36959 this.indicatorEl().removeClass('invisible');
36960 this.indicatorEl().addClass('visible');
36962 if (Roo.bootstrap.version == 3) {
36963 this.el.removeClass([this.invalidClass, this.validClass]);
36964 this.el.addClass(this.invalidClass);
36966 this.el.removeClass(['is-invalid','is-valid']);
36967 this.el.addClass(['is-invalid']);
36970 this.fireEvent('invalid', this, msg);
36974 setValue : function(v, suppressEvent)
36976 if(this.value === v){
36983 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36986 Roo.each(this.radioes, function(i){
36988 i.el.removeClass('checked');
36991 Roo.each(this.radioes, function(i){
36993 if(i.value === v || i.value.toString() === v.toString()){
36995 i.el.addClass('checked');
36997 if(suppressEvent !== true){
36998 this.fireEvent('check', this, i);
37009 clearInvalid : function(){
37011 if(!this.el || this.preventMark){
37015 this.el.removeClass([this.invalidClass]);
37017 this.fireEvent('valid', this);
37022 Roo.apply(Roo.bootstrap.RadioSet, {
37026 register : function(set)
37028 this.groups[set.name] = set;
37031 get: function(name)
37033 if (typeof(this.groups[name]) == 'undefined') {
37037 return this.groups[name] ;
37043 * Ext JS Library 1.1.1
37044 * Copyright(c) 2006-2007, Ext JS, LLC.
37046 * Originally Released Under LGPL - original licence link has changed is not relivant.
37049 * <script type="text/javascript">
37054 * @class Roo.bootstrap.SplitBar
37055 * @extends Roo.util.Observable
37056 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37060 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37061 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37062 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37063 split.minSize = 100;
37064 split.maxSize = 600;
37065 split.animate = true;
37066 split.on('moved', splitterMoved);
37069 * Create a new SplitBar
37070 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37071 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37072 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37073 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37074 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37075 position of the SplitBar).
37077 Roo.bootstrap.SplitBar = function(cfg){
37082 // dragElement : elm
37083 // resizingElement: el,
37085 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37086 // placement : Roo.bootstrap.SplitBar.LEFT ,
37087 // existingProxy ???
37090 this.el = Roo.get(cfg.dragElement, true);
37091 this.el.dom.unselectable = "on";
37093 this.resizingEl = Roo.get(cfg.resizingElement, true);
37097 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37098 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37101 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37104 * The minimum size of the resizing element. (Defaults to 0)
37110 * The maximum size of the resizing element. (Defaults to 2000)
37113 this.maxSize = 2000;
37116 * Whether to animate the transition to the new size
37119 this.animate = false;
37122 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37125 this.useShim = false;
37130 if(!cfg.existingProxy){
37132 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37134 this.proxy = Roo.get(cfg.existingProxy).dom;
37137 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37140 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37143 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37146 this.dragSpecs = {};
37149 * @private The adapter to use to positon and resize elements
37151 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37152 this.adapter.init(this);
37154 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37156 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37157 this.el.addClass("roo-splitbar-h");
37160 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37161 this.el.addClass("roo-splitbar-v");
37167 * Fires when the splitter is moved (alias for {@link #event-moved})
37168 * @param {Roo.bootstrap.SplitBar} this
37169 * @param {Number} newSize the new width or height
37174 * Fires when the splitter is moved
37175 * @param {Roo.bootstrap.SplitBar} this
37176 * @param {Number} newSize the new width or height
37180 * @event beforeresize
37181 * Fires before the splitter is dragged
37182 * @param {Roo.bootstrap.SplitBar} this
37184 "beforeresize" : true,
37186 "beforeapply" : true
37189 Roo.util.Observable.call(this);
37192 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37193 onStartProxyDrag : function(x, y){
37194 this.fireEvent("beforeresize", this);
37196 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37198 o.enableDisplayMode("block");
37199 // all splitbars share the same overlay
37200 Roo.bootstrap.SplitBar.prototype.overlay = o;
37202 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37203 this.overlay.show();
37204 Roo.get(this.proxy).setDisplayed("block");
37205 var size = this.adapter.getElementSize(this);
37206 this.activeMinSize = this.getMinimumSize();;
37207 this.activeMaxSize = this.getMaximumSize();;
37208 var c1 = size - this.activeMinSize;
37209 var c2 = Math.max(this.activeMaxSize - size, 0);
37210 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37211 this.dd.resetConstraints();
37212 this.dd.setXConstraint(
37213 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37214 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37216 this.dd.setYConstraint(0, 0);
37218 this.dd.resetConstraints();
37219 this.dd.setXConstraint(0, 0);
37220 this.dd.setYConstraint(
37221 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37222 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37225 this.dragSpecs.startSize = size;
37226 this.dragSpecs.startPoint = [x, y];
37227 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37231 * @private Called after the drag operation by the DDProxy
37233 onEndProxyDrag : function(e){
37234 Roo.get(this.proxy).setDisplayed(false);
37235 var endPoint = Roo.lib.Event.getXY(e);
37237 this.overlay.hide();
37240 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37241 newSize = this.dragSpecs.startSize +
37242 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37243 endPoint[0] - this.dragSpecs.startPoint[0] :
37244 this.dragSpecs.startPoint[0] - endPoint[0]
37247 newSize = this.dragSpecs.startSize +
37248 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37249 endPoint[1] - this.dragSpecs.startPoint[1] :
37250 this.dragSpecs.startPoint[1] - endPoint[1]
37253 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37254 if(newSize != this.dragSpecs.startSize){
37255 if(this.fireEvent('beforeapply', this, newSize) !== false){
37256 this.adapter.setElementSize(this, newSize);
37257 this.fireEvent("moved", this, newSize);
37258 this.fireEvent("resize", this, newSize);
37264 * Get the adapter this SplitBar uses
37265 * @return The adapter object
37267 getAdapter : function(){
37268 return this.adapter;
37272 * Set the adapter this SplitBar uses
37273 * @param {Object} adapter A SplitBar adapter object
37275 setAdapter : function(adapter){
37276 this.adapter = adapter;
37277 this.adapter.init(this);
37281 * Gets the minimum size for the resizing element
37282 * @return {Number} The minimum size
37284 getMinimumSize : function(){
37285 return this.minSize;
37289 * Sets the minimum size for the resizing element
37290 * @param {Number} minSize The minimum size
37292 setMinimumSize : function(minSize){
37293 this.minSize = minSize;
37297 * Gets the maximum size for the resizing element
37298 * @return {Number} The maximum size
37300 getMaximumSize : function(){
37301 return this.maxSize;
37305 * Sets the maximum size for the resizing element
37306 * @param {Number} maxSize The maximum size
37308 setMaximumSize : function(maxSize){
37309 this.maxSize = maxSize;
37313 * Sets the initialize size for the resizing element
37314 * @param {Number} size The initial size
37316 setCurrentSize : function(size){
37317 var oldAnimate = this.animate;
37318 this.animate = false;
37319 this.adapter.setElementSize(this, size);
37320 this.animate = oldAnimate;
37324 * Destroy this splitbar.
37325 * @param {Boolean} removeEl True to remove the element
37327 destroy : function(removeEl){
37329 this.shim.remove();
37332 this.proxy.parentNode.removeChild(this.proxy);
37340 * @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.
37342 Roo.bootstrap.SplitBar.createProxy = function(dir){
37343 var proxy = new Roo.Element(document.createElement("div"));
37344 proxy.unselectable();
37345 var cls = 'roo-splitbar-proxy';
37346 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37347 document.body.appendChild(proxy.dom);
37352 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37353 * Default Adapter. It assumes the splitter and resizing element are not positioned
37354 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37356 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37359 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37360 // do nothing for now
37361 init : function(s){
37365 * Called before drag operations to get the current size of the resizing element.
37366 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37368 getElementSize : function(s){
37369 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37370 return s.resizingEl.getWidth();
37372 return s.resizingEl.getHeight();
37377 * Called after drag operations to set the size of the resizing element.
37378 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37379 * @param {Number} newSize The new size to set
37380 * @param {Function} onComplete A function to be invoked when resizing is complete
37382 setElementSize : function(s, newSize, onComplete){
37383 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37385 s.resizingEl.setWidth(newSize);
37387 onComplete(s, newSize);
37390 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37395 s.resizingEl.setHeight(newSize);
37397 onComplete(s, newSize);
37400 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37407 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37408 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37409 * Adapter that moves the splitter element to align with the resized sizing element.
37410 * Used with an absolute positioned SplitBar.
37411 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37412 * document.body, make sure you assign an id to the body element.
37414 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37415 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37416 this.container = Roo.get(container);
37419 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37420 init : function(s){
37421 this.basic.init(s);
37424 getElementSize : function(s){
37425 return this.basic.getElementSize(s);
37428 setElementSize : function(s, newSize, onComplete){
37429 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37432 moveSplitter : function(s){
37433 var yes = Roo.bootstrap.SplitBar;
37434 switch(s.placement){
37436 s.el.setX(s.resizingEl.getRight());
37439 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37442 s.el.setY(s.resizingEl.getBottom());
37445 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37452 * Orientation constant - Create a vertical SplitBar
37456 Roo.bootstrap.SplitBar.VERTICAL = 1;
37459 * Orientation constant - Create a horizontal SplitBar
37463 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37466 * Placement constant - The resizing element is to the left of the splitter element
37470 Roo.bootstrap.SplitBar.LEFT = 1;
37473 * Placement constant - The resizing element is to the right of the splitter element
37477 Roo.bootstrap.SplitBar.RIGHT = 2;
37480 * Placement constant - The resizing element is positioned above the splitter element
37484 Roo.bootstrap.SplitBar.TOP = 3;
37487 * Placement constant - The resizing element is positioned under splitter element
37491 Roo.bootstrap.SplitBar.BOTTOM = 4;
37492 Roo.namespace("Roo.bootstrap.layout");/*
37494 * Ext JS Library 1.1.1
37495 * Copyright(c) 2006-2007, Ext JS, LLC.
37497 * Originally Released Under LGPL - original licence link has changed is not relivant.
37500 * <script type="text/javascript">
37504 * @class Roo.bootstrap.layout.Manager
37505 * @extends Roo.bootstrap.Component
37506 * Base class for layout managers.
37508 Roo.bootstrap.layout.Manager = function(config)
37510 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37516 /** false to disable window resize monitoring @type Boolean */
37517 this.monitorWindowResize = true;
37522 * Fires when a layout is performed.
37523 * @param {Roo.LayoutManager} this
37527 * @event regionresized
37528 * Fires when the user resizes a region.
37529 * @param {Roo.LayoutRegion} region The resized region
37530 * @param {Number} newSize The new size (width for east/west, height for north/south)
37532 "regionresized" : true,
37534 * @event regioncollapsed
37535 * Fires when a region is collapsed.
37536 * @param {Roo.LayoutRegion} region The collapsed region
37538 "regioncollapsed" : true,
37540 * @event regionexpanded
37541 * Fires when a region is expanded.
37542 * @param {Roo.LayoutRegion} region The expanded region
37544 "regionexpanded" : true
37546 this.updating = false;
37549 this.el = Roo.get(config.el);
37555 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37560 monitorWindowResize : true,
37566 onRender : function(ct, position)
37569 this.el = Roo.get(ct);
37572 //this.fireEvent('render',this);
37576 initEvents: function()
37580 // ie scrollbar fix
37581 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37582 document.body.scroll = "no";
37583 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37584 this.el.position('relative');
37586 this.id = this.el.id;
37587 this.el.addClass("roo-layout-container");
37588 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37589 if(this.el.dom != document.body ) {
37590 this.el.on('resize', this.layout,this);
37591 this.el.on('show', this.layout,this);
37597 * Returns true if this layout is currently being updated
37598 * @return {Boolean}
37600 isUpdating : function(){
37601 return this.updating;
37605 * Suspend the LayoutManager from doing auto-layouts while
37606 * making multiple add or remove calls
37608 beginUpdate : function(){
37609 this.updating = true;
37613 * Restore auto-layouts and optionally disable the manager from performing a layout
37614 * @param {Boolean} noLayout true to disable a layout update
37616 endUpdate : function(noLayout){
37617 this.updating = false;
37623 layout: function(){
37627 onRegionResized : function(region, newSize){
37628 this.fireEvent("regionresized", region, newSize);
37632 onRegionCollapsed : function(region){
37633 this.fireEvent("regioncollapsed", region);
37636 onRegionExpanded : function(region){
37637 this.fireEvent("regionexpanded", region);
37641 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37642 * performs box-model adjustments.
37643 * @return {Object} The size as an object {width: (the width), height: (the height)}
37645 getViewSize : function()
37648 if(this.el.dom != document.body){
37649 size = this.el.getSize();
37651 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37653 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37654 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37659 * Returns the Element this layout is bound to.
37660 * @return {Roo.Element}
37662 getEl : function(){
37667 * Returns the specified region.
37668 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37669 * @return {Roo.LayoutRegion}
37671 getRegion : function(target){
37672 return this.regions[target.toLowerCase()];
37675 onWindowResize : function(){
37676 if(this.monitorWindowResize){
37683 * Ext JS Library 1.1.1
37684 * Copyright(c) 2006-2007, Ext JS, LLC.
37686 * Originally Released Under LGPL - original licence link has changed is not relivant.
37689 * <script type="text/javascript">
37692 * @class Roo.bootstrap.layout.Border
37693 * @extends Roo.bootstrap.layout.Manager
37694 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37695 * please see: examples/bootstrap/nested.html<br><br>
37697 <b>The container the layout is rendered into can be either the body element or any other element.
37698 If it is not the body element, the container needs to either be an absolute positioned element,
37699 or you will need to add "position:relative" to the css of the container. You will also need to specify
37700 the container size if it is not the body element.</b>
37703 * Create a new Border
37704 * @param {Object} config Configuration options
37706 Roo.bootstrap.layout.Border = function(config){
37707 config = config || {};
37708 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37712 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37713 if(config[region]){
37714 config[region].region = region;
37715 this.addRegion(config[region]);
37721 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37723 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37725 parent : false, // this might point to a 'nest' or a ???
37728 * Creates and adds a new region if it doesn't already exist.
37729 * @param {String} target The target region key (north, south, east, west or center).
37730 * @param {Object} config The regions config object
37731 * @return {BorderLayoutRegion} The new region
37733 addRegion : function(config)
37735 if(!this.regions[config.region]){
37736 var r = this.factory(config);
37737 this.bindRegion(r);
37739 return this.regions[config.region];
37743 bindRegion : function(r){
37744 this.regions[r.config.region] = r;
37746 r.on("visibilitychange", this.layout, this);
37747 r.on("paneladded", this.layout, this);
37748 r.on("panelremoved", this.layout, this);
37749 r.on("invalidated", this.layout, this);
37750 r.on("resized", this.onRegionResized, this);
37751 r.on("collapsed", this.onRegionCollapsed, this);
37752 r.on("expanded", this.onRegionExpanded, this);
37756 * Performs a layout update.
37758 layout : function()
37760 if(this.updating) {
37764 // render all the rebions if they have not been done alreayd?
37765 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37766 if(this.regions[region] && !this.regions[region].bodyEl){
37767 this.regions[region].onRender(this.el)
37771 var size = this.getViewSize();
37772 var w = size.width;
37773 var h = size.height;
37778 //var x = 0, y = 0;
37780 var rs = this.regions;
37781 var north = rs["north"];
37782 var south = rs["south"];
37783 var west = rs["west"];
37784 var east = rs["east"];
37785 var center = rs["center"];
37786 //if(this.hideOnLayout){ // not supported anymore
37787 //c.el.setStyle("display", "none");
37789 if(north && north.isVisible()){
37790 var b = north.getBox();
37791 var m = north.getMargins();
37792 b.width = w - (m.left+m.right);
37795 centerY = b.height + b.y + m.bottom;
37796 centerH -= centerY;
37797 north.updateBox(this.safeBox(b));
37799 if(south && south.isVisible()){
37800 var b = south.getBox();
37801 var m = south.getMargins();
37802 b.width = w - (m.left+m.right);
37804 var totalHeight = (b.height + m.top + m.bottom);
37805 b.y = h - totalHeight + m.top;
37806 centerH -= totalHeight;
37807 south.updateBox(this.safeBox(b));
37809 if(west && west.isVisible()){
37810 var b = west.getBox();
37811 var m = west.getMargins();
37812 b.height = centerH - (m.top+m.bottom);
37814 b.y = centerY + m.top;
37815 var totalWidth = (b.width + m.left + m.right);
37816 centerX += totalWidth;
37817 centerW -= totalWidth;
37818 west.updateBox(this.safeBox(b));
37820 if(east && east.isVisible()){
37821 var b = east.getBox();
37822 var m = east.getMargins();
37823 b.height = centerH - (m.top+m.bottom);
37824 var totalWidth = (b.width + m.left + m.right);
37825 b.x = w - totalWidth + m.left;
37826 b.y = centerY + m.top;
37827 centerW -= totalWidth;
37828 east.updateBox(this.safeBox(b));
37831 var m = center.getMargins();
37833 x: centerX + m.left,
37834 y: centerY + m.top,
37835 width: centerW - (m.left+m.right),
37836 height: centerH - (m.top+m.bottom)
37838 //if(this.hideOnLayout){
37839 //center.el.setStyle("display", "block");
37841 center.updateBox(this.safeBox(centerBox));
37844 this.fireEvent("layout", this);
37848 safeBox : function(box){
37849 box.width = Math.max(0, box.width);
37850 box.height = Math.max(0, box.height);
37855 * Adds a ContentPanel (or subclass) to this layout.
37856 * @param {String} target The target region key (north, south, east, west or center).
37857 * @param {Roo.ContentPanel} panel The panel to add
37858 * @return {Roo.ContentPanel} The added panel
37860 add : function(target, panel){
37862 target = target.toLowerCase();
37863 return this.regions[target].add(panel);
37867 * Remove a ContentPanel (or subclass) to this layout.
37868 * @param {String} target The target region key (north, south, east, west or center).
37869 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37870 * @return {Roo.ContentPanel} The removed panel
37872 remove : function(target, panel){
37873 target = target.toLowerCase();
37874 return this.regions[target].remove(panel);
37878 * Searches all regions for a panel with the specified id
37879 * @param {String} panelId
37880 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37882 findPanel : function(panelId){
37883 var rs = this.regions;
37884 for(var target in rs){
37885 if(typeof rs[target] != "function"){
37886 var p = rs[target].getPanel(panelId);
37896 * Searches all regions for a panel with the specified id and activates (shows) it.
37897 * @param {String/ContentPanel} panelId The panels id or the panel itself
37898 * @return {Roo.ContentPanel} The shown panel or null
37900 showPanel : function(panelId) {
37901 var rs = this.regions;
37902 for(var target in rs){
37903 var r = rs[target];
37904 if(typeof r != "function"){
37905 if(r.hasPanel(panelId)){
37906 return r.showPanel(panelId);
37914 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37915 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37918 restoreState : function(provider){
37920 provider = Roo.state.Manager;
37922 var sm = new Roo.LayoutStateManager();
37923 sm.init(this, provider);
37929 * Adds a xtype elements to the layout.
37933 xtype : 'ContentPanel',
37940 xtype : 'NestedLayoutPanel',
37946 items : [ ... list of content panels or nested layout panels.. ]
37950 * @param {Object} cfg Xtype definition of item to add.
37952 addxtype : function(cfg)
37954 // basically accepts a pannel...
37955 // can accept a layout region..!?!?
37956 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37959 // theory? children can only be panels??
37961 //if (!cfg.xtype.match(/Panel$/)) {
37966 if (typeof(cfg.region) == 'undefined') {
37967 Roo.log("Failed to add Panel, region was not set");
37971 var region = cfg.region;
37977 xitems = cfg.items;
37982 if ( region == 'center') {
37983 Roo.log("Center: " + cfg.title);
37989 case 'Content': // ContentPanel (el, cfg)
37990 case 'Scroll': // ContentPanel (el, cfg)
37992 cfg.autoCreate = cfg.autoCreate || true;
37993 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37995 // var el = this.el.createChild();
37996 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37999 this.add(region, ret);
38003 case 'TreePanel': // our new panel!
38004 cfg.el = this.el.createChild();
38005 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38006 this.add(region, ret);
38011 // create a new Layout (which is a Border Layout...
38013 var clayout = cfg.layout;
38014 clayout.el = this.el.createChild();
38015 clayout.items = clayout.items || [];
38019 // replace this exitems with the clayout ones..
38020 xitems = clayout.items;
38022 // force background off if it's in center...
38023 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38024 cfg.background = false;
38026 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38029 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38030 //console.log('adding nested layout panel ' + cfg.toSource());
38031 this.add(region, ret);
38032 nb = {}; /// find first...
38037 // needs grid and region
38039 //var el = this.getRegion(region).el.createChild();
38041 *var el = this.el.createChild();
38042 // create the grid first...
38043 cfg.grid.container = el;
38044 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38047 if (region == 'center' && this.active ) {
38048 cfg.background = false;
38051 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38053 this.add(region, ret);
38055 if (cfg.background) {
38056 // render grid on panel activation (if panel background)
38057 ret.on('activate', function(gp) {
38058 if (!gp.grid.rendered) {
38059 // gp.grid.render(el);
38063 // cfg.grid.render(el);
38069 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38070 // it was the old xcomponent building that caused this before.
38071 // espeically if border is the top element in the tree.
38081 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38083 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38084 this.add(region, ret);
38088 throw "Can not add '" + cfg.xtype + "' to Border";
38094 this.beginUpdate();
38098 Roo.each(xitems, function(i) {
38099 region = nb && i.region ? i.region : false;
38101 var add = ret.addxtype(i);
38104 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38105 if (!i.background) {
38106 abn[region] = nb[region] ;
38113 // make the last non-background panel active..
38114 //if (nb) { Roo.log(abn); }
38117 for(var r in abn) {
38118 region = this.getRegion(r);
38120 // tried using nb[r], but it does not work..
38122 region.showPanel(abn[r]);
38133 factory : function(cfg)
38136 var validRegions = Roo.bootstrap.layout.Border.regions;
38138 var target = cfg.region;
38141 var r = Roo.bootstrap.layout;
38145 return new r.North(cfg);
38147 return new r.South(cfg);
38149 return new r.East(cfg);
38151 return new r.West(cfg);
38153 return new r.Center(cfg);
38155 throw 'Layout region "'+target+'" not supported.';
38162 * Ext JS Library 1.1.1
38163 * Copyright(c) 2006-2007, Ext JS, LLC.
38165 * Originally Released Under LGPL - original licence link has changed is not relivant.
38168 * <script type="text/javascript">
38172 * @class Roo.bootstrap.layout.Basic
38173 * @extends Roo.util.Observable
38174 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38175 * and does not have a titlebar, tabs or any other features. All it does is size and position
38176 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38177 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38178 * @cfg {string} region the region that it inhabits..
38179 * @cfg {bool} skipConfig skip config?
38183 Roo.bootstrap.layout.Basic = function(config){
38185 this.mgr = config.mgr;
38187 this.position = config.region;
38189 var skipConfig = config.skipConfig;
38193 * @scope Roo.BasicLayoutRegion
38197 * @event beforeremove
38198 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38199 * @param {Roo.LayoutRegion} this
38200 * @param {Roo.ContentPanel} panel The panel
38201 * @param {Object} e The cancel event object
38203 "beforeremove" : true,
38205 * @event invalidated
38206 * Fires when the layout for this region is changed.
38207 * @param {Roo.LayoutRegion} this
38209 "invalidated" : true,
38211 * @event visibilitychange
38212 * Fires when this region is shown or hidden
38213 * @param {Roo.LayoutRegion} this
38214 * @param {Boolean} visibility true or false
38216 "visibilitychange" : true,
38218 * @event paneladded
38219 * Fires when a panel is added.
38220 * @param {Roo.LayoutRegion} this
38221 * @param {Roo.ContentPanel} panel The panel
38223 "paneladded" : true,
38225 * @event panelremoved
38226 * Fires when a panel is removed.
38227 * @param {Roo.LayoutRegion} this
38228 * @param {Roo.ContentPanel} panel The panel
38230 "panelremoved" : true,
38232 * @event beforecollapse
38233 * Fires when this region before collapse.
38234 * @param {Roo.LayoutRegion} this
38236 "beforecollapse" : true,
38239 * Fires when this region is collapsed.
38240 * @param {Roo.LayoutRegion} this
38242 "collapsed" : true,
38245 * Fires when this region is expanded.
38246 * @param {Roo.LayoutRegion} this
38251 * Fires when this region is slid into view.
38252 * @param {Roo.LayoutRegion} this
38254 "slideshow" : true,
38257 * Fires when this region slides out of view.
38258 * @param {Roo.LayoutRegion} this
38260 "slidehide" : true,
38262 * @event panelactivated
38263 * Fires when a panel is activated.
38264 * @param {Roo.LayoutRegion} this
38265 * @param {Roo.ContentPanel} panel The activated panel
38267 "panelactivated" : true,
38270 * Fires when the user resizes this region.
38271 * @param {Roo.LayoutRegion} this
38272 * @param {Number} newSize The new size (width for east/west, height for north/south)
38276 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38277 this.panels = new Roo.util.MixedCollection();
38278 this.panels.getKey = this.getPanelId.createDelegate(this);
38280 this.activePanel = null;
38281 // ensure listeners are added...
38283 if (config.listeners || config.events) {
38284 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38285 listeners : config.listeners || {},
38286 events : config.events || {}
38290 if(skipConfig !== true){
38291 this.applyConfig(config);
38295 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38297 getPanelId : function(p){
38301 applyConfig : function(config){
38302 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38303 this.config = config;
38308 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38309 * the width, for horizontal (north, south) the height.
38310 * @param {Number} newSize The new width or height
38312 resizeTo : function(newSize){
38313 var el = this.el ? this.el :
38314 (this.activePanel ? this.activePanel.getEl() : null);
38316 switch(this.position){
38319 el.setWidth(newSize);
38320 this.fireEvent("resized", this, newSize);
38324 el.setHeight(newSize);
38325 this.fireEvent("resized", this, newSize);
38331 getBox : function(){
38332 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38335 getMargins : function(){
38336 return this.margins;
38339 updateBox : function(box){
38341 var el = this.activePanel.getEl();
38342 el.dom.style.left = box.x + "px";
38343 el.dom.style.top = box.y + "px";
38344 this.activePanel.setSize(box.width, box.height);
38348 * Returns the container element for this region.
38349 * @return {Roo.Element}
38351 getEl : function(){
38352 return this.activePanel;
38356 * Returns true if this region is currently visible.
38357 * @return {Boolean}
38359 isVisible : function(){
38360 return this.activePanel ? true : false;
38363 setActivePanel : function(panel){
38364 panel = this.getPanel(panel);
38365 if(this.activePanel && this.activePanel != panel){
38366 this.activePanel.setActiveState(false);
38367 this.activePanel.getEl().setLeftTop(-10000,-10000);
38369 this.activePanel = panel;
38370 panel.setActiveState(true);
38372 panel.setSize(this.box.width, this.box.height);
38374 this.fireEvent("panelactivated", this, panel);
38375 this.fireEvent("invalidated");
38379 * Show the specified panel.
38380 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38381 * @return {Roo.ContentPanel} The shown panel or null
38383 showPanel : function(panel){
38384 panel = this.getPanel(panel);
38386 this.setActivePanel(panel);
38392 * Get the active panel for this region.
38393 * @return {Roo.ContentPanel} The active panel or null
38395 getActivePanel : function(){
38396 return this.activePanel;
38400 * Add the passed ContentPanel(s)
38401 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38402 * @return {Roo.ContentPanel} The panel added (if only one was added)
38404 add : function(panel){
38405 if(arguments.length > 1){
38406 for(var i = 0, len = arguments.length; i < len; i++) {
38407 this.add(arguments[i]);
38411 if(this.hasPanel(panel)){
38412 this.showPanel(panel);
38415 var el = panel.getEl();
38416 if(el.dom.parentNode != this.mgr.el.dom){
38417 this.mgr.el.dom.appendChild(el.dom);
38419 if(panel.setRegion){
38420 panel.setRegion(this);
38422 this.panels.add(panel);
38423 el.setStyle("position", "absolute");
38424 if(!panel.background){
38425 this.setActivePanel(panel);
38426 if(this.config.initialSize && this.panels.getCount()==1){
38427 this.resizeTo(this.config.initialSize);
38430 this.fireEvent("paneladded", this, panel);
38435 * Returns true if the panel is in this region.
38436 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38437 * @return {Boolean}
38439 hasPanel : function(panel){
38440 if(typeof panel == "object"){ // must be panel obj
38441 panel = panel.getId();
38443 return this.getPanel(panel) ? true : false;
38447 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38448 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38449 * @param {Boolean} preservePanel Overrides the config preservePanel option
38450 * @return {Roo.ContentPanel} The panel that was removed
38452 remove : function(panel, preservePanel){
38453 panel = this.getPanel(panel);
38458 this.fireEvent("beforeremove", this, panel, e);
38459 if(e.cancel === true){
38462 var panelId = panel.getId();
38463 this.panels.removeKey(panelId);
38468 * Returns the panel specified or null if it's not in this region.
38469 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38470 * @return {Roo.ContentPanel}
38472 getPanel : function(id){
38473 if(typeof id == "object"){ // must be panel obj
38476 return this.panels.get(id);
38480 * Returns this regions position (north/south/east/west/center).
38483 getPosition: function(){
38484 return this.position;
38488 * Ext JS Library 1.1.1
38489 * Copyright(c) 2006-2007, Ext JS, LLC.
38491 * Originally Released Under LGPL - original licence link has changed is not relivant.
38494 * <script type="text/javascript">
38498 * @class Roo.bootstrap.layout.Region
38499 * @extends Roo.bootstrap.layout.Basic
38500 * This class represents a region in a layout manager.
38502 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38503 * @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})
38504 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38505 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38506 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38507 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38508 * @cfg {String} title The title for the region (overrides panel titles)
38509 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38510 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38511 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38512 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38513 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38514 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38515 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38516 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38517 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38518 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38520 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38521 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38522 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38523 * @cfg {Number} width For East/West panels
38524 * @cfg {Number} height For North/South panels
38525 * @cfg {Boolean} split To show the splitter
38526 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38528 * @cfg {string} cls Extra CSS classes to add to region
38530 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38531 * @cfg {string} region the region that it inhabits..
38534 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38535 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38537 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38538 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38539 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38541 Roo.bootstrap.layout.Region = function(config)
38543 this.applyConfig(config);
38545 var mgr = config.mgr;
38546 var pos = config.region;
38547 config.skipConfig = true;
38548 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38551 this.onRender(mgr.el);
38554 this.visible = true;
38555 this.collapsed = false;
38556 this.unrendered_panels = [];
38559 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38561 position: '', // set by wrapper (eg. north/south etc..)
38562 unrendered_panels : null, // unrendered panels.
38564 tabPosition : false,
38566 mgr: false, // points to 'Border'
38569 createBody : function(){
38570 /** This region's body element
38571 * @type Roo.Element */
38572 this.bodyEl = this.el.createChild({
38574 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38578 onRender: function(ctr, pos)
38580 var dh = Roo.DomHelper;
38581 /** This region's container element
38582 * @type Roo.Element */
38583 this.el = dh.append(ctr.dom, {
38585 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38587 /** This region's title element
38588 * @type Roo.Element */
38590 this.titleEl = dh.append(this.el.dom, {
38592 unselectable: "on",
38593 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38595 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38596 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38600 this.titleEl.enableDisplayMode();
38601 /** This region's title text element
38602 * @type HTMLElement */
38603 this.titleTextEl = this.titleEl.dom.firstChild;
38604 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38606 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38607 this.closeBtn.enableDisplayMode();
38608 this.closeBtn.on("click", this.closeClicked, this);
38609 this.closeBtn.hide();
38611 this.createBody(this.config);
38612 if(this.config.hideWhenEmpty){
38614 this.on("paneladded", this.validateVisibility, this);
38615 this.on("panelremoved", this.validateVisibility, this);
38617 if(this.autoScroll){
38618 this.bodyEl.setStyle("overflow", "auto");
38620 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38622 //if(c.titlebar !== false){
38623 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38624 this.titleEl.hide();
38626 this.titleEl.show();
38627 if(this.config.title){
38628 this.titleTextEl.innerHTML = this.config.title;
38632 if(this.config.collapsed){
38633 this.collapse(true);
38635 if(this.config.hidden){
38639 if (this.unrendered_panels && this.unrendered_panels.length) {
38640 for (var i =0;i< this.unrendered_panels.length; i++) {
38641 this.add(this.unrendered_panels[i]);
38643 this.unrendered_panels = null;
38649 applyConfig : function(c)
38652 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38653 var dh = Roo.DomHelper;
38654 if(c.titlebar !== false){
38655 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38656 this.collapseBtn.on("click", this.collapse, this);
38657 this.collapseBtn.enableDisplayMode();
38659 if(c.showPin === true || this.showPin){
38660 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38661 this.stickBtn.enableDisplayMode();
38662 this.stickBtn.on("click", this.expand, this);
38663 this.stickBtn.hide();
38668 /** This region's collapsed element
38669 * @type Roo.Element */
38672 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38673 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38676 if(c.floatable !== false){
38677 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38678 this.collapsedEl.on("click", this.collapseClick, this);
38681 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38682 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38683 id: "message", unselectable: "on", style:{"float":"left"}});
38684 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38686 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38687 this.expandBtn.on("click", this.expand, this);
38691 if(this.collapseBtn){
38692 this.collapseBtn.setVisible(c.collapsible == true);
38695 this.cmargins = c.cmargins || this.cmargins ||
38696 (this.position == "west" || this.position == "east" ?
38697 {top: 0, left: 2, right:2, bottom: 0} :
38698 {top: 2, left: 0, right:0, bottom: 2});
38700 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38703 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38705 this.autoScroll = c.autoScroll || false;
38710 this.duration = c.duration || .30;
38711 this.slideDuration = c.slideDuration || .45;
38716 * Returns true if this region is currently visible.
38717 * @return {Boolean}
38719 isVisible : function(){
38720 return this.visible;
38724 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38725 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38727 //setCollapsedTitle : function(title){
38728 // title = title || " ";
38729 // if(this.collapsedTitleTextEl){
38730 // this.collapsedTitleTextEl.innerHTML = title;
38734 getBox : function(){
38736 // if(!this.collapsed){
38737 b = this.el.getBox(false, true);
38739 // b = this.collapsedEl.getBox(false, true);
38744 getMargins : function(){
38745 return this.margins;
38746 //return this.collapsed ? this.cmargins : this.margins;
38749 highlight : function(){
38750 this.el.addClass("x-layout-panel-dragover");
38753 unhighlight : function(){
38754 this.el.removeClass("x-layout-panel-dragover");
38757 updateBox : function(box)
38759 if (!this.bodyEl) {
38760 return; // not rendered yet..
38764 if(!this.collapsed){
38765 this.el.dom.style.left = box.x + "px";
38766 this.el.dom.style.top = box.y + "px";
38767 this.updateBody(box.width, box.height);
38769 this.collapsedEl.dom.style.left = box.x + "px";
38770 this.collapsedEl.dom.style.top = box.y + "px";
38771 this.collapsedEl.setSize(box.width, box.height);
38774 this.tabs.autoSizeTabs();
38778 updateBody : function(w, h)
38781 this.el.setWidth(w);
38782 w -= this.el.getBorderWidth("rl");
38783 if(this.config.adjustments){
38784 w += this.config.adjustments[0];
38787 if(h !== null && h > 0){
38788 this.el.setHeight(h);
38789 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38790 h -= this.el.getBorderWidth("tb");
38791 if(this.config.adjustments){
38792 h += this.config.adjustments[1];
38794 this.bodyEl.setHeight(h);
38796 h = this.tabs.syncHeight(h);
38799 if(this.panelSize){
38800 w = w !== null ? w : this.panelSize.width;
38801 h = h !== null ? h : this.panelSize.height;
38803 if(this.activePanel){
38804 var el = this.activePanel.getEl();
38805 w = w !== null ? w : el.getWidth();
38806 h = h !== null ? h : el.getHeight();
38807 this.panelSize = {width: w, height: h};
38808 this.activePanel.setSize(w, h);
38810 if(Roo.isIE && this.tabs){
38811 this.tabs.el.repaint();
38816 * Returns the container element for this region.
38817 * @return {Roo.Element}
38819 getEl : function(){
38824 * Hides this region.
38827 //if(!this.collapsed){
38828 this.el.dom.style.left = "-2000px";
38831 // this.collapsedEl.dom.style.left = "-2000px";
38832 // this.collapsedEl.hide();
38834 this.visible = false;
38835 this.fireEvent("visibilitychange", this, false);
38839 * Shows this region if it was previously hidden.
38842 //if(!this.collapsed){
38845 // this.collapsedEl.show();
38847 this.visible = true;
38848 this.fireEvent("visibilitychange", this, true);
38851 closeClicked : function(){
38852 if(this.activePanel){
38853 this.remove(this.activePanel);
38857 collapseClick : function(e){
38859 e.stopPropagation();
38862 e.stopPropagation();
38868 * Collapses this region.
38869 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38872 collapse : function(skipAnim, skipCheck = false){
38873 if(this.collapsed) {
38877 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38879 this.collapsed = true;
38881 this.split.el.hide();
38883 if(this.config.animate && skipAnim !== true){
38884 this.fireEvent("invalidated", this);
38885 this.animateCollapse();
38887 this.el.setLocation(-20000,-20000);
38889 this.collapsedEl.show();
38890 this.fireEvent("collapsed", this);
38891 this.fireEvent("invalidated", this);
38897 animateCollapse : function(){
38902 * Expands this region if it was previously collapsed.
38903 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38904 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38907 expand : function(e, skipAnim){
38909 e.stopPropagation();
38911 if(!this.collapsed || this.el.hasActiveFx()) {
38915 this.afterSlideIn();
38918 this.collapsed = false;
38919 if(this.config.animate && skipAnim !== true){
38920 this.animateExpand();
38924 this.split.el.show();
38926 this.collapsedEl.setLocation(-2000,-2000);
38927 this.collapsedEl.hide();
38928 this.fireEvent("invalidated", this);
38929 this.fireEvent("expanded", this);
38933 animateExpand : function(){
38937 initTabs : function()
38939 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38941 var ts = new Roo.bootstrap.panel.Tabs({
38942 el: this.bodyEl.dom,
38944 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38945 disableTooltips: this.config.disableTabTips,
38946 toolbar : this.config.toolbar
38949 if(this.config.hideTabs){
38950 ts.stripWrap.setDisplayed(false);
38953 ts.resizeTabs = this.config.resizeTabs === true;
38954 ts.minTabWidth = this.config.minTabWidth || 40;
38955 ts.maxTabWidth = this.config.maxTabWidth || 250;
38956 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38957 ts.monitorResize = false;
38958 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38959 ts.bodyEl.addClass('roo-layout-tabs-body');
38960 this.panels.each(this.initPanelAsTab, this);
38963 initPanelAsTab : function(panel){
38964 var ti = this.tabs.addTab(
38968 this.config.closeOnTab && panel.isClosable(),
38971 if(panel.tabTip !== undefined){
38972 ti.setTooltip(panel.tabTip);
38974 ti.on("activate", function(){
38975 this.setActivePanel(panel);
38978 if(this.config.closeOnTab){
38979 ti.on("beforeclose", function(t, e){
38981 this.remove(panel);
38985 panel.tabItem = ti;
38990 updatePanelTitle : function(panel, title)
38992 if(this.activePanel == panel){
38993 this.updateTitle(title);
38996 var ti = this.tabs.getTab(panel.getEl().id);
38998 if(panel.tabTip !== undefined){
38999 ti.setTooltip(panel.tabTip);
39004 updateTitle : function(title){
39005 if(this.titleTextEl && !this.config.title){
39006 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39010 setActivePanel : function(panel)
39012 panel = this.getPanel(panel);
39013 if(this.activePanel && this.activePanel != panel){
39014 if(this.activePanel.setActiveState(false) === false){
39018 this.activePanel = panel;
39019 panel.setActiveState(true);
39020 if(this.panelSize){
39021 panel.setSize(this.panelSize.width, this.panelSize.height);
39024 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39026 this.updateTitle(panel.getTitle());
39028 this.fireEvent("invalidated", this);
39030 this.fireEvent("panelactivated", this, panel);
39034 * Shows the specified panel.
39035 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39036 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39038 showPanel : function(panel)
39040 panel = this.getPanel(panel);
39043 var tab = this.tabs.getTab(panel.getEl().id);
39044 if(tab.isHidden()){
39045 this.tabs.unhideTab(tab.id);
39049 this.setActivePanel(panel);
39056 * Get the active panel for this region.
39057 * @return {Roo.ContentPanel} The active panel or null
39059 getActivePanel : function(){
39060 return this.activePanel;
39063 validateVisibility : function(){
39064 if(this.panels.getCount() < 1){
39065 this.updateTitle(" ");
39066 this.closeBtn.hide();
39069 if(!this.isVisible()){
39076 * Adds the passed ContentPanel(s) to this region.
39077 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39078 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39080 add : function(panel)
39082 if(arguments.length > 1){
39083 for(var i = 0, len = arguments.length; i < len; i++) {
39084 this.add(arguments[i]);
39089 // if we have not been rendered yet, then we can not really do much of this..
39090 if (!this.bodyEl) {
39091 this.unrendered_panels.push(panel);
39098 if(this.hasPanel(panel)){
39099 this.showPanel(panel);
39102 panel.setRegion(this);
39103 this.panels.add(panel);
39104 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39105 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39106 // and hide them... ???
39107 this.bodyEl.dom.appendChild(panel.getEl().dom);
39108 if(panel.background !== true){
39109 this.setActivePanel(panel);
39111 this.fireEvent("paneladded", this, panel);
39118 this.initPanelAsTab(panel);
39122 if(panel.background !== true){
39123 this.tabs.activate(panel.getEl().id);
39125 this.fireEvent("paneladded", this, panel);
39130 * Hides the tab for the specified panel.
39131 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39133 hidePanel : function(panel){
39134 if(this.tabs && (panel = this.getPanel(panel))){
39135 this.tabs.hideTab(panel.getEl().id);
39140 * Unhides the tab for a previously hidden panel.
39141 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39143 unhidePanel : function(panel){
39144 if(this.tabs && (panel = this.getPanel(panel))){
39145 this.tabs.unhideTab(panel.getEl().id);
39149 clearPanels : function(){
39150 while(this.panels.getCount() > 0){
39151 this.remove(this.panels.first());
39156 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39157 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39158 * @param {Boolean} preservePanel Overrides the config preservePanel option
39159 * @return {Roo.ContentPanel} The panel that was removed
39161 remove : function(panel, preservePanel)
39163 panel = this.getPanel(panel);
39168 this.fireEvent("beforeremove", this, panel, e);
39169 if(e.cancel === true){
39172 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39173 var panelId = panel.getId();
39174 this.panels.removeKey(panelId);
39176 document.body.appendChild(panel.getEl().dom);
39179 this.tabs.removeTab(panel.getEl().id);
39180 }else if (!preservePanel){
39181 this.bodyEl.dom.removeChild(panel.getEl().dom);
39183 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39184 var p = this.panels.first();
39185 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39186 tempEl.appendChild(p.getEl().dom);
39187 this.bodyEl.update("");
39188 this.bodyEl.dom.appendChild(p.getEl().dom);
39190 this.updateTitle(p.getTitle());
39192 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39193 this.setActivePanel(p);
39195 panel.setRegion(null);
39196 if(this.activePanel == panel){
39197 this.activePanel = null;
39199 if(this.config.autoDestroy !== false && preservePanel !== true){
39200 try{panel.destroy();}catch(e){}
39202 this.fireEvent("panelremoved", this, panel);
39207 * Returns the TabPanel component used by this region
39208 * @return {Roo.TabPanel}
39210 getTabs : function(){
39214 createTool : function(parentEl, className){
39215 var btn = Roo.DomHelper.append(parentEl, {
39217 cls: "x-layout-tools-button",
39220 cls: "roo-layout-tools-button-inner " + className,
39224 btn.addClassOnOver("roo-layout-tools-button-over");
39229 * Ext JS Library 1.1.1
39230 * Copyright(c) 2006-2007, Ext JS, LLC.
39232 * Originally Released Under LGPL - original licence link has changed is not relivant.
39235 * <script type="text/javascript">
39241 * @class Roo.SplitLayoutRegion
39242 * @extends Roo.LayoutRegion
39243 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39245 Roo.bootstrap.layout.Split = function(config){
39246 this.cursor = config.cursor;
39247 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39250 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39252 splitTip : "Drag to resize.",
39253 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39254 useSplitTips : false,
39256 applyConfig : function(config){
39257 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39260 onRender : function(ctr,pos) {
39262 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39263 if(!this.config.split){
39268 var splitEl = Roo.DomHelper.append(ctr.dom, {
39270 id: this.el.id + "-split",
39271 cls: "roo-layout-split roo-layout-split-"+this.position,
39274 /** The SplitBar for this region
39275 * @type Roo.SplitBar */
39276 // does not exist yet...
39277 Roo.log([this.position, this.orientation]);
39279 this.split = new Roo.bootstrap.SplitBar({
39280 dragElement : splitEl,
39281 resizingElement: this.el,
39282 orientation : this.orientation
39285 this.split.on("moved", this.onSplitMove, this);
39286 this.split.useShim = this.config.useShim === true;
39287 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39288 if(this.useSplitTips){
39289 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39291 //if(config.collapsible){
39292 // this.split.el.on("dblclick", this.collapse, this);
39295 if(typeof this.config.minSize != "undefined"){
39296 this.split.minSize = this.config.minSize;
39298 if(typeof this.config.maxSize != "undefined"){
39299 this.split.maxSize = this.config.maxSize;
39301 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39302 this.hideSplitter();
39307 getHMaxSize : function(){
39308 var cmax = this.config.maxSize || 10000;
39309 var center = this.mgr.getRegion("center");
39310 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39313 getVMaxSize : function(){
39314 var cmax = this.config.maxSize || 10000;
39315 var center = this.mgr.getRegion("center");
39316 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39319 onSplitMove : function(split, newSize){
39320 this.fireEvent("resized", this, newSize);
39324 * Returns the {@link Roo.SplitBar} for this region.
39325 * @return {Roo.SplitBar}
39327 getSplitBar : function(){
39332 this.hideSplitter();
39333 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39336 hideSplitter : function(){
39338 this.split.el.setLocation(-2000,-2000);
39339 this.split.el.hide();
39345 this.split.el.show();
39347 Roo.bootstrap.layout.Split.superclass.show.call(this);
39350 beforeSlide: function(){
39351 if(Roo.isGecko){// firefox overflow auto bug workaround
39352 this.bodyEl.clip();
39354 this.tabs.bodyEl.clip();
39356 if(this.activePanel){
39357 this.activePanel.getEl().clip();
39359 if(this.activePanel.beforeSlide){
39360 this.activePanel.beforeSlide();
39366 afterSlide : function(){
39367 if(Roo.isGecko){// firefox overflow auto bug workaround
39368 this.bodyEl.unclip();
39370 this.tabs.bodyEl.unclip();
39372 if(this.activePanel){
39373 this.activePanel.getEl().unclip();
39374 if(this.activePanel.afterSlide){
39375 this.activePanel.afterSlide();
39381 initAutoHide : function(){
39382 if(this.autoHide !== false){
39383 if(!this.autoHideHd){
39384 var st = new Roo.util.DelayedTask(this.slideIn, this);
39385 this.autoHideHd = {
39386 "mouseout": function(e){
39387 if(!e.within(this.el, true)){
39391 "mouseover" : function(e){
39397 this.el.on(this.autoHideHd);
39401 clearAutoHide : function(){
39402 if(this.autoHide !== false){
39403 this.el.un("mouseout", this.autoHideHd.mouseout);
39404 this.el.un("mouseover", this.autoHideHd.mouseover);
39408 clearMonitor : function(){
39409 Roo.get(document).un("click", this.slideInIf, this);
39412 // these names are backwards but not changed for compat
39413 slideOut : function(){
39414 if(this.isSlid || this.el.hasActiveFx()){
39417 this.isSlid = true;
39418 if(this.collapseBtn){
39419 this.collapseBtn.hide();
39421 this.closeBtnState = this.closeBtn.getStyle('display');
39422 this.closeBtn.hide();
39424 this.stickBtn.show();
39427 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39428 this.beforeSlide();
39429 this.el.setStyle("z-index", 10001);
39430 this.el.slideIn(this.getSlideAnchor(), {
39431 callback: function(){
39433 this.initAutoHide();
39434 Roo.get(document).on("click", this.slideInIf, this);
39435 this.fireEvent("slideshow", this);
39442 afterSlideIn : function(){
39443 this.clearAutoHide();
39444 this.isSlid = false;
39445 this.clearMonitor();
39446 this.el.setStyle("z-index", "");
39447 if(this.collapseBtn){
39448 this.collapseBtn.show();
39450 this.closeBtn.setStyle('display', this.closeBtnState);
39452 this.stickBtn.hide();
39454 this.fireEvent("slidehide", this);
39457 slideIn : function(cb){
39458 if(!this.isSlid || this.el.hasActiveFx()){
39462 this.isSlid = false;
39463 this.beforeSlide();
39464 this.el.slideOut(this.getSlideAnchor(), {
39465 callback: function(){
39466 this.el.setLeftTop(-10000, -10000);
39468 this.afterSlideIn();
39476 slideInIf : function(e){
39477 if(!e.within(this.el)){
39482 animateCollapse : function(){
39483 this.beforeSlide();
39484 this.el.setStyle("z-index", 20000);
39485 var anchor = this.getSlideAnchor();
39486 this.el.slideOut(anchor, {
39487 callback : function(){
39488 this.el.setStyle("z-index", "");
39489 this.collapsedEl.slideIn(anchor, {duration:.3});
39491 this.el.setLocation(-10000,-10000);
39493 this.fireEvent("collapsed", this);
39500 animateExpand : function(){
39501 this.beforeSlide();
39502 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39503 this.el.setStyle("z-index", 20000);
39504 this.collapsedEl.hide({
39507 this.el.slideIn(this.getSlideAnchor(), {
39508 callback : function(){
39509 this.el.setStyle("z-index", "");
39512 this.split.el.show();
39514 this.fireEvent("invalidated", this);
39515 this.fireEvent("expanded", this);
39543 getAnchor : function(){
39544 return this.anchors[this.position];
39547 getCollapseAnchor : function(){
39548 return this.canchors[this.position];
39551 getSlideAnchor : function(){
39552 return this.sanchors[this.position];
39555 getAlignAdj : function(){
39556 var cm = this.cmargins;
39557 switch(this.position){
39573 getExpandAdj : function(){
39574 var c = this.collapsedEl, cm = this.cmargins;
39575 switch(this.position){
39577 return [-(cm.right+c.getWidth()+cm.left), 0];
39580 return [cm.right+c.getWidth()+cm.left, 0];
39583 return [0, -(cm.top+cm.bottom+c.getHeight())];
39586 return [0, cm.top+cm.bottom+c.getHeight()];
39592 * Ext JS Library 1.1.1
39593 * Copyright(c) 2006-2007, Ext JS, LLC.
39595 * Originally Released Under LGPL - original licence link has changed is not relivant.
39598 * <script type="text/javascript">
39601 * These classes are private internal classes
39603 Roo.bootstrap.layout.Center = function(config){
39604 config.region = "center";
39605 Roo.bootstrap.layout.Region.call(this, config);
39606 this.visible = true;
39607 this.minWidth = config.minWidth || 20;
39608 this.minHeight = config.minHeight || 20;
39611 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39613 // center panel can't be hidden
39617 // center panel can't be hidden
39620 getMinWidth: function(){
39621 return this.minWidth;
39624 getMinHeight: function(){
39625 return this.minHeight;
39639 Roo.bootstrap.layout.North = function(config)
39641 config.region = 'north';
39642 config.cursor = 'n-resize';
39644 Roo.bootstrap.layout.Split.call(this, config);
39648 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39649 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39650 this.split.el.addClass("roo-layout-split-v");
39652 //var size = config.initialSize || config.height;
39653 //if(this.el && typeof size != "undefined"){
39654 // this.el.setHeight(size);
39657 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39659 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39662 onRender : function(ctr, pos)
39664 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39665 var size = this.config.initialSize || this.config.height;
39666 if(this.el && typeof size != "undefined"){
39667 this.el.setHeight(size);
39672 getBox : function(){
39673 if(this.collapsed){
39674 return this.collapsedEl.getBox();
39676 var box = this.el.getBox();
39678 box.height += this.split.el.getHeight();
39683 updateBox : function(box){
39684 if(this.split && !this.collapsed){
39685 box.height -= this.split.el.getHeight();
39686 this.split.el.setLeft(box.x);
39687 this.split.el.setTop(box.y+box.height);
39688 this.split.el.setWidth(box.width);
39690 if(this.collapsed){
39691 this.updateBody(box.width, null);
39693 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39701 Roo.bootstrap.layout.South = function(config){
39702 config.region = 'south';
39703 config.cursor = 's-resize';
39704 Roo.bootstrap.layout.Split.call(this, config);
39706 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39707 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39708 this.split.el.addClass("roo-layout-split-v");
39713 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39714 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39716 onRender : function(ctr, pos)
39718 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39719 var size = this.config.initialSize || this.config.height;
39720 if(this.el && typeof size != "undefined"){
39721 this.el.setHeight(size);
39726 getBox : function(){
39727 if(this.collapsed){
39728 return this.collapsedEl.getBox();
39730 var box = this.el.getBox();
39732 var sh = this.split.el.getHeight();
39739 updateBox : function(box){
39740 if(this.split && !this.collapsed){
39741 var sh = this.split.el.getHeight();
39744 this.split.el.setLeft(box.x);
39745 this.split.el.setTop(box.y-sh);
39746 this.split.el.setWidth(box.width);
39748 if(this.collapsed){
39749 this.updateBody(box.width, null);
39751 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39755 Roo.bootstrap.layout.East = function(config){
39756 config.region = "east";
39757 config.cursor = "e-resize";
39758 Roo.bootstrap.layout.Split.call(this, config);
39760 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39761 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39762 this.split.el.addClass("roo-layout-split-h");
39766 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39767 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39769 onRender : function(ctr, pos)
39771 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39772 var size = this.config.initialSize || this.config.width;
39773 if(this.el && typeof size != "undefined"){
39774 this.el.setWidth(size);
39779 getBox : function(){
39780 if(this.collapsed){
39781 return this.collapsedEl.getBox();
39783 var box = this.el.getBox();
39785 var sw = this.split.el.getWidth();
39792 updateBox : function(box){
39793 if(this.split && !this.collapsed){
39794 var sw = this.split.el.getWidth();
39796 this.split.el.setLeft(box.x);
39797 this.split.el.setTop(box.y);
39798 this.split.el.setHeight(box.height);
39801 if(this.collapsed){
39802 this.updateBody(null, box.height);
39804 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39808 Roo.bootstrap.layout.West = function(config){
39809 config.region = "west";
39810 config.cursor = "w-resize";
39812 Roo.bootstrap.layout.Split.call(this, config);
39814 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39815 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39816 this.split.el.addClass("roo-layout-split-h");
39820 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39821 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39823 onRender: function(ctr, pos)
39825 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39826 var size = this.config.initialSize || this.config.width;
39827 if(typeof size != "undefined"){
39828 this.el.setWidth(size);
39832 getBox : function(){
39833 if(this.collapsed){
39834 return this.collapsedEl.getBox();
39836 var box = this.el.getBox();
39837 if (box.width == 0) {
39838 box.width = this.config.width; // kludge?
39841 box.width += this.split.el.getWidth();
39846 updateBox : function(box){
39847 if(this.split && !this.collapsed){
39848 var sw = this.split.el.getWidth();
39850 this.split.el.setLeft(box.x+box.width);
39851 this.split.el.setTop(box.y);
39852 this.split.el.setHeight(box.height);
39854 if(this.collapsed){
39855 this.updateBody(null, box.height);
39857 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39859 });Roo.namespace("Roo.bootstrap.panel");/*
39861 * Ext JS Library 1.1.1
39862 * Copyright(c) 2006-2007, Ext JS, LLC.
39864 * Originally Released Under LGPL - original licence link has changed is not relivant.
39867 * <script type="text/javascript">
39870 * @class Roo.ContentPanel
39871 * @extends Roo.util.Observable
39872 * A basic ContentPanel element.
39873 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39874 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39875 * @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
39876 * @cfg {Boolean} closable True if the panel can be closed/removed
39877 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39878 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39879 * @cfg {Toolbar} toolbar A toolbar for this panel
39880 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39881 * @cfg {String} title The title for this panel
39882 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39883 * @cfg {String} url Calls {@link #setUrl} with this value
39884 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39885 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39886 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39887 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39888 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39889 * @cfg {Boolean} badges render the badges
39890 * @cfg {String} cls extra classes to use
39891 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39894 * Create a new ContentPanel.
39895 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39896 * @param {String/Object} config A string to set only the title or a config object
39897 * @param {String} content (optional) Set the HTML content for this panel
39898 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39900 Roo.bootstrap.panel.Content = function( config){
39902 this.tpl = config.tpl || false;
39904 var el = config.el;
39905 var content = config.content;
39907 if(config.autoCreate){ // xtype is available if this is called from factory
39910 this.el = Roo.get(el);
39911 if(!this.el && config && config.autoCreate){
39912 if(typeof config.autoCreate == "object"){
39913 if(!config.autoCreate.id){
39914 config.autoCreate.id = config.id||el;
39916 this.el = Roo.DomHelper.append(document.body,
39917 config.autoCreate, true);
39921 cls: (config.cls || '') +
39922 (config.background ? ' bg-' + config.background : '') +
39923 " roo-layout-inactive-content",
39926 if (config.iframe) {
39930 style : 'border: 0px',
39931 src : 'about:blank'
39937 elcfg.html = config.html;
39941 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39942 if (config.iframe) {
39943 this.iframeEl = this.el.select('iframe',true).first();
39948 this.closable = false;
39949 this.loaded = false;
39950 this.active = false;
39953 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39955 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39957 this.wrapEl = this.el; //this.el.wrap();
39959 if (config.toolbar.items) {
39960 ti = config.toolbar.items ;
39961 delete config.toolbar.items ;
39965 this.toolbar.render(this.wrapEl, 'before');
39966 for(var i =0;i < ti.length;i++) {
39967 // Roo.log(['add child', items[i]]);
39968 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39970 this.toolbar.items = nitems;
39971 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39972 delete config.toolbar;
39976 // xtype created footer. - not sure if will work as we normally have to render first..
39977 if (this.footer && !this.footer.el && this.footer.xtype) {
39978 if (!this.wrapEl) {
39979 this.wrapEl = this.el.wrap();
39982 this.footer.container = this.wrapEl.createChild();
39984 this.footer = Roo.factory(this.footer, Roo);
39989 if(typeof config == "string"){
39990 this.title = config;
39992 Roo.apply(this, config);
39996 this.resizeEl = Roo.get(this.resizeEl, true);
39998 this.resizeEl = this.el;
40000 // handle view.xtype
40008 * Fires when this panel is activated.
40009 * @param {Roo.ContentPanel} this
40013 * @event deactivate
40014 * Fires when this panel is activated.
40015 * @param {Roo.ContentPanel} this
40017 "deactivate" : true,
40021 * Fires when this panel is resized if fitToFrame is true.
40022 * @param {Roo.ContentPanel} this
40023 * @param {Number} width The width after any component adjustments
40024 * @param {Number} height The height after any component adjustments
40030 * Fires when this tab is created
40031 * @param {Roo.ContentPanel} this
40042 if(this.autoScroll && !this.iframe){
40043 this.resizeEl.setStyle("overflow", "auto");
40045 // fix randome scrolling
40046 //this.el.on('scroll', function() {
40047 // Roo.log('fix random scolling');
40048 // this.scrollTo('top',0);
40051 content = content || this.content;
40053 this.setContent(content);
40055 if(config && config.url){
40056 this.setUrl(this.url, this.params, this.loadOnce);
40061 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40063 if (this.view && typeof(this.view.xtype) != 'undefined') {
40064 this.view.el = this.el.appendChild(document.createElement("div"));
40065 this.view = Roo.factory(this.view);
40066 this.view.render && this.view.render(false, '');
40070 this.fireEvent('render', this);
40073 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40083 setRegion : function(region){
40084 this.region = region;
40085 this.setActiveClass(region && !this.background);
40089 setActiveClass: function(state)
40092 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40093 this.el.setStyle('position','relative');
40095 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40096 this.el.setStyle('position', 'absolute');
40101 * Returns the toolbar for this Panel if one was configured.
40102 * @return {Roo.Toolbar}
40104 getToolbar : function(){
40105 return this.toolbar;
40108 setActiveState : function(active)
40110 this.active = active;
40111 this.setActiveClass(active);
40113 if(this.fireEvent("deactivate", this) === false){
40118 this.fireEvent("activate", this);
40122 * Updates this panel's element (not for iframe)
40123 * @param {String} content The new content
40124 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40126 setContent : function(content, loadScripts){
40131 this.el.update(content, loadScripts);
40134 ignoreResize : function(w, h){
40135 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40138 this.lastSize = {width: w, height: h};
40143 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40144 * @return {Roo.UpdateManager} The UpdateManager
40146 getUpdateManager : function(){
40150 return this.el.getUpdateManager();
40153 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40154 * Does not work with IFRAME contents
40155 * @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:
40158 url: "your-url.php",
40159 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40160 callback: yourFunction,
40161 scope: yourObject, //(optional scope)
40164 text: "Loading...",
40170 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40171 * 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.
40172 * @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}
40173 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40174 * @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.
40175 * @return {Roo.ContentPanel} this
40183 var um = this.el.getUpdateManager();
40184 um.update.apply(um, arguments);
40190 * 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.
40191 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40192 * @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)
40193 * @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)
40194 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40196 setUrl : function(url, params, loadOnce){
40198 this.iframeEl.dom.src = url;
40202 if(this.refreshDelegate){
40203 this.removeListener("activate", this.refreshDelegate);
40205 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40206 this.on("activate", this.refreshDelegate);
40207 return this.el.getUpdateManager();
40210 _handleRefresh : function(url, params, loadOnce){
40211 if(!loadOnce || !this.loaded){
40212 var updater = this.el.getUpdateManager();
40213 updater.update(url, params, this._setLoaded.createDelegate(this));
40217 _setLoaded : function(){
40218 this.loaded = true;
40222 * Returns this panel's id
40225 getId : function(){
40230 * Returns this panel's element - used by regiosn to add.
40231 * @return {Roo.Element}
40233 getEl : function(){
40234 return this.wrapEl || this.el;
40239 adjustForComponents : function(width, height)
40241 //Roo.log('adjustForComponents ');
40242 if(this.resizeEl != this.el){
40243 width -= this.el.getFrameWidth('lr');
40244 height -= this.el.getFrameWidth('tb');
40247 var te = this.toolbar.getEl();
40248 te.setWidth(width);
40249 height -= te.getHeight();
40252 var te = this.footer.getEl();
40253 te.setWidth(width);
40254 height -= te.getHeight();
40258 if(this.adjustments){
40259 width += this.adjustments[0];
40260 height += this.adjustments[1];
40262 return {"width": width, "height": height};
40265 setSize : function(width, height){
40266 if(this.fitToFrame && !this.ignoreResize(width, height)){
40267 if(this.fitContainer && this.resizeEl != this.el){
40268 this.el.setSize(width, height);
40270 var size = this.adjustForComponents(width, height);
40272 this.iframeEl.setSize(width,height);
40275 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40276 this.fireEvent('resize', this, size.width, size.height);
40283 * Returns this panel's title
40286 getTitle : function(){
40288 if (typeof(this.title) != 'object') {
40293 for (var k in this.title) {
40294 if (!this.title.hasOwnProperty(k)) {
40298 if (k.indexOf('-') >= 0) {
40299 var s = k.split('-');
40300 for (var i = 0; i<s.length; i++) {
40301 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40304 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40311 * Set this panel's title
40312 * @param {String} title
40314 setTitle : function(title){
40315 this.title = title;
40317 this.region.updatePanelTitle(this, title);
40322 * Returns true is this panel was configured to be closable
40323 * @return {Boolean}
40325 isClosable : function(){
40326 return this.closable;
40329 beforeSlide : function(){
40331 this.resizeEl.clip();
40334 afterSlide : function(){
40336 this.resizeEl.unclip();
40340 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40341 * Will fail silently if the {@link #setUrl} method has not been called.
40342 * This does not activate the panel, just updates its content.
40344 refresh : function(){
40345 if(this.refreshDelegate){
40346 this.loaded = false;
40347 this.refreshDelegate();
40352 * Destroys this panel
40354 destroy : function(){
40355 this.el.removeAllListeners();
40356 var tempEl = document.createElement("span");
40357 tempEl.appendChild(this.el.dom);
40358 tempEl.innerHTML = "";
40364 * form - if the content panel contains a form - this is a reference to it.
40365 * @type {Roo.form.Form}
40369 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40370 * This contains a reference to it.
40376 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40386 * @param {Object} cfg Xtype definition of item to add.
40390 getChildContainer: function () {
40391 return this.getEl();
40396 var ret = new Roo.factory(cfg);
40401 if (cfg.xtype.match(/^Form$/)) {
40404 //if (this.footer) {
40405 // el = this.footer.container.insertSibling(false, 'before');
40407 el = this.el.createChild();
40410 this.form = new Roo.form.Form(cfg);
40413 if ( this.form.allItems.length) {
40414 this.form.render(el.dom);
40418 // should only have one of theses..
40419 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40420 // views.. should not be just added - used named prop 'view''
40422 cfg.el = this.el.appendChild(document.createElement("div"));
40425 var ret = new Roo.factory(cfg);
40427 ret.render && ret.render(false, ''); // render blank..
40437 * @class Roo.bootstrap.panel.Grid
40438 * @extends Roo.bootstrap.panel.Content
40440 * Create a new GridPanel.
40441 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40442 * @param {Object} config A the config object
40448 Roo.bootstrap.panel.Grid = function(config)
40452 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40453 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40455 config.el = this.wrapper;
40456 //this.el = this.wrapper;
40458 if (config.container) {
40459 // ctor'ed from a Border/panel.grid
40462 this.wrapper.setStyle("overflow", "hidden");
40463 this.wrapper.addClass('roo-grid-container');
40468 if(config.toolbar){
40469 var tool_el = this.wrapper.createChild();
40470 this.toolbar = Roo.factory(config.toolbar);
40472 if (config.toolbar.items) {
40473 ti = config.toolbar.items ;
40474 delete config.toolbar.items ;
40478 this.toolbar.render(tool_el);
40479 for(var i =0;i < ti.length;i++) {
40480 // Roo.log(['add child', items[i]]);
40481 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40483 this.toolbar.items = nitems;
40485 delete config.toolbar;
40488 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40489 config.grid.scrollBody = true;;
40490 config.grid.monitorWindowResize = false; // turn off autosizing
40491 config.grid.autoHeight = false;
40492 config.grid.autoWidth = false;
40494 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40496 if (config.background) {
40497 // render grid on panel activation (if panel background)
40498 this.on('activate', function(gp) {
40499 if (!gp.grid.rendered) {
40500 gp.grid.render(this.wrapper);
40501 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40506 this.grid.render(this.wrapper);
40507 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40510 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40511 // ??? needed ??? config.el = this.wrapper;
40516 // xtype created footer. - not sure if will work as we normally have to render first..
40517 if (this.footer && !this.footer.el && this.footer.xtype) {
40519 var ctr = this.grid.getView().getFooterPanel(true);
40520 this.footer.dataSource = this.grid.dataSource;
40521 this.footer = Roo.factory(this.footer, Roo);
40522 this.footer.render(ctr);
40532 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40533 getId : function(){
40534 return this.grid.id;
40538 * Returns the grid for this panel
40539 * @return {Roo.bootstrap.Table}
40541 getGrid : function(){
40545 setSize : function(width, height){
40546 if(!this.ignoreResize(width, height)){
40547 var grid = this.grid;
40548 var size = this.adjustForComponents(width, height);
40549 // tfoot is not a footer?
40552 var gridel = grid.getGridEl();
40553 gridel.setSize(size.width, size.height);
40555 var tbd = grid.getGridEl().select('tbody', true).first();
40556 var thd = grid.getGridEl().select('thead',true).first();
40557 var tbf= grid.getGridEl().select('tfoot', true).first();
40560 size.height -= tbf.getHeight();
40563 size.height -= thd.getHeight();
40566 tbd.setSize(size.width, size.height );
40567 // this is for the account management tab -seems to work there.
40568 var thd = grid.getGridEl().select('thead',true).first();
40570 // tbd.setSize(size.width, size.height - thd.getHeight());
40579 beforeSlide : function(){
40580 this.grid.getView().scroller.clip();
40583 afterSlide : function(){
40584 this.grid.getView().scroller.unclip();
40587 destroy : function(){
40588 this.grid.destroy();
40590 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40595 * @class Roo.bootstrap.panel.Nest
40596 * @extends Roo.bootstrap.panel.Content
40598 * Create a new Panel, that can contain a layout.Border.
40601 * @param {Roo.BorderLayout} layout The layout for this panel
40602 * @param {String/Object} config A string to set only the title or a config object
40604 Roo.bootstrap.panel.Nest = function(config)
40606 // construct with only one argument..
40607 /* FIXME - implement nicer consturctors
40608 if (layout.layout) {
40610 layout = config.layout;
40611 delete config.layout;
40613 if (layout.xtype && !layout.getEl) {
40614 // then layout needs constructing..
40615 layout = Roo.factory(layout, Roo);
40619 config.el = config.layout.getEl();
40621 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40623 config.layout.monitorWindowResize = false; // turn off autosizing
40624 this.layout = config.layout;
40625 this.layout.getEl().addClass("roo-layout-nested-layout");
40626 this.layout.parent = this;
40633 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40635 setSize : function(width, height){
40636 if(!this.ignoreResize(width, height)){
40637 var size = this.adjustForComponents(width, height);
40638 var el = this.layout.getEl();
40639 if (size.height < 1) {
40640 el.setWidth(size.width);
40642 el.setSize(size.width, size.height);
40644 var touch = el.dom.offsetWidth;
40645 this.layout.layout();
40646 // ie requires a double layout on the first pass
40647 if(Roo.isIE && !this.initialized){
40648 this.initialized = true;
40649 this.layout.layout();
40654 // activate all subpanels if not currently active..
40656 setActiveState : function(active){
40657 this.active = active;
40658 this.setActiveClass(active);
40661 this.fireEvent("deactivate", this);
40665 this.fireEvent("activate", this);
40666 // not sure if this should happen before or after..
40667 if (!this.layout) {
40668 return; // should not happen..
40671 for (var r in this.layout.regions) {
40672 reg = this.layout.getRegion(r);
40673 if (reg.getActivePanel()) {
40674 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40675 reg.setActivePanel(reg.getActivePanel());
40678 if (!reg.panels.length) {
40681 reg.showPanel(reg.getPanel(0));
40690 * Returns the nested BorderLayout for this panel
40691 * @return {Roo.BorderLayout}
40693 getLayout : function(){
40694 return this.layout;
40698 * Adds a xtype elements to the layout of the nested panel
40702 xtype : 'ContentPanel',
40709 xtype : 'NestedLayoutPanel',
40715 items : [ ... list of content panels or nested layout panels.. ]
40719 * @param {Object} cfg Xtype definition of item to add.
40721 addxtype : function(cfg) {
40722 return this.layout.addxtype(cfg);
40727 * Ext JS Library 1.1.1
40728 * Copyright(c) 2006-2007, Ext JS, LLC.
40730 * Originally Released Under LGPL - original licence link has changed is not relivant.
40733 * <script type="text/javascript">
40736 * @class Roo.TabPanel
40737 * @extends Roo.util.Observable
40738 * A lightweight tab container.
40742 // basic tabs 1, built from existing content
40743 var tabs = new Roo.TabPanel("tabs1");
40744 tabs.addTab("script", "View Script");
40745 tabs.addTab("markup", "View Markup");
40746 tabs.activate("script");
40748 // more advanced tabs, built from javascript
40749 var jtabs = new Roo.TabPanel("jtabs");
40750 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40752 // set up the UpdateManager
40753 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40754 var updater = tab2.getUpdateManager();
40755 updater.setDefaultUrl("ajax1.htm");
40756 tab2.on('activate', updater.refresh, updater, true);
40758 // Use setUrl for Ajax loading
40759 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40760 tab3.setUrl("ajax2.htm", null, true);
40763 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40766 jtabs.activate("jtabs-1");
40769 * Create a new TabPanel.
40770 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40771 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40773 Roo.bootstrap.panel.Tabs = function(config){
40775 * The container element for this TabPanel.
40776 * @type Roo.Element
40778 this.el = Roo.get(config.el);
40781 if(typeof config == "boolean"){
40782 this.tabPosition = config ? "bottom" : "top";
40784 Roo.apply(this, config);
40788 if(this.tabPosition == "bottom"){
40789 // if tabs are at the bottom = create the body first.
40790 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40791 this.el.addClass("roo-tabs-bottom");
40793 // next create the tabs holders
40795 if (this.tabPosition == "west"){
40797 var reg = this.region; // fake it..
40799 if (!reg.mgr.parent) {
40802 reg = reg.mgr.parent.region;
40804 Roo.log("got nest?");
40806 if (reg.mgr.getRegion('west')) {
40807 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40808 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40809 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40810 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40811 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40819 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40820 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40821 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40822 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40827 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40830 // finally - if tabs are at the top, then create the body last..
40831 if(this.tabPosition != "bottom"){
40832 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40833 * @type Roo.Element
40835 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40836 this.el.addClass("roo-tabs-top");
40840 this.bodyEl.setStyle("position", "relative");
40842 this.active = null;
40843 this.activateDelegate = this.activate.createDelegate(this);
40848 * Fires when the active tab changes
40849 * @param {Roo.TabPanel} this
40850 * @param {Roo.TabPanelItem} activePanel The new active tab
40854 * @event beforetabchange
40855 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40856 * @param {Roo.TabPanel} this
40857 * @param {Object} e Set cancel to true on this object to cancel the tab change
40858 * @param {Roo.TabPanelItem} tab The tab being changed to
40860 "beforetabchange" : true
40863 Roo.EventManager.onWindowResize(this.onResize, this);
40864 this.cpad = this.el.getPadding("lr");
40865 this.hiddenCount = 0;
40868 // toolbar on the tabbar support...
40869 if (this.toolbar) {
40870 alert("no toolbar support yet");
40871 this.toolbar = false;
40873 var tcfg = this.toolbar;
40874 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40875 this.toolbar = new Roo.Toolbar(tcfg);
40876 if (Roo.isSafari) {
40877 var tbl = tcfg.container.child('table', true);
40878 tbl.setAttribute('width', '100%');
40886 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40889 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40891 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40893 tabPosition : "top",
40895 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40897 currentTabWidth : 0,
40899 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40903 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40907 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40909 preferredTabWidth : 175,
40911 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40913 resizeTabs : false,
40915 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40917 monitorResize : true,
40919 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40921 toolbar : false, // set by caller..
40923 region : false, /// set by caller
40925 disableTooltips : true, // not used yet...
40928 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40929 * @param {String} id The id of the div to use <b>or create</b>
40930 * @param {String} text The text for the tab
40931 * @param {String} content (optional) Content to put in the TabPanelItem body
40932 * @param {Boolean} closable (optional) True to create a close icon on the tab
40933 * @return {Roo.TabPanelItem} The created TabPanelItem
40935 addTab : function(id, text, content, closable, tpl)
40937 var item = new Roo.bootstrap.panel.TabItem({
40941 closable : closable,
40944 this.addTabItem(item);
40946 item.setContent(content);
40952 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40953 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40954 * @return {Roo.TabPanelItem}
40956 getTab : function(id){
40957 return this.items[id];
40961 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40962 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40964 hideTab : function(id){
40965 var t = this.items[id];
40968 this.hiddenCount++;
40969 this.autoSizeTabs();
40974 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40975 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40977 unhideTab : function(id){
40978 var t = this.items[id];
40980 t.setHidden(false);
40981 this.hiddenCount--;
40982 this.autoSizeTabs();
40987 * Adds an existing {@link Roo.TabPanelItem}.
40988 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40990 addTabItem : function(item)
40992 this.items[item.id] = item;
40993 this.items.push(item);
40994 this.autoSizeTabs();
40995 // if(this.resizeTabs){
40996 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40997 // this.autoSizeTabs();
40999 // item.autoSize();
41004 * Removes a {@link Roo.TabPanelItem}.
41005 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41007 removeTab : function(id){
41008 var items = this.items;
41009 var tab = items[id];
41010 if(!tab) { return; }
41011 var index = items.indexOf(tab);
41012 if(this.active == tab && items.length > 1){
41013 var newTab = this.getNextAvailable(index);
41018 this.stripEl.dom.removeChild(tab.pnode.dom);
41019 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41020 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41022 items.splice(index, 1);
41023 delete this.items[tab.id];
41024 tab.fireEvent("close", tab);
41025 tab.purgeListeners();
41026 this.autoSizeTabs();
41029 getNextAvailable : function(start){
41030 var items = this.items;
41032 // look for a next tab that will slide over to
41033 // replace the one being removed
41034 while(index < items.length){
41035 var item = items[++index];
41036 if(item && !item.isHidden()){
41040 // if one isn't found select the previous tab (on the left)
41043 var item = items[--index];
41044 if(item && !item.isHidden()){
41052 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41053 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41055 disableTab : function(id){
41056 var tab = this.items[id];
41057 if(tab && this.active != tab){
41063 * Enables a {@link Roo.TabPanelItem} that is disabled.
41064 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41066 enableTab : function(id){
41067 var tab = this.items[id];
41072 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41073 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41074 * @return {Roo.TabPanelItem} The TabPanelItem.
41076 activate : function(id)
41078 //Roo.log('activite:' + id);
41080 var tab = this.items[id];
41084 if(tab == this.active || tab.disabled){
41088 this.fireEvent("beforetabchange", this, e, tab);
41089 if(e.cancel !== true && !tab.disabled){
41091 this.active.hide();
41093 this.active = this.items[id];
41094 this.active.show();
41095 this.fireEvent("tabchange", this, this.active);
41101 * Gets the active {@link Roo.TabPanelItem}.
41102 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41104 getActiveTab : function(){
41105 return this.active;
41109 * Updates the tab body element to fit the height of the container element
41110 * for overflow scrolling
41111 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41113 syncHeight : function(targetHeight){
41114 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41115 var bm = this.bodyEl.getMargins();
41116 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41117 this.bodyEl.setHeight(newHeight);
41121 onResize : function(){
41122 if(this.monitorResize){
41123 this.autoSizeTabs();
41128 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41130 beginUpdate : function(){
41131 this.updating = true;
41135 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41137 endUpdate : function(){
41138 this.updating = false;
41139 this.autoSizeTabs();
41143 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41145 autoSizeTabs : function()
41147 var count = this.items.length;
41148 var vcount = count - this.hiddenCount;
41151 this.stripEl.hide();
41153 this.stripEl.show();
41156 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41161 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41162 var availWidth = Math.floor(w / vcount);
41163 var b = this.stripBody;
41164 if(b.getWidth() > w){
41165 var tabs = this.items;
41166 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41167 if(availWidth < this.minTabWidth){
41168 /*if(!this.sleft){ // incomplete scrolling code
41169 this.createScrollButtons();
41172 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41175 if(this.currentTabWidth < this.preferredTabWidth){
41176 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41182 * Returns the number of tabs in this TabPanel.
41185 getCount : function(){
41186 return this.items.length;
41190 * Resizes all the tabs to the passed width
41191 * @param {Number} The new width
41193 setTabWidth : function(width){
41194 this.currentTabWidth = width;
41195 for(var i = 0, len = this.items.length; i < len; i++) {
41196 if(!this.items[i].isHidden()) {
41197 this.items[i].setWidth(width);
41203 * Destroys this TabPanel
41204 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41206 destroy : function(removeEl){
41207 Roo.EventManager.removeResizeListener(this.onResize, this);
41208 for(var i = 0, len = this.items.length; i < len; i++){
41209 this.items[i].purgeListeners();
41211 if(removeEl === true){
41212 this.el.update("");
41217 createStrip : function(container)
41219 var strip = document.createElement("nav");
41220 strip.className = Roo.bootstrap.version == 4 ?
41221 "navbar-light bg-light" :
41222 "navbar navbar-default"; //"x-tabs-wrap";
41223 container.appendChild(strip);
41227 createStripList : function(strip)
41229 // div wrapper for retard IE
41230 // returns the "tr" element.
41231 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41232 //'<div class="x-tabs-strip-wrap">'+
41233 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41234 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41235 return strip.firstChild; //.firstChild.firstChild.firstChild;
41237 createBody : function(container)
41239 var body = document.createElement("div");
41240 Roo.id(body, "tab-body");
41241 //Roo.fly(body).addClass("x-tabs-body");
41242 Roo.fly(body).addClass("tab-content");
41243 container.appendChild(body);
41246 createItemBody :function(bodyEl, id){
41247 var body = Roo.getDom(id);
41249 body = document.createElement("div");
41252 //Roo.fly(body).addClass("x-tabs-item-body");
41253 Roo.fly(body).addClass("tab-pane");
41254 bodyEl.insertBefore(body, bodyEl.firstChild);
41258 createStripElements : function(stripEl, text, closable, tpl)
41260 var td = document.createElement("li"); // was td..
41261 td.className = 'nav-item';
41263 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41266 stripEl.appendChild(td);
41268 td.className = "x-tabs-closable";
41269 if(!this.closeTpl){
41270 this.closeTpl = new Roo.Template(
41271 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41272 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41273 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41276 var el = this.closeTpl.overwrite(td, {"text": text});
41277 var close = el.getElementsByTagName("div")[0];
41278 var inner = el.getElementsByTagName("em")[0];
41279 return {"el": el, "close": close, "inner": inner};
41282 // not sure what this is..
41283 // if(!this.tabTpl){
41284 //this.tabTpl = new Roo.Template(
41285 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41286 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41288 // this.tabTpl = new Roo.Template(
41289 // '<a href="#">' +
41290 // '<span unselectable="on"' +
41291 // (this.disableTooltips ? '' : ' title="{text}"') +
41292 // ' >{text}</span></a>'
41298 var template = tpl || this.tabTpl || false;
41301 template = new Roo.Template(
41302 Roo.bootstrap.version == 4 ?
41304 '<a class="nav-link" href="#" unselectable="on"' +
41305 (this.disableTooltips ? '' : ' title="{text}"') +
41308 '<a class="nav-link" href="#">' +
41309 '<span unselectable="on"' +
41310 (this.disableTooltips ? '' : ' title="{text}"') +
41311 ' >{text}</span></a>'
41316 switch (typeof(template)) {
41320 template = new Roo.Template(template);
41326 var el = template.overwrite(td, {"text": text});
41328 var inner = el.getElementsByTagName("span")[0];
41330 return {"el": el, "inner": inner};
41338 * @class Roo.TabPanelItem
41339 * @extends Roo.util.Observable
41340 * Represents an individual item (tab plus body) in a TabPanel.
41341 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41342 * @param {String} id The id of this TabPanelItem
41343 * @param {String} text The text for the tab of this TabPanelItem
41344 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41346 Roo.bootstrap.panel.TabItem = function(config){
41348 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41349 * @type Roo.TabPanel
41351 this.tabPanel = config.panel;
41353 * The id for this TabPanelItem
41356 this.id = config.id;
41358 this.disabled = false;
41360 this.text = config.text;
41362 this.loaded = false;
41363 this.closable = config.closable;
41366 * The body element for this TabPanelItem.
41367 * @type Roo.Element
41369 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41370 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41371 this.bodyEl.setStyle("display", "block");
41372 this.bodyEl.setStyle("zoom", "1");
41373 //this.hideAction();
41375 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41377 this.el = Roo.get(els.el);
41378 this.inner = Roo.get(els.inner, true);
41379 this.textEl = Roo.bootstrap.version == 4 ?
41380 this.el : Roo.get(this.el.dom.firstChild, true);
41382 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41383 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41386 // this.el.on("mousedown", this.onTabMouseDown, this);
41387 this.el.on("click", this.onTabClick, this);
41389 if(config.closable){
41390 var c = Roo.get(els.close, true);
41391 c.dom.title = this.closeText;
41392 c.addClassOnOver("close-over");
41393 c.on("click", this.closeClick, this);
41399 * Fires when this tab becomes the active tab.
41400 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41401 * @param {Roo.TabPanelItem} this
41405 * @event beforeclose
41406 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41407 * @param {Roo.TabPanelItem} this
41408 * @param {Object} e Set cancel to true on this object to cancel the close.
41410 "beforeclose": true,
41413 * Fires when this tab is closed.
41414 * @param {Roo.TabPanelItem} this
41418 * @event deactivate
41419 * Fires when this tab is no longer the active tab.
41420 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41421 * @param {Roo.TabPanelItem} this
41423 "deactivate" : true
41425 this.hidden = false;
41427 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41430 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41432 purgeListeners : function(){
41433 Roo.util.Observable.prototype.purgeListeners.call(this);
41434 this.el.removeAllListeners();
41437 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41440 this.status_node.addClass("active");
41443 this.tabPanel.stripWrap.repaint();
41445 this.fireEvent("activate", this.tabPanel, this);
41449 * Returns true if this tab is the active tab.
41450 * @return {Boolean}
41452 isActive : function(){
41453 return this.tabPanel.getActiveTab() == this;
41457 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41460 this.status_node.removeClass("active");
41462 this.fireEvent("deactivate", this.tabPanel, this);
41465 hideAction : function(){
41466 this.bodyEl.hide();
41467 this.bodyEl.setStyle("position", "absolute");
41468 this.bodyEl.setLeft("-20000px");
41469 this.bodyEl.setTop("-20000px");
41472 showAction : function(){
41473 this.bodyEl.setStyle("position", "relative");
41474 this.bodyEl.setTop("");
41475 this.bodyEl.setLeft("");
41476 this.bodyEl.show();
41480 * Set the tooltip for the tab.
41481 * @param {String} tooltip The tab's tooltip
41483 setTooltip : function(text){
41484 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41485 this.textEl.dom.qtip = text;
41486 this.textEl.dom.removeAttribute('title');
41488 this.textEl.dom.title = text;
41492 onTabClick : function(e){
41493 e.preventDefault();
41494 this.tabPanel.activate(this.id);
41497 onTabMouseDown : function(e){
41498 e.preventDefault();
41499 this.tabPanel.activate(this.id);
41502 getWidth : function(){
41503 return this.inner.getWidth();
41506 setWidth : function(width){
41507 var iwidth = width - this.linode.getPadding("lr");
41508 this.inner.setWidth(iwidth);
41509 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41510 this.linode.setWidth(width);
41514 * Show or hide the tab
41515 * @param {Boolean} hidden True to hide or false to show.
41517 setHidden : function(hidden){
41518 this.hidden = hidden;
41519 this.linode.setStyle("display", hidden ? "none" : "");
41523 * Returns true if this tab is "hidden"
41524 * @return {Boolean}
41526 isHidden : function(){
41527 return this.hidden;
41531 * Returns the text for this tab
41534 getText : function(){
41538 autoSize : function(){
41539 //this.el.beginMeasure();
41540 this.textEl.setWidth(1);
41542 * #2804 [new] Tabs in Roojs
41543 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41545 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41546 //this.el.endMeasure();
41550 * Sets the text for the tab (Note: this also sets the tooltip text)
41551 * @param {String} text The tab's text and tooltip
41553 setText : function(text){
41555 this.textEl.update(text);
41556 this.setTooltip(text);
41557 //if(!this.tabPanel.resizeTabs){
41558 // this.autoSize();
41562 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41564 activate : function(){
41565 this.tabPanel.activate(this.id);
41569 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41571 disable : function(){
41572 if(this.tabPanel.active != this){
41573 this.disabled = true;
41574 this.status_node.addClass("disabled");
41579 * Enables this TabPanelItem if it was previously disabled.
41581 enable : function(){
41582 this.disabled = false;
41583 this.status_node.removeClass("disabled");
41587 * Sets the content for this TabPanelItem.
41588 * @param {String} content The content
41589 * @param {Boolean} loadScripts true to look for and load scripts
41591 setContent : function(content, loadScripts){
41592 this.bodyEl.update(content, loadScripts);
41596 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41597 * @return {Roo.UpdateManager} The UpdateManager
41599 getUpdateManager : function(){
41600 return this.bodyEl.getUpdateManager();
41604 * Set a URL to be used to load the content for this TabPanelItem.
41605 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41606 * @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)
41607 * @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)
41608 * @return {Roo.UpdateManager} The UpdateManager
41610 setUrl : function(url, params, loadOnce){
41611 if(this.refreshDelegate){
41612 this.un('activate', this.refreshDelegate);
41614 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41615 this.on("activate", this.refreshDelegate);
41616 return this.bodyEl.getUpdateManager();
41620 _handleRefresh : function(url, params, loadOnce){
41621 if(!loadOnce || !this.loaded){
41622 var updater = this.bodyEl.getUpdateManager();
41623 updater.update(url, params, this._setLoaded.createDelegate(this));
41628 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41629 * Will fail silently if the setUrl method has not been called.
41630 * This does not activate the panel, just updates its content.
41632 refresh : function(){
41633 if(this.refreshDelegate){
41634 this.loaded = false;
41635 this.refreshDelegate();
41640 _setLoaded : function(){
41641 this.loaded = true;
41645 closeClick : function(e){
41648 this.fireEvent("beforeclose", this, o);
41649 if(o.cancel !== true){
41650 this.tabPanel.removeTab(this.id);
41654 * The text displayed in the tooltip for the close icon.
41657 closeText : "Close this tab"
41660 * This script refer to:
41661 * Title: International Telephone Input
41662 * Author: Jack O'Connor
41663 * Code version: v12.1.12
41664 * Availability: https://github.com/jackocnr/intl-tel-input.git
41667 Roo.bootstrap.PhoneInputData = function() {
41670 "Afghanistan (افغانستان)",
41675 "Albania (Shqipëri)",
41680 "Algeria (الجزائر)",
41705 "Antigua and Barbuda",
41715 "Armenia (Հայաստան)",
41731 "Austria (Österreich)",
41736 "Azerbaijan (Azərbaycan)",
41746 "Bahrain (البحرين)",
41751 "Bangladesh (বাংলাদেশ)",
41761 "Belarus (Беларусь)",
41766 "Belgium (België)",
41796 "Bosnia and Herzegovina (Босна и Херцеговина)",
41811 "British Indian Ocean Territory",
41816 "British Virgin Islands",
41826 "Bulgaria (България)",
41836 "Burundi (Uburundi)",
41841 "Cambodia (កម្ពុជា)",
41846 "Cameroon (Cameroun)",
41855 ["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"]
41858 "Cape Verde (Kabu Verdi)",
41863 "Caribbean Netherlands",
41874 "Central African Republic (République centrafricaine)",
41894 "Christmas Island",
41900 "Cocos (Keeling) Islands",
41911 "Comoros (جزر القمر)",
41916 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41921 "Congo (Republic) (Congo-Brazzaville)",
41941 "Croatia (Hrvatska)",
41962 "Czech Republic (Česká republika)",
41967 "Denmark (Danmark)",
41982 "Dominican Republic (República Dominicana)",
41986 ["809", "829", "849"]
42004 "Equatorial Guinea (Guinea Ecuatorial)",
42024 "Falkland Islands (Islas Malvinas)",
42029 "Faroe Islands (Føroyar)",
42050 "French Guiana (Guyane française)",
42055 "French Polynesia (Polynésie française)",
42070 "Georgia (საქართველო)",
42075 "Germany (Deutschland)",
42095 "Greenland (Kalaallit Nunaat)",
42132 "Guinea-Bissau (Guiné Bissau)",
42157 "Hungary (Magyarország)",
42162 "Iceland (Ísland)",
42182 "Iraq (العراق)",
42198 "Israel (ישראל)",
42225 "Jordan (الأردن)",
42230 "Kazakhstan (Казахстан)",
42251 "Kuwait (الكويت)",
42256 "Kyrgyzstan (Кыргызстан)",
42266 "Latvia (Latvija)",
42271 "Lebanon (لبنان)",
42286 "Libya (ليبيا)",
42296 "Lithuania (Lietuva)",
42311 "Macedonia (FYROM) (Македонија)",
42316 "Madagascar (Madagasikara)",
42346 "Marshall Islands",
42356 "Mauritania (موريتانيا)",
42361 "Mauritius (Moris)",
42382 "Moldova (Republica Moldova)",
42392 "Mongolia (Монгол)",
42397 "Montenegro (Crna Gora)",
42407 "Morocco (المغرب)",
42413 "Mozambique (Moçambique)",
42418 "Myanmar (Burma) (မြန်မာ)",
42423 "Namibia (Namibië)",
42438 "Netherlands (Nederland)",
42443 "New Caledonia (Nouvelle-Calédonie)",
42478 "North Korea (조선 민주주의 인민 공화국)",
42483 "Northern Mariana Islands",
42499 "Pakistan (پاکستان)",
42509 "Palestine (فلسطين)",
42519 "Papua New Guinea",
42561 "Réunion (La Réunion)",
42567 "Romania (România)",
42583 "Saint Barthélemy",
42594 "Saint Kitts and Nevis",
42604 "Saint Martin (Saint-Martin (partie française))",
42610 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42615 "Saint Vincent and the Grenadines",
42630 "São Tomé and Príncipe (São Tomé e Príncipe)",
42635 "Saudi Arabia (المملكة العربية السعودية)",
42640 "Senegal (Sénégal)",
42670 "Slovakia (Slovensko)",
42675 "Slovenia (Slovenija)",
42685 "Somalia (Soomaaliya)",
42695 "South Korea (대한민국)",
42700 "South Sudan (جنوب السودان)",
42710 "Sri Lanka (ශ්රී ලංකාව)",
42715 "Sudan (السودان)",
42725 "Svalbard and Jan Mayen",
42736 "Sweden (Sverige)",
42741 "Switzerland (Schweiz)",
42746 "Syria (سوريا)",
42791 "Trinidad and Tobago",
42796 "Tunisia (تونس)",
42801 "Turkey (Türkiye)",
42811 "Turks and Caicos Islands",
42821 "U.S. Virgin Islands",
42831 "Ukraine (Україна)",
42836 "United Arab Emirates (الإمارات العربية المتحدة)",
42858 "Uzbekistan (Oʻzbekiston)",
42868 "Vatican City (Città del Vaticano)",
42879 "Vietnam (Việt Nam)",
42884 "Wallis and Futuna (Wallis-et-Futuna)",
42889 "Western Sahara (الصحراء الغربية)",
42895 "Yemen (اليمن)",
42919 * This script refer to:
42920 * Title: International Telephone Input
42921 * Author: Jack O'Connor
42922 * Code version: v12.1.12
42923 * Availability: https://github.com/jackocnr/intl-tel-input.git
42927 * @class Roo.bootstrap.PhoneInput
42928 * @extends Roo.bootstrap.TriggerField
42929 * An input with International dial-code selection
42931 * @cfg {String} defaultDialCode default '+852'
42932 * @cfg {Array} preferedCountries default []
42935 * Create a new PhoneInput.
42936 * @param {Object} config Configuration options
42939 Roo.bootstrap.PhoneInput = function(config) {
42940 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42943 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42945 listWidth: undefined,
42947 selectedClass: 'active',
42949 invalidClass : "has-warning",
42951 validClass: 'has-success',
42953 allowed: '0123456789',
42958 * @cfg {String} defaultDialCode The default dial code when initializing the input
42960 defaultDialCode: '+852',
42963 * @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
42965 preferedCountries: false,
42967 getAutoCreate : function()
42969 var data = Roo.bootstrap.PhoneInputData();
42970 var align = this.labelAlign || this.parentLabelAlign();
42973 this.allCountries = [];
42974 this.dialCodeMapping = [];
42976 for (var i = 0; i < data.length; i++) {
42978 this.allCountries[i] = {
42982 priority: c[3] || 0,
42983 areaCodes: c[4] || null
42985 this.dialCodeMapping[c[2]] = {
42988 priority: c[3] || 0,
42989 areaCodes: c[4] || null
43001 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43002 maxlength: this.max_length,
43003 cls : 'form-control tel-input',
43004 autocomplete: 'new-password'
43007 var hiddenInput = {
43010 cls: 'hidden-tel-input'
43014 hiddenInput.name = this.name;
43017 if (this.disabled) {
43018 input.disabled = true;
43021 var flag_container = {
43038 cls: this.hasFeedback ? 'has-feedback' : '',
43044 cls: 'dial-code-holder',
43051 cls: 'roo-select2-container input-group',
43058 if (this.fieldLabel.length) {
43061 tooltip: 'This field is required'
43067 cls: 'control-label',
43073 html: this.fieldLabel
43076 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43082 if(this.indicatorpos == 'right') {
43083 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43090 if(align == 'left') {
43098 if(this.labelWidth > 12){
43099 label.style = "width: " + this.labelWidth + 'px';
43101 if(this.labelWidth < 13 && this.labelmd == 0){
43102 this.labelmd = this.labelWidth;
43104 if(this.labellg > 0){
43105 label.cls += ' col-lg-' + this.labellg;
43106 input.cls += ' col-lg-' + (12 - this.labellg);
43108 if(this.labelmd > 0){
43109 label.cls += ' col-md-' + this.labelmd;
43110 container.cls += ' col-md-' + (12 - this.labelmd);
43112 if(this.labelsm > 0){
43113 label.cls += ' col-sm-' + this.labelsm;
43114 container.cls += ' col-sm-' + (12 - this.labelsm);
43116 if(this.labelxs > 0){
43117 label.cls += ' col-xs-' + this.labelxs;
43118 container.cls += ' col-xs-' + (12 - this.labelxs);
43128 var settings = this;
43130 ['xs','sm','md','lg'].map(function(size){
43131 if (settings[size]) {
43132 cfg.cls += ' col-' + size + '-' + settings[size];
43136 this.store = new Roo.data.Store({
43137 proxy : new Roo.data.MemoryProxy({}),
43138 reader : new Roo.data.JsonReader({
43149 'name' : 'dialCode',
43153 'name' : 'priority',
43157 'name' : 'areaCodes',
43164 if(!this.preferedCountries) {
43165 this.preferedCountries = [
43172 var p = this.preferedCountries.reverse();
43175 for (var i = 0; i < p.length; i++) {
43176 for (var j = 0; j < this.allCountries.length; j++) {
43177 if(this.allCountries[j].iso2 == p[i]) {
43178 var t = this.allCountries[j];
43179 this.allCountries.splice(j,1);
43180 this.allCountries.unshift(t);
43186 this.store.proxy.data = {
43188 data: this.allCountries
43194 initEvents : function()
43197 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43199 this.indicator = this.indicatorEl();
43200 this.flag = this.flagEl();
43201 this.dialCodeHolder = this.dialCodeHolderEl();
43203 this.trigger = this.el.select('div.flag-box',true).first();
43204 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43209 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43210 _this.list.setWidth(lw);
43213 this.list.on('mouseover', this.onViewOver, this);
43214 this.list.on('mousemove', this.onViewMove, this);
43215 this.inputEl().on("keyup", this.onKeyUp, this);
43216 this.inputEl().on("keypress", this.onKeyPress, this);
43218 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43220 this.view = new Roo.View(this.list, this.tpl, {
43221 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43224 this.view.on('click', this.onViewClick, this);
43225 this.setValue(this.defaultDialCode);
43228 onTriggerClick : function(e)
43230 Roo.log('trigger click');
43235 if(this.isExpanded()){
43237 this.hasFocus = false;
43239 this.store.load({});
43240 this.hasFocus = true;
43245 isExpanded : function()
43247 return this.list.isVisible();
43250 collapse : function()
43252 if(!this.isExpanded()){
43256 Roo.get(document).un('mousedown', this.collapseIf, this);
43257 Roo.get(document).un('mousewheel', this.collapseIf, this);
43258 this.fireEvent('collapse', this);
43262 expand : function()
43266 if(this.isExpanded() || !this.hasFocus){
43270 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43271 this.list.setWidth(lw);
43274 this.restrictHeight();
43276 Roo.get(document).on('mousedown', this.collapseIf, this);
43277 Roo.get(document).on('mousewheel', this.collapseIf, this);
43279 this.fireEvent('expand', this);
43282 restrictHeight : function()
43284 this.list.alignTo(this.inputEl(), this.listAlign);
43285 this.list.alignTo(this.inputEl(), this.listAlign);
43288 onViewOver : function(e, t)
43290 if(this.inKeyMode){
43293 var item = this.view.findItemFromChild(t);
43296 var index = this.view.indexOf(item);
43297 this.select(index, false);
43302 onViewClick : function(view, doFocus, el, e)
43304 var index = this.view.getSelectedIndexes()[0];
43306 var r = this.store.getAt(index);
43309 this.onSelect(r, index);
43311 if(doFocus !== false && !this.blockFocus){
43312 this.inputEl().focus();
43316 onViewMove : function(e, t)
43318 this.inKeyMode = false;
43321 select : function(index, scrollIntoView)
43323 this.selectedIndex = index;
43324 this.view.select(index);
43325 if(scrollIntoView !== false){
43326 var el = this.view.getNode(index);
43328 this.list.scrollChildIntoView(el, false);
43333 createList : function()
43335 this.list = Roo.get(document.body).createChild({
43337 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43338 style: 'display:none'
43341 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43344 collapseIf : function(e)
43346 var in_combo = e.within(this.el);
43347 var in_list = e.within(this.list);
43348 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43350 if (in_combo || in_list || is_list) {
43356 onSelect : function(record, index)
43358 if(this.fireEvent('beforeselect', this, record, index) !== false){
43360 this.setFlagClass(record.data.iso2);
43361 this.setDialCode(record.data.dialCode);
43362 this.hasFocus = false;
43364 this.fireEvent('select', this, record, index);
43368 flagEl : function()
43370 var flag = this.el.select('div.flag',true).first();
43377 dialCodeHolderEl : function()
43379 var d = this.el.select('input.dial-code-holder',true).first();
43386 setDialCode : function(v)
43388 this.dialCodeHolder.dom.value = '+'+v;
43391 setFlagClass : function(n)
43393 this.flag.dom.className = 'flag '+n;
43396 getValue : function()
43398 var v = this.inputEl().getValue();
43399 if(this.dialCodeHolder) {
43400 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43405 setValue : function(v)
43407 var d = this.getDialCode(v);
43409 //invalid dial code
43410 if(v.length == 0 || !d || d.length == 0) {
43412 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43413 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43419 this.setFlagClass(this.dialCodeMapping[d].iso2);
43420 this.setDialCode(d);
43421 this.inputEl().dom.value = v.replace('+'+d,'');
43422 this.hiddenEl().dom.value = this.getValue();
43427 getDialCode : function(v)
43431 if (v.length == 0) {
43432 return this.dialCodeHolder.dom.value;
43436 if (v.charAt(0) != "+") {
43439 var numericChars = "";
43440 for (var i = 1; i < v.length; i++) {
43441 var c = v.charAt(i);
43444 if (this.dialCodeMapping[numericChars]) {
43445 dialCode = v.substr(1, i);
43447 if (numericChars.length == 4) {
43457 this.setValue(this.defaultDialCode);
43461 hiddenEl : function()
43463 return this.el.select('input.hidden-tel-input',true).first();
43466 // after setting val
43467 onKeyUp : function(e){
43468 this.setValue(this.getValue());
43471 onKeyPress : function(e){
43472 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43479 * @class Roo.bootstrap.MoneyField
43480 * @extends Roo.bootstrap.ComboBox
43481 * Bootstrap MoneyField class
43484 * Create a new MoneyField.
43485 * @param {Object} config Configuration options
43488 Roo.bootstrap.MoneyField = function(config) {
43490 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43494 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43497 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43499 allowDecimals : true,
43501 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43503 decimalSeparator : ".",
43505 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43507 decimalPrecision : 0,
43509 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43511 allowNegative : true,
43513 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43517 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43519 minValue : Number.NEGATIVE_INFINITY,
43521 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43523 maxValue : Number.MAX_VALUE,
43525 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43527 minText : "The minimum value for this field is {0}",
43529 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43531 maxText : "The maximum value for this field is {0}",
43533 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43534 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43536 nanText : "{0} is not a valid number",
43538 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43542 * @cfg {String} defaults currency of the MoneyField
43543 * value should be in lkey
43545 defaultCurrency : false,
43547 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43549 thousandsDelimiter : false,
43551 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43562 getAutoCreate : function()
43564 var align = this.labelAlign || this.parentLabelAlign();
43576 cls : 'form-control roo-money-amount-input',
43577 autocomplete: 'new-password'
43580 var hiddenInput = {
43584 cls: 'hidden-number-input'
43587 if(this.max_length) {
43588 input.maxlength = this.max_length;
43592 hiddenInput.name = this.name;
43595 if (this.disabled) {
43596 input.disabled = true;
43599 var clg = 12 - this.inputlg;
43600 var cmd = 12 - this.inputmd;
43601 var csm = 12 - this.inputsm;
43602 var cxs = 12 - this.inputxs;
43606 cls : 'row roo-money-field',
43610 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43614 cls: 'roo-select2-container input-group',
43618 cls : 'form-control roo-money-currency-input',
43619 autocomplete: 'new-password',
43621 name : this.currencyName
43625 cls : 'input-group-addon',
43639 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43643 cls: this.hasFeedback ? 'has-feedback' : '',
43654 if (this.fieldLabel.length) {
43657 tooltip: 'This field is required'
43663 cls: 'control-label',
43669 html: this.fieldLabel
43672 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43678 if(this.indicatorpos == 'right') {
43679 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43686 if(align == 'left') {
43694 if(this.labelWidth > 12){
43695 label.style = "width: " + this.labelWidth + 'px';
43697 if(this.labelWidth < 13 && this.labelmd == 0){
43698 this.labelmd = this.labelWidth;
43700 if(this.labellg > 0){
43701 label.cls += ' col-lg-' + this.labellg;
43702 input.cls += ' col-lg-' + (12 - this.labellg);
43704 if(this.labelmd > 0){
43705 label.cls += ' col-md-' + this.labelmd;
43706 container.cls += ' col-md-' + (12 - this.labelmd);
43708 if(this.labelsm > 0){
43709 label.cls += ' col-sm-' + this.labelsm;
43710 container.cls += ' col-sm-' + (12 - this.labelsm);
43712 if(this.labelxs > 0){
43713 label.cls += ' col-xs-' + this.labelxs;
43714 container.cls += ' col-xs-' + (12 - this.labelxs);
43725 var settings = this;
43727 ['xs','sm','md','lg'].map(function(size){
43728 if (settings[size]) {
43729 cfg.cls += ' col-' + size + '-' + settings[size];
43736 initEvents : function()
43738 this.indicator = this.indicatorEl();
43740 this.initCurrencyEvent();
43742 this.initNumberEvent();
43745 initCurrencyEvent : function()
43748 throw "can not find store for combo";
43751 this.store = Roo.factory(this.store, Roo.data);
43752 this.store.parent = this;
43756 this.triggerEl = this.el.select('.input-group-addon', true).first();
43758 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43763 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43764 _this.list.setWidth(lw);
43767 this.list.on('mouseover', this.onViewOver, this);
43768 this.list.on('mousemove', this.onViewMove, this);
43769 this.list.on('scroll', this.onViewScroll, this);
43772 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43775 this.view = new Roo.View(this.list, this.tpl, {
43776 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43779 this.view.on('click', this.onViewClick, this);
43781 this.store.on('beforeload', this.onBeforeLoad, this);
43782 this.store.on('load', this.onLoad, this);
43783 this.store.on('loadexception', this.onLoadException, this);
43785 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43786 "up" : function(e){
43787 this.inKeyMode = true;
43791 "down" : function(e){
43792 if(!this.isExpanded()){
43793 this.onTriggerClick();
43795 this.inKeyMode = true;
43800 "enter" : function(e){
43803 if(this.fireEvent("specialkey", this, e)){
43804 this.onViewClick(false);
43810 "esc" : function(e){
43814 "tab" : function(e){
43817 if(this.fireEvent("specialkey", this, e)){
43818 this.onViewClick(false);
43826 doRelay : function(foo, bar, hname){
43827 if(hname == 'down' || this.scope.isExpanded()){
43828 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43836 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43840 initNumberEvent : function(e)
43842 this.inputEl().on("keydown" , this.fireKey, this);
43843 this.inputEl().on("focus", this.onFocus, this);
43844 this.inputEl().on("blur", this.onBlur, this);
43846 this.inputEl().relayEvent('keyup', this);
43848 if(this.indicator){
43849 this.indicator.addClass('invisible');
43852 this.originalValue = this.getValue();
43854 if(this.validationEvent == 'keyup'){
43855 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43856 this.inputEl().on('keyup', this.filterValidation, this);
43858 else if(this.validationEvent !== false){
43859 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43862 if(this.selectOnFocus){
43863 this.on("focus", this.preFocus, this);
43866 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43867 this.inputEl().on("keypress", this.filterKeys, this);
43869 this.inputEl().relayEvent('keypress', this);
43872 var allowed = "0123456789";
43874 if(this.allowDecimals){
43875 allowed += this.decimalSeparator;
43878 if(this.allowNegative){
43882 if(this.thousandsDelimiter) {
43886 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43888 var keyPress = function(e){
43890 var k = e.getKey();
43892 var c = e.getCharCode();
43895 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43896 allowed.indexOf(String.fromCharCode(c)) === -1
43902 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43906 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43911 this.inputEl().on("keypress", keyPress, this);
43915 onTriggerClick : function(e)
43922 this.loadNext = false;
43924 if(this.isExpanded()){
43929 this.hasFocus = true;
43931 if(this.triggerAction == 'all') {
43932 this.doQuery(this.allQuery, true);
43936 this.doQuery(this.getRawValue());
43939 getCurrency : function()
43941 var v = this.currencyEl().getValue();
43946 restrictHeight : function()
43948 this.list.alignTo(this.currencyEl(), this.listAlign);
43949 this.list.alignTo(this.currencyEl(), this.listAlign);
43952 onViewClick : function(view, doFocus, el, e)
43954 var index = this.view.getSelectedIndexes()[0];
43956 var r = this.store.getAt(index);
43959 this.onSelect(r, index);
43963 onSelect : function(record, index){
43965 if(this.fireEvent('beforeselect', this, record, index) !== false){
43967 this.setFromCurrencyData(index > -1 ? record.data : false);
43971 this.fireEvent('select', this, record, index);
43975 setFromCurrencyData : function(o)
43979 this.lastCurrency = o;
43981 if (this.currencyField) {
43982 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43984 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43987 this.lastSelectionText = currency;
43989 //setting default currency
43990 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43991 this.setCurrency(this.defaultCurrency);
43995 this.setCurrency(currency);
43998 setFromData : function(o)
44002 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44004 this.setFromCurrencyData(c);
44009 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44011 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44014 this.setValue(value);
44018 setCurrency : function(v)
44020 this.currencyValue = v;
44023 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44028 setValue : function(v)
44030 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44036 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44038 this.inputEl().dom.value = (v == '') ? '' :
44039 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44041 if(!this.allowZero && v === '0') {
44042 this.hiddenEl().dom.value = '';
44043 this.inputEl().dom.value = '';
44050 getRawValue : function()
44052 var v = this.inputEl().getValue();
44057 getValue : function()
44059 return this.fixPrecision(this.parseValue(this.getRawValue()));
44062 parseValue : function(value)
44064 if(this.thousandsDelimiter) {
44066 r = new RegExp(",", "g");
44067 value = value.replace(r, "");
44070 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44071 return isNaN(value) ? '' : value;
44075 fixPrecision : function(value)
44077 if(this.thousandsDelimiter) {
44079 r = new RegExp(",", "g");
44080 value = value.replace(r, "");
44083 var nan = isNaN(value);
44085 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44086 return nan ? '' : value;
44088 return parseFloat(value).toFixed(this.decimalPrecision);
44091 decimalPrecisionFcn : function(v)
44093 return Math.floor(v);
44096 validateValue : function(value)
44098 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44102 var num = this.parseValue(value);
44105 this.markInvalid(String.format(this.nanText, value));
44109 if(num < this.minValue){
44110 this.markInvalid(String.format(this.minText, this.minValue));
44114 if(num > this.maxValue){
44115 this.markInvalid(String.format(this.maxText, this.maxValue));
44122 validate : function()
44124 if(this.disabled || this.allowBlank){
44129 var currency = this.getCurrency();
44131 if(this.validateValue(this.getRawValue()) && currency.length){
44136 this.markInvalid();
44140 getName: function()
44145 beforeBlur : function()
44151 var v = this.parseValue(this.getRawValue());
44158 onBlur : function()
44162 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44163 //this.el.removeClass(this.focusClass);
44166 this.hasFocus = false;
44168 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44172 var v = this.getValue();
44174 if(String(v) !== String(this.startValue)){
44175 this.fireEvent('change', this, v, this.startValue);
44178 this.fireEvent("blur", this);
44181 inputEl : function()
44183 return this.el.select('.roo-money-amount-input', true).first();
44186 currencyEl : function()
44188 return this.el.select('.roo-money-currency-input', true).first();
44191 hiddenEl : function()
44193 return this.el.select('input.hidden-number-input',true).first();
44197 * @class Roo.bootstrap.BezierSignature
44198 * @extends Roo.bootstrap.Component
44199 * Bootstrap BezierSignature class
44200 * This script refer to:
44201 * Title: Signature Pad
44203 * Availability: https://github.com/szimek/signature_pad
44206 * Create a new BezierSignature
44207 * @param {Object} config The config object
44210 Roo.bootstrap.BezierSignature = function(config){
44211 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44217 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44224 mouse_btn_down: true,
44227 * @cfg {int} canvas height
44229 canvas_height: '200px',
44232 * @cfg {float|function} Radius of a single dot.
44237 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44242 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44247 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44252 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44257 * @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.
44259 bg_color: 'rgba(0, 0, 0, 0)',
44262 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44264 dot_color: 'black',
44267 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44269 velocity_filter_weight: 0.7,
44272 * @cfg {function} Callback when stroke begin.
44277 * @cfg {function} Callback when stroke end.
44281 getAutoCreate : function()
44283 var cls = 'roo-signature column';
44286 cls += ' ' + this.cls;
44296 for(var i = 0; i < col_sizes.length; i++) {
44297 if(this[col_sizes[i]]) {
44298 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44308 cls: 'roo-signature-body',
44312 cls: 'roo-signature-body-canvas',
44313 height: this.canvas_height,
44314 width: this.canvas_width
44321 style: 'display: none'
44329 initEvents: function()
44331 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44333 var canvas = this.canvasEl();
44335 // mouse && touch event swapping...
44336 canvas.dom.style.touchAction = 'none';
44337 canvas.dom.style.msTouchAction = 'none';
44339 this.mouse_btn_down = false;
44340 canvas.on('mousedown', this._handleMouseDown, this);
44341 canvas.on('mousemove', this._handleMouseMove, this);
44342 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44344 if (window.PointerEvent) {
44345 canvas.on('pointerdown', this._handleMouseDown, this);
44346 canvas.on('pointermove', this._handleMouseMove, this);
44347 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44350 if ('ontouchstart' in window) {
44351 canvas.on('touchstart', this._handleTouchStart, this);
44352 canvas.on('touchmove', this._handleTouchMove, this);
44353 canvas.on('touchend', this._handleTouchEnd, this);
44356 Roo.EventManager.onWindowResize(this.resize, this, true);
44358 // file input event
44359 this.fileEl().on('change', this.uploadImage, this);
44366 resize: function(){
44368 var canvas = this.canvasEl().dom;
44369 var ctx = this.canvasElCtx();
44370 var img_data = false;
44372 if(canvas.width > 0) {
44373 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44375 // setting canvas width will clean img data
44378 var style = window.getComputedStyle ?
44379 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44381 var padding_left = parseInt(style.paddingLeft) || 0;
44382 var padding_right = parseInt(style.paddingRight) || 0;
44384 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44387 ctx.putImageData(img_data, 0, 0);
44391 _handleMouseDown: function(e)
44393 if (e.browserEvent.which === 1) {
44394 this.mouse_btn_down = true;
44395 this.strokeBegin(e);
44399 _handleMouseMove: function (e)
44401 if (this.mouse_btn_down) {
44402 this.strokeMoveUpdate(e);
44406 _handleMouseUp: function (e)
44408 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44409 this.mouse_btn_down = false;
44414 _handleTouchStart: function (e) {
44416 e.preventDefault();
44417 if (e.browserEvent.targetTouches.length === 1) {
44418 // var touch = e.browserEvent.changedTouches[0];
44419 // this.strokeBegin(touch);
44421 this.strokeBegin(e); // assume e catching the correct xy...
44425 _handleTouchMove: function (e) {
44426 e.preventDefault();
44427 // var touch = event.targetTouches[0];
44428 // _this._strokeMoveUpdate(touch);
44429 this.strokeMoveUpdate(e);
44432 _handleTouchEnd: function (e) {
44433 var wasCanvasTouched = e.target === this.canvasEl().dom;
44434 if (wasCanvasTouched) {
44435 e.preventDefault();
44436 // var touch = event.changedTouches[0];
44437 // _this._strokeEnd(touch);
44442 reset: function () {
44443 this._lastPoints = [];
44444 this._lastVelocity = 0;
44445 this._lastWidth = (this.min_width + this.max_width) / 2;
44446 this.canvasElCtx().fillStyle = this.dot_color;
44449 strokeMoveUpdate: function(e)
44451 this.strokeUpdate(e);
44453 if (this.throttle) {
44454 this.throttleStroke(this.strokeUpdate, this.throttle);
44457 this.strokeUpdate(e);
44461 strokeBegin: function(e)
44463 var newPointGroup = {
44464 color: this.dot_color,
44468 if (typeof this.onBegin === 'function') {
44472 this.curve_data.push(newPointGroup);
44474 this.strokeUpdate(e);
44477 strokeUpdate: function(e)
44479 var rect = this.canvasEl().dom.getBoundingClientRect();
44480 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44481 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44482 var lastPoints = lastPointGroup.points;
44483 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44484 var isLastPointTooClose = lastPoint
44485 ? point.distanceTo(lastPoint) <= this.min_distance
44487 var color = lastPointGroup.color;
44488 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44489 var curve = this.addPoint(point);
44491 this.drawDot({color: color, point: point});
44494 this.drawCurve({color: color, curve: curve});
44504 strokeEnd: function(e)
44506 this.strokeUpdate(e);
44507 if (typeof this.onEnd === 'function') {
44512 addPoint: function (point) {
44513 var _lastPoints = this._lastPoints;
44514 _lastPoints.push(point);
44515 if (_lastPoints.length > 2) {
44516 if (_lastPoints.length === 3) {
44517 _lastPoints.unshift(_lastPoints[0]);
44519 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44520 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44521 _lastPoints.shift();
44527 calculateCurveWidths: function (startPoint, endPoint) {
44528 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44529 (1 - this.velocity_filter_weight) * this._lastVelocity;
44531 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44534 start: this._lastWidth
44537 this._lastVelocity = velocity;
44538 this._lastWidth = newWidth;
44542 drawDot: function (_a) {
44543 var color = _a.color, point = _a.point;
44544 var ctx = this.canvasElCtx();
44545 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44547 this.drawCurveSegment(point.x, point.y, width);
44549 ctx.fillStyle = color;
44553 drawCurve: function (_a) {
44554 var color = _a.color, curve = _a.curve;
44555 var ctx = this.canvasElCtx();
44556 var widthDelta = curve.endWidth - curve.startWidth;
44557 var drawSteps = Math.floor(curve.length()) * 2;
44559 ctx.fillStyle = color;
44560 for (var i = 0; i < drawSteps; i += 1) {
44561 var t = i / drawSteps;
44567 var x = uuu * curve.startPoint.x;
44568 x += 3 * uu * t * curve.control1.x;
44569 x += 3 * u * tt * curve.control2.x;
44570 x += ttt * curve.endPoint.x;
44571 var y = uuu * curve.startPoint.y;
44572 y += 3 * uu * t * curve.control1.y;
44573 y += 3 * u * tt * curve.control2.y;
44574 y += ttt * curve.endPoint.y;
44575 var width = curve.startWidth + ttt * widthDelta;
44576 this.drawCurveSegment(x, y, width);
44582 drawCurveSegment: function (x, y, width) {
44583 var ctx = this.canvasElCtx();
44585 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44586 this.is_empty = false;
44591 var ctx = this.canvasElCtx();
44592 var canvas = this.canvasEl().dom;
44593 ctx.fillStyle = this.bg_color;
44594 ctx.clearRect(0, 0, canvas.width, canvas.height);
44595 ctx.fillRect(0, 0, canvas.width, canvas.height);
44596 this.curve_data = [];
44598 this.is_empty = true;
44603 return this.el.select('input',true).first();
44606 canvasEl: function()
44608 return this.el.select('canvas',true).first();
44611 canvasElCtx: function()
44613 return this.el.select('canvas',true).first().dom.getContext('2d');
44616 getImage: function(type)
44618 if(this.is_empty) {
44623 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44626 drawFromImage: function(img_src)
44628 var img = new Image();
44630 img.onload = function(){
44631 this.canvasElCtx().drawImage(img, 0, 0);
44636 this.is_empty = false;
44639 selectImage: function()
44641 this.fileEl().dom.click();
44644 uploadImage: function(e)
44646 var reader = new FileReader();
44648 reader.onload = function(e){
44649 var img = new Image();
44650 img.onload = function(){
44652 this.canvasElCtx().drawImage(img, 0, 0);
44654 img.src = e.target.result;
44657 reader.readAsDataURL(e.target.files[0]);
44660 // Bezier Point Constructor
44661 Point: (function () {
44662 function Point(x, y, time) {
44665 this.time = time || Date.now();
44667 Point.prototype.distanceTo = function (start) {
44668 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44670 Point.prototype.equals = function (other) {
44671 return this.x === other.x && this.y === other.y && this.time === other.time;
44673 Point.prototype.velocityFrom = function (start) {
44674 return this.time !== start.time
44675 ? this.distanceTo(start) / (this.time - start.time)
44682 // Bezier Constructor
44683 Bezier: (function () {
44684 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44685 this.startPoint = startPoint;
44686 this.control2 = control2;
44687 this.control1 = control1;
44688 this.endPoint = endPoint;
44689 this.startWidth = startWidth;
44690 this.endWidth = endWidth;
44692 Bezier.fromPoints = function (points, widths, scope) {
44693 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44694 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44695 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44697 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44698 var dx1 = s1.x - s2.x;
44699 var dy1 = s1.y - s2.y;
44700 var dx2 = s2.x - s3.x;
44701 var dy2 = s2.y - s3.y;
44702 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44703 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44704 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44705 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44706 var dxm = m1.x - m2.x;
44707 var dym = m1.y - m2.y;
44708 var k = l2 / (l1 + l2);
44709 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44710 var tx = s2.x - cm.x;
44711 var ty = s2.y - cm.y;
44713 c1: new scope.Point(m1.x + tx, m1.y + ty),
44714 c2: new scope.Point(m2.x + tx, m2.y + ty)
44717 Bezier.prototype.length = function () {
44722 for (var i = 0; i <= steps; i += 1) {
44724 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44725 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44727 var xdiff = cx - px;
44728 var ydiff = cy - py;
44729 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44736 Bezier.prototype.point = function (t, start, c1, c2, end) {
44737 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44738 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44739 + (3.0 * c2 * (1.0 - t) * t * t)
44740 + (end * t * t * t);
44745 throttleStroke: function(fn, wait) {
44746 if (wait === void 0) { wait = 250; }
44748 var timeout = null;
44752 var later = function () {
44753 previous = Date.now();
44755 result = fn.apply(storedContext, storedArgs);
44757 storedContext = null;
44761 return function wrapper() {
44763 for (var _i = 0; _i < arguments.length; _i++) {
44764 args[_i] = arguments[_i];
44766 var now = Date.now();
44767 var remaining = wait - (now - previous);
44768 storedContext = this;
44770 if (remaining <= 0 || remaining > wait) {
44772 clearTimeout(timeout);
44776 result = fn.apply(storedContext, storedArgs);
44778 storedContext = null;
44782 else if (!timeout) {
44783 timeout = window.setTimeout(later, remaining);