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.UploaderButton} this
2892 * @param {Array} Array of files that have been uploaded
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){
2977 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2978 this.selectorEl.dom.reset();
2980 this.fireEvent('uploaded', this, files );
2988 * addCard - add an Attachment to the uploader
2989 * @param data - the data about the image to upload
2993 title : "Title of file",
2994 is_uploaded : false,
2995 src : "http://.....",
2996 srcfile : { the File upload object },
2997 mimetype : file.type,
3000 .. any other data...
3025 * @class Roo.bootstrap.Img
3026 * @extends Roo.bootstrap.Component
3027 * Bootstrap Img class
3028 * @cfg {Boolean} imgResponsive false | true
3029 * @cfg {String} border rounded | circle | thumbnail
3030 * @cfg {String} src image source
3031 * @cfg {String} alt image alternative text
3032 * @cfg {String} href a tag href
3033 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3034 * @cfg {String} xsUrl xs image source
3035 * @cfg {String} smUrl sm image source
3036 * @cfg {String} mdUrl md image source
3037 * @cfg {String} lgUrl lg image source
3040 * Create a new Input
3041 * @param {Object} config The config object
3044 Roo.bootstrap.Img = function(config){
3045 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3051 * The img click event for the img.
3052 * @param {Roo.EventObject} e
3058 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3060 imgResponsive: true,
3070 getAutoCreate : function()
3072 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3073 return this.createSingleImg();
3078 cls: 'roo-image-responsive-group',
3083 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3085 if(!_this[size + 'Url']){
3091 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3092 html: _this.html || cfg.html,
3093 src: _this[size + 'Url']
3096 img.cls += ' roo-image-responsive-' + size;
3098 var s = ['xs', 'sm', 'md', 'lg'];
3100 s.splice(s.indexOf(size), 1);
3102 Roo.each(s, function(ss){
3103 img.cls += ' hidden-' + ss;
3106 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3107 cfg.cls += ' img-' + _this.border;
3111 cfg.alt = _this.alt;
3124 a.target = _this.target;
3128 cfg.cn.push((_this.href) ? a : img);
3135 createSingleImg : function()
3139 cls: (this.imgResponsive) ? 'img-responsive' : '',
3141 src : 'about:blank' // just incase src get's set to undefined?!?
3144 cfg.html = this.html || cfg.html;
3146 cfg.src = this.src || cfg.src;
3148 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3149 cfg.cls += ' img-' + this.border;
3166 a.target = this.target;
3171 return (this.href) ? a : cfg;
3174 initEvents: function()
3177 this.el.on('click', this.onClick, this);
3182 onClick : function(e)
3184 Roo.log('img onclick');
3185 this.fireEvent('click', this, e);
3188 * Sets the url of the image - used to update it
3189 * @param {String} url the url of the image
3192 setSrc : function(url)
3196 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3197 this.el.dom.src = url;
3201 this.el.select('img', true).first().dom.src = url;
3217 * @class Roo.bootstrap.Link
3218 * @extends Roo.bootstrap.Component
3219 * Bootstrap Link Class
3220 * @cfg {String} alt image alternative text
3221 * @cfg {String} href a tag href
3222 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3223 * @cfg {String} html the content of the link.
3224 * @cfg {String} anchor name for the anchor link
3225 * @cfg {String} fa - favicon
3227 * @cfg {Boolean} preventDefault (true | false) default false
3231 * Create a new Input
3232 * @param {Object} config The config object
3235 Roo.bootstrap.Link = function(config){
3236 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3242 * The img click event for the img.
3243 * @param {Roo.EventObject} e
3249 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3253 preventDefault: false,
3259 getAutoCreate : function()
3261 var html = this.html || '';
3263 if (this.fa !== false) {
3264 html = '<i class="fa fa-' + this.fa + '"></i>';
3269 // anchor's do not require html/href...
3270 if (this.anchor === false) {
3272 cfg.href = this.href || '#';
3274 cfg.name = this.anchor;
3275 if (this.html !== false || this.fa !== false) {
3278 if (this.href !== false) {
3279 cfg.href = this.href;
3283 if(this.alt !== false){
3288 if(this.target !== false) {
3289 cfg.target = this.target;
3295 initEvents: function() {
3297 if(!this.href || this.preventDefault){
3298 this.el.on('click', this.onClick, this);
3302 onClick : function(e)
3304 if(this.preventDefault){
3307 //Roo.log('img onclick');
3308 this.fireEvent('click', this, e);
3321 * @class Roo.bootstrap.Header
3322 * @extends Roo.bootstrap.Component
3323 * Bootstrap Header class
3324 * @cfg {String} html content of header
3325 * @cfg {Number} level (1|2|3|4|5|6) default 1
3328 * Create a new Header
3329 * @param {Object} config The config object
3333 Roo.bootstrap.Header = function(config){
3334 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3337 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3345 getAutoCreate : function(){
3350 tag: 'h' + (1 *this.level),
3351 html: this.html || ''
3363 * Ext JS Library 1.1.1
3364 * Copyright(c) 2006-2007, Ext JS, LLC.
3366 * Originally Released Under LGPL - original licence link has changed is not relivant.
3369 * <script type="text/javascript">
3373 * @class Roo.bootstrap.MenuMgr
3374 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3377 Roo.bootstrap.MenuMgr = function(){
3378 var menus, active, groups = {}, attached = false, lastShow = new Date();
3380 // private - called when first menu is created
3383 active = new Roo.util.MixedCollection();
3384 Roo.get(document).addKeyListener(27, function(){
3385 if(active.length > 0){
3393 if(active && active.length > 0){
3394 var c = active.clone();
3404 if(active.length < 1){
3405 Roo.get(document).un("mouseup", onMouseDown);
3413 var last = active.last();
3414 lastShow = new Date();
3417 Roo.get(document).on("mouseup", onMouseDown);
3422 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3423 m.parentMenu.activeChild = m;
3424 }else if(last && last.isVisible()){
3425 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3430 function onBeforeHide(m){
3432 m.activeChild.hide();
3434 if(m.autoHideTimer){
3435 clearTimeout(m.autoHideTimer);
3436 delete m.autoHideTimer;
3441 function onBeforeShow(m){
3442 var pm = m.parentMenu;
3443 if(!pm && !m.allowOtherMenus){
3445 }else if(pm && pm.activeChild && active != m){
3446 pm.activeChild.hide();
3450 // private this should really trigger on mouseup..
3451 function onMouseDown(e){
3452 Roo.log("on Mouse Up");
3454 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3455 Roo.log("MenuManager hideAll");
3464 function onBeforeCheck(mi, state){
3466 var g = groups[mi.group];
3467 for(var i = 0, l = g.length; i < l; i++){
3469 g[i].setChecked(false);
3478 * Hides all menus that are currently visible
3480 hideAll : function(){
3485 register : function(menu){
3489 menus[menu.id] = menu;
3490 menu.on("beforehide", onBeforeHide);
3491 menu.on("hide", onHide);
3492 menu.on("beforeshow", onBeforeShow);
3493 menu.on("show", onShow);
3495 if(g && menu.events["checkchange"]){
3499 groups[g].push(menu);
3500 menu.on("checkchange", onCheck);
3505 * Returns a {@link Roo.menu.Menu} object
3506 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3507 * be used to generate and return a new Menu instance.
3509 get : function(menu){
3510 if(typeof menu == "string"){ // menu id
3512 }else if(menu.events){ // menu instance
3515 /*else if(typeof menu.length == 'number'){ // array of menu items?
3516 return new Roo.bootstrap.Menu({items:menu});
3517 }else{ // otherwise, must be a config
3518 return new Roo.bootstrap.Menu(menu);
3525 unregister : function(menu){
3526 delete menus[menu.id];
3527 menu.un("beforehide", onBeforeHide);
3528 menu.un("hide", onHide);
3529 menu.un("beforeshow", onBeforeShow);
3530 menu.un("show", onShow);
3532 if(g && menu.events["checkchange"]){
3533 groups[g].remove(menu);
3534 menu.un("checkchange", onCheck);
3539 registerCheckable : function(menuItem){
3540 var g = menuItem.group;
3545 groups[g].push(menuItem);
3546 menuItem.on("beforecheckchange", onBeforeCheck);
3551 unregisterCheckable : function(menuItem){
3552 var g = menuItem.group;
3554 groups[g].remove(menuItem);
3555 menuItem.un("beforecheckchange", onBeforeCheck);
3567 * @class Roo.bootstrap.Menu
3568 * @extends Roo.bootstrap.Component
3569 * Bootstrap Menu class - container for MenuItems
3570 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3571 * @cfg {bool} hidden if the menu should be hidden when rendered.
3572 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3573 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3577 * @param {Object} config The config object
3581 Roo.bootstrap.Menu = function(config){
3582 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3583 if (this.registerMenu && this.type != 'treeview') {
3584 Roo.bootstrap.MenuMgr.register(this);
3591 * Fires before this menu is displayed (return false to block)
3592 * @param {Roo.menu.Menu} this
3597 * Fires before this menu is hidden (return false to block)
3598 * @param {Roo.menu.Menu} this
3603 * Fires after this menu is displayed
3604 * @param {Roo.menu.Menu} this
3609 * Fires after this menu is hidden
3610 * @param {Roo.menu.Menu} this
3615 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3616 * @param {Roo.menu.Menu} this
3617 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3618 * @param {Roo.EventObject} e
3623 * Fires when the mouse is hovering over this menu
3624 * @param {Roo.menu.Menu} this
3625 * @param {Roo.EventObject} e
3626 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3631 * Fires when the mouse exits this menu
3632 * @param {Roo.menu.Menu} this
3633 * @param {Roo.EventObject} e
3634 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3639 * Fires when a menu item contained in this menu is clicked
3640 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3641 * @param {Roo.EventObject} e
3645 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3648 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3652 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3655 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3657 registerMenu : true,
3659 menuItems :false, // stores the menu items..
3669 getChildContainer : function() {
3673 getAutoCreate : function(){
3675 //if (['right'].indexOf(this.align)!==-1) {
3676 // cfg.cn[1].cls += ' pull-right'
3682 cls : 'dropdown-menu' ,
3683 style : 'z-index:1000'
3687 if (this.type === 'submenu') {
3688 cfg.cls = 'submenu active';
3690 if (this.type === 'treeview') {
3691 cfg.cls = 'treeview-menu';
3696 initEvents : function() {
3698 // Roo.log("ADD event");
3699 // Roo.log(this.triggerEl.dom);
3701 this.triggerEl.on('click', this.onTriggerClick, this);
3703 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3706 if (this.triggerEl.hasClass('nav-item')) {
3707 // dropdown toggle on the 'a' in BS4?
3708 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3710 this.triggerEl.addClass('dropdown-toggle');
3713 this.el.on('touchstart' , this.onTouch, this);
3715 this.el.on('click' , this.onClick, this);
3717 this.el.on("mouseover", this.onMouseOver, this);
3718 this.el.on("mouseout", this.onMouseOut, this);
3722 findTargetItem : function(e)
3724 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3728 //Roo.log(t); Roo.log(t.id);
3730 //Roo.log(this.menuitems);
3731 return this.menuitems.get(t.id);
3733 //return this.items.get(t.menuItemId);
3739 onTouch : function(e)
3741 Roo.log("menu.onTouch");
3742 //e.stopEvent(); this make the user popdown broken
3746 onClick : function(e)
3748 Roo.log("menu.onClick");
3750 var t = this.findTargetItem(e);
3751 if(!t || t.isContainer){
3756 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3757 if(t == this.activeItem && t.shouldDeactivate(e)){
3758 this.activeItem.deactivate();
3759 delete this.activeItem;
3763 this.setActiveItem(t, true);
3771 Roo.log('pass click event');
3775 this.fireEvent("click", this, t, e);
3779 if(!t.href.length || t.href == '#'){
3780 (function() { _this.hide(); }).defer(100);
3785 onMouseOver : function(e){
3786 var t = this.findTargetItem(e);
3789 // if(t.canActivate && !t.disabled){
3790 // this.setActiveItem(t, true);
3794 this.fireEvent("mouseover", this, e, t);
3796 isVisible : function(){
3797 return !this.hidden;
3799 onMouseOut : function(e){
3800 var t = this.findTargetItem(e);
3803 // if(t == this.activeItem && t.shouldDeactivate(e)){
3804 // this.activeItem.deactivate();
3805 // delete this.activeItem;
3808 this.fireEvent("mouseout", this, e, t);
3813 * Displays this menu relative to another element
3814 * @param {String/HTMLElement/Roo.Element} element The element to align to
3815 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3816 * the element (defaults to this.defaultAlign)
3817 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3819 show : function(el, pos, parentMenu)
3821 if (false === this.fireEvent("beforeshow", this)) {
3822 Roo.log("show canceled");
3825 this.parentMenu = parentMenu;
3830 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3833 * Displays this menu at a specific xy position
3834 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3835 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3837 showAt : function(xy, parentMenu, /* private: */_e){
3838 this.parentMenu = parentMenu;
3843 this.fireEvent("beforeshow", this);
3844 //xy = this.el.adjustForConstraints(xy);
3848 this.hideMenuItems();
3849 this.hidden = false;
3850 this.triggerEl.addClass('open');
3851 this.el.addClass('show');
3853 // reassign x when hitting right
3854 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3855 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3858 // reassign y when hitting bottom
3859 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3860 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3863 // but the list may align on trigger left or trigger top... should it be a properity?
3865 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3870 this.fireEvent("show", this);
3876 this.doFocus.defer(50, this);
3880 doFocus : function(){
3882 this.focusEl.focus();
3887 * Hides this menu and optionally all parent menus
3888 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3890 hide : function(deep)
3892 if (false === this.fireEvent("beforehide", this)) {
3893 Roo.log("hide canceled");
3896 this.hideMenuItems();
3897 if(this.el && this.isVisible()){
3899 if(this.activeItem){
3900 this.activeItem.deactivate();
3901 this.activeItem = null;
3903 this.triggerEl.removeClass('open');;
3904 this.el.removeClass('show');
3906 this.fireEvent("hide", this);
3908 if(deep === true && this.parentMenu){
3909 this.parentMenu.hide(true);
3913 onTriggerClick : function(e)
3915 Roo.log('trigger click');
3917 var target = e.getTarget();
3919 Roo.log(target.nodeName.toLowerCase());
3921 if(target.nodeName.toLowerCase() === 'i'){
3927 onTriggerPress : function(e)
3929 Roo.log('trigger press');
3930 //Roo.log(e.getTarget());
3931 // Roo.log(this.triggerEl.dom);
3933 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3934 var pel = Roo.get(e.getTarget());
3935 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3936 Roo.log('is treeview or dropdown?');
3940 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3944 if (this.isVisible()) {
3949 this.show(this.triggerEl, '?', false);
3952 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3959 hideMenuItems : function()
3961 Roo.log("hide Menu Items");
3966 this.el.select('.open',true).each(function(aa) {
3968 aa.removeClass('open');
3972 addxtypeChild : function (tree, cntr) {
3973 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3975 this.menuitems.add(comp);
3987 this.getEl().dom.innerHTML = '';
3988 this.menuitems.clear();
4002 * @class Roo.bootstrap.MenuItem
4003 * @extends Roo.bootstrap.Component
4004 * Bootstrap MenuItem class
4005 * @cfg {String} html the menu label
4006 * @cfg {String} href the link
4007 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4008 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4009 * @cfg {Boolean} active used on sidebars to highlight active itesm
4010 * @cfg {String} fa favicon to show on left of menu item.
4011 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4015 * Create a new MenuItem
4016 * @param {Object} config The config object
4020 Roo.bootstrap.MenuItem = function(config){
4021 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4026 * The raw click event for the entire grid.
4027 * @param {Roo.bootstrap.MenuItem} this
4028 * @param {Roo.EventObject} e
4034 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4038 preventDefault: false,
4039 isContainer : false,
4043 getAutoCreate : function(){
4045 if(this.isContainer){
4048 cls: 'dropdown-menu-item '
4058 cls : 'dropdown-item',
4063 if (this.fa !== false) {
4066 cls : 'fa fa-' + this.fa
4075 cls: 'dropdown-menu-item',
4078 if (this.parent().type == 'treeview') {
4079 cfg.cls = 'treeview-menu';
4082 cfg.cls += ' active';
4087 anc.href = this.href || cfg.cn[0].href ;
4088 ctag.html = this.html || cfg.cn[0].html ;
4092 initEvents: function()
4094 if (this.parent().type == 'treeview') {
4095 this.el.select('a').on('click', this.onClick, this);
4099 this.menu.parentType = this.xtype;
4100 this.menu.triggerEl = this.el;
4101 this.menu = this.addxtype(Roo.apply({}, this.menu));
4105 onClick : function(e)
4107 Roo.log('item on click ');
4109 if(this.preventDefault){
4112 //this.parent().hideMenuItems();
4114 this.fireEvent('click', this, e);
4133 * @class Roo.bootstrap.MenuSeparator
4134 * @extends Roo.bootstrap.Component
4135 * Bootstrap MenuSeparator class
4138 * Create a new MenuItem
4139 * @param {Object} config The config object
4143 Roo.bootstrap.MenuSeparator = function(config){
4144 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4147 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4149 getAutoCreate : function(){
4168 * @class Roo.bootstrap.Modal
4169 * @extends Roo.bootstrap.Component
4170 * Bootstrap Modal class
4171 * @cfg {String} title Title of dialog
4172 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4173 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4174 * @cfg {Boolean} specificTitle default false
4175 * @cfg {Array} buttons Array of buttons or standard button set..
4176 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4177 * @cfg {Boolean} animate default true
4178 * @cfg {Boolean} allow_close default true
4179 * @cfg {Boolean} fitwindow default false
4180 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4181 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4182 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4183 * @cfg {String} size (sm|lg|xl) default empty
4184 * @cfg {Number} max_width set the max width of modal
4185 * @cfg {Boolean} editableTitle can the title be edited
4190 * Create a new Modal Dialog
4191 * @param {Object} config The config object
4194 Roo.bootstrap.Modal = function(config){
4195 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4200 * The raw btnclick event for the button
4201 * @param {Roo.EventObject} e
4206 * Fire when dialog resize
4207 * @param {Roo.bootstrap.Modal} this
4208 * @param {Roo.EventObject} e
4212 * @event titlechanged
4213 * Fire when the editable title has been changed
4214 * @param {Roo.bootstrap.Modal} this
4215 * @param {Roo.EventObject} value
4217 "titlechanged" : true
4220 this.buttons = this.buttons || [];
4223 this.tmpl = Roo.factory(this.tmpl);
4228 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4230 title : 'test dialog',
4240 specificTitle: false,
4242 buttonPosition: 'right',
4264 editableTitle : false,
4266 onRender : function(ct, position)
4268 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4271 var cfg = Roo.apply({}, this.getAutoCreate());
4274 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4276 //if (!cfg.name.length) {
4280 cfg.cls += ' ' + this.cls;
4283 cfg.style = this.style;
4285 this.el = Roo.get(document.body).createChild(cfg, position);
4287 //var type = this.el.dom.type;
4290 if(this.tabIndex !== undefined){
4291 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4294 this.dialogEl = this.el.select('.modal-dialog',true).first();
4295 this.bodyEl = this.el.select('.modal-body',true).first();
4296 this.closeEl = this.el.select('.modal-header .close', true).first();
4297 this.headerEl = this.el.select('.modal-header',true).first();
4298 this.titleEl = this.el.select('.modal-title',true).first();
4299 this.footerEl = this.el.select('.modal-footer',true).first();
4301 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4303 //this.el.addClass("x-dlg-modal");
4305 if (this.buttons.length) {
4306 Roo.each(this.buttons, function(bb) {
4307 var b = Roo.apply({}, bb);
4308 b.xns = b.xns || Roo.bootstrap;
4309 b.xtype = b.xtype || 'Button';
4310 if (typeof(b.listeners) == 'undefined') {
4311 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4314 var btn = Roo.factory(b);
4316 btn.render(this.getButtonContainer());
4320 // render the children.
4323 if(typeof(this.items) != 'undefined'){
4324 var items = this.items;
4327 for(var i =0;i < items.length;i++) {
4328 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4332 this.items = nitems;
4334 // where are these used - they used to be body/close/footer
4338 //this.el.addClass([this.fieldClass, this.cls]);
4342 getAutoCreate : function()
4344 // we will default to modal-body-overflow - might need to remove or make optional later.
4346 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4347 html : this.html || ''
4352 cls : 'modal-title',
4356 if(this.specificTitle){ // WTF is this?
4361 if (this.allow_close && Roo.bootstrap.version == 3) {
4371 if (this.editableTitle) {
4373 cls: 'form-control roo-editable-title d-none',
4379 if (this.allow_close && Roo.bootstrap.version == 4) {
4389 if(this.size.length){
4390 size = 'modal-' + this.size;
4393 var footer = Roo.bootstrap.version == 3 ?
4395 cls : 'modal-footer',
4399 cls: 'btn-' + this.buttonPosition
4404 { // BS4 uses mr-auto on left buttons....
4405 cls : 'modal-footer'
4416 cls: "modal-dialog " + size,
4419 cls : "modal-content",
4422 cls : 'modal-header',
4437 modal.cls += ' fade';
4443 getChildContainer : function() {
4448 getButtonContainer : function() {
4450 return Roo.bootstrap.version == 4 ?
4451 this.el.select('.modal-footer',true).first()
4452 : this.el.select('.modal-footer div',true).first();
4455 initEvents : function()
4457 if (this.allow_close) {
4458 this.closeEl.on('click', this.hide, this);
4460 Roo.EventManager.onWindowResize(this.resize, this, true);
4461 if (this.editableTitle) {
4462 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4463 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4464 this.headerEditEl.on('keyup', function(e) {
4465 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4466 this.toggleHeaderInput(false)
4469 this.headerEditEl.on('blur', function(e) {
4470 this.toggleHeaderInput(false)
4479 this.maskEl.setSize(
4480 Roo.lib.Dom.getViewWidth(true),
4481 Roo.lib.Dom.getViewHeight(true)
4484 if (this.fitwindow) {
4486 this.dialogEl.setStyle( { 'max-width' : '100%' });
4488 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4489 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4494 if(this.max_width !== 0) {
4496 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4499 this.setSize(w, this.height);
4503 if(this.max_height) {
4504 this.setSize(w,Math.min(
4506 Roo.lib.Dom.getViewportHeight(true) - 60
4512 if(!this.fit_content) {
4513 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4517 this.setSize(w, Math.min(
4519 this.headerEl.getHeight() +
4520 this.footerEl.getHeight() +
4521 this.getChildHeight(this.bodyEl.dom.childNodes),
4522 Roo.lib.Dom.getViewportHeight(true) - 60)
4528 setSize : function(w,h)
4539 if (!this.rendered) {
4542 this.toggleHeaderInput(false);
4543 //this.el.setStyle('display', 'block');
4544 this.el.removeClass('hideing');
4545 this.el.dom.style.display='block';
4547 Roo.get(document.body).addClass('modal-open');
4549 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4552 this.el.addClass('show');
4553 this.el.addClass('in');
4556 this.el.addClass('show');
4557 this.el.addClass('in');
4560 // not sure how we can show data in here..
4562 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4565 Roo.get(document.body).addClass("x-body-masked");
4567 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4568 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4569 this.maskEl.dom.style.display = 'block';
4570 this.maskEl.addClass('show');
4575 this.fireEvent('show', this);
4577 // set zindex here - otherwise it appears to be ignored...
4578 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4581 this.items.forEach( function(e) {
4582 e.layout ? e.layout() : false;
4590 if(this.fireEvent("beforehide", this) !== false){
4592 this.maskEl.removeClass('show');
4594 this.maskEl.dom.style.display = '';
4595 Roo.get(document.body).removeClass("x-body-masked");
4596 this.el.removeClass('in');
4597 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4599 if(this.animate){ // why
4600 this.el.addClass('hideing');
4601 this.el.removeClass('show');
4603 if (!this.el.hasClass('hideing')) {
4604 return; // it's been shown again...
4607 this.el.dom.style.display='';
4609 Roo.get(document.body).removeClass('modal-open');
4610 this.el.removeClass('hideing');
4614 this.el.removeClass('show');
4615 this.el.dom.style.display='';
4616 Roo.get(document.body).removeClass('modal-open');
4619 this.fireEvent('hide', this);
4622 isVisible : function()
4625 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4629 addButton : function(str, cb)
4633 var b = Roo.apply({}, { html : str } );
4634 b.xns = b.xns || Roo.bootstrap;
4635 b.xtype = b.xtype || 'Button';
4636 if (typeof(b.listeners) == 'undefined') {
4637 b.listeners = { click : cb.createDelegate(this) };
4640 var btn = Roo.factory(b);
4642 btn.render(this.getButtonContainer());
4648 setDefaultButton : function(btn)
4650 //this.el.select('.modal-footer').()
4653 resizeTo: function(w,h)
4655 this.dialogEl.setWidth(w);
4657 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4659 this.bodyEl.setHeight(h - diff);
4661 this.fireEvent('resize', this);
4664 setContentSize : function(w, h)
4668 onButtonClick: function(btn,e)
4671 this.fireEvent('btnclick', btn.name, e);
4674 * Set the title of the Dialog
4675 * @param {String} str new Title
4677 setTitle: function(str) {
4678 this.titleEl.dom.innerHTML = str;
4682 * Set the body of the Dialog
4683 * @param {String} str new Title
4685 setBody: function(str) {
4686 this.bodyEl.dom.innerHTML = str;
4689 * Set the body of the Dialog using the template
4690 * @param {Obj} data - apply this data to the template and replace the body contents.
4692 applyBody: function(obj)
4695 Roo.log("Error - using apply Body without a template");
4698 this.tmpl.overwrite(this.bodyEl, obj);
4701 getChildHeight : function(child_nodes)
4705 child_nodes.length == 0
4710 var child_height = 0;
4712 for(var i = 0; i < child_nodes.length; i++) {
4715 * for modal with tabs...
4716 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4718 var layout_childs = child_nodes[i].childNodes;
4720 for(var j = 0; j < layout_childs.length; j++) {
4722 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4724 var layout_body_childs = layout_childs[j].childNodes;
4726 for(var k = 0; k < layout_body_childs.length; k++) {
4728 if(layout_body_childs[k].classList.contains('navbar')) {
4729 child_height += layout_body_childs[k].offsetHeight;
4733 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4735 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4737 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4739 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4740 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4755 child_height += child_nodes[i].offsetHeight;
4756 // Roo.log(child_nodes[i].offsetHeight);
4759 return child_height;
4761 toggleHeaderInput : function(is_edit)
4763 if (!this.editableTitle) {
4764 return; // not editable.
4766 if (is_edit && this.is_header_editing) {
4767 return; // already editing..
4771 this.headerEditEl.dom.value = this.title;
4772 this.headerEditEl.removeClass('d-none');
4773 this.headerEditEl.dom.focus();
4774 this.titleEl.addClass('d-none');
4776 this.is_header_editing = true;
4779 // flip back to not editing.
4780 this.title = this.headerEditEl.dom.value;
4781 this.headerEditEl.addClass('d-none');
4782 this.titleEl.removeClass('d-none');
4783 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4784 this.is_header_editing = false;
4785 this.fireEvent('titlechanged', this, this.title);
4794 Roo.apply(Roo.bootstrap.Modal, {
4796 * Button config that displays a single OK button
4805 * Button config that displays Yes and No buttons
4821 * Button config that displays OK and Cancel buttons
4836 * Button config that displays Yes, No and Cancel buttons
4861 * messagebox - can be used as a replace
4865 * @class Roo.MessageBox
4866 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4870 Roo.Msg.alert('Status', 'Changes saved successfully.');
4872 // Prompt for user data:
4873 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4875 // process text value...
4879 // Show a dialog using config options:
4881 title:'Save Changes?',
4882 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4883 buttons: Roo.Msg.YESNOCANCEL,
4890 Roo.bootstrap.MessageBox = function(){
4891 var dlg, opt, mask, waitTimer;
4892 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4893 var buttons, activeTextEl, bwidth;
4897 var handleButton = function(button){
4899 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4903 var handleHide = function(){
4905 dlg.el.removeClass(opt.cls);
4908 // Roo.TaskMgr.stop(waitTimer);
4909 // waitTimer = null;
4914 var updateButtons = function(b){
4917 buttons["ok"].hide();
4918 buttons["cancel"].hide();
4919 buttons["yes"].hide();
4920 buttons["no"].hide();
4921 dlg.footerEl.hide();
4925 dlg.footerEl.show();
4926 for(var k in buttons){
4927 if(typeof buttons[k] != "function"){
4930 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4931 width += buttons[k].el.getWidth()+15;
4941 var handleEsc = function(d, k, e){
4942 if(opt && opt.closable !== false){
4952 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4953 * @return {Roo.BasicDialog} The BasicDialog element
4955 getDialog : function(){
4957 dlg = new Roo.bootstrap.Modal( {
4960 //constraintoviewport:false,
4962 //collapsible : false,
4967 //buttonAlign:"center",
4968 closeClick : function(){
4969 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4972 handleButton("cancel");
4977 dlg.on("hide", handleHide);
4979 //dlg.addKeyListener(27, handleEsc);
4981 this.buttons = buttons;
4982 var bt = this.buttonText;
4983 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4984 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4985 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4986 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4988 bodyEl = dlg.bodyEl.createChild({
4990 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4991 '<textarea class="roo-mb-textarea"></textarea>' +
4992 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4994 msgEl = bodyEl.dom.firstChild;
4995 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4996 textboxEl.enableDisplayMode();
4997 textboxEl.addKeyListener([10,13], function(){
4998 if(dlg.isVisible() && opt && opt.buttons){
5001 }else if(opt.buttons.yes){
5002 handleButton("yes");
5006 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5007 textareaEl.enableDisplayMode();
5008 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5009 progressEl.enableDisplayMode();
5011 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5012 var pf = progressEl.dom.firstChild;
5014 pp = Roo.get(pf.firstChild);
5015 pp.setHeight(pf.offsetHeight);
5023 * Updates the message box body text
5024 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5025 * the XHTML-compliant non-breaking space character '&#160;')
5026 * @return {Roo.MessageBox} This message box
5028 updateText : function(text)
5030 if(!dlg.isVisible() && !opt.width){
5031 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5032 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5034 msgEl.innerHTML = text || ' ';
5036 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5037 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5039 Math.min(opt.width || cw , this.maxWidth),
5040 Math.max(opt.minWidth || this.minWidth, bwidth)
5043 activeTextEl.setWidth(w);
5045 if(dlg.isVisible()){
5046 dlg.fixedcenter = false;
5048 // to big, make it scroll. = But as usual stupid IE does not support
5051 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5052 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5053 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5055 bodyEl.dom.style.height = '';
5056 bodyEl.dom.style.overflowY = '';
5059 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5061 bodyEl.dom.style.overflowX = '';
5064 dlg.setContentSize(w, bodyEl.getHeight());
5065 if(dlg.isVisible()){
5066 dlg.fixedcenter = true;
5072 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5073 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5074 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5075 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5076 * @return {Roo.MessageBox} This message box
5078 updateProgress : function(value, text){
5080 this.updateText(text);
5083 if (pp) { // weird bug on my firefox - for some reason this is not defined
5084 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5085 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5091 * Returns true if the message box is currently displayed
5092 * @return {Boolean} True if the message box is visible, else false
5094 isVisible : function(){
5095 return dlg && dlg.isVisible();
5099 * Hides the message box if it is displayed
5102 if(this.isVisible()){
5108 * Displays a new message box, or reinitializes an existing message box, based on the config options
5109 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5110 * The following config object properties are supported:
5112 Property Type Description
5113 ---------- --------------- ------------------------------------------------------------------------------------
5114 animEl String/Element An id or Element from which the message box should animate as it opens and
5115 closes (defaults to undefined)
5116 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5117 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5118 closable Boolean False to hide the top-right close button (defaults to true). Note that
5119 progress and wait dialogs will ignore this property and always hide the
5120 close button as they can only be closed programmatically.
5121 cls String A custom CSS class to apply to the message box element
5122 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5123 displayed (defaults to 75)
5124 fn Function A callback function to execute after closing the dialog. The arguments to the
5125 function will be btn (the name of the button that was clicked, if applicable,
5126 e.g. "ok"), and text (the value of the active text field, if applicable).
5127 Progress and wait dialogs will ignore this option since they do not respond to
5128 user actions and can only be closed programmatically, so any required function
5129 should be called by the same code after it closes the dialog.
5130 icon String A CSS class that provides a background image to be used as an icon for
5131 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5132 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5133 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5134 modal Boolean False to allow user interaction with the page while the message box is
5135 displayed (defaults to true)
5136 msg String A string that will replace the existing message box body text (defaults
5137 to the XHTML-compliant non-breaking space character ' ')
5138 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5139 progress Boolean True to display a progress bar (defaults to false)
5140 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5141 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5142 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5143 title String The title text
5144 value String The string value to set into the active textbox element if displayed
5145 wait Boolean True to display a progress bar (defaults to false)
5146 width Number The width of the dialog in pixels
5153 msg: 'Please enter your address:',
5155 buttons: Roo.MessageBox.OKCANCEL,
5158 animEl: 'addAddressBtn'
5161 * @param {Object} config Configuration options
5162 * @return {Roo.MessageBox} This message box
5164 show : function(options)
5167 // this causes nightmares if you show one dialog after another
5168 // especially on callbacks..
5170 if(this.isVisible()){
5173 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5174 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5175 Roo.log("New Dialog Message:" + options.msg )
5176 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5177 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5180 var d = this.getDialog();
5182 d.setTitle(opt.title || " ");
5183 d.closeEl.setDisplayed(opt.closable !== false);
5184 activeTextEl = textboxEl;
5185 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5190 textareaEl.setHeight(typeof opt.multiline == "number" ?
5191 opt.multiline : this.defaultTextHeight);
5192 activeTextEl = textareaEl;
5201 progressEl.setDisplayed(opt.progress === true);
5203 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5205 this.updateProgress(0);
5206 activeTextEl.dom.value = opt.value || "";
5208 dlg.setDefaultButton(activeTextEl);
5210 var bs = opt.buttons;
5214 }else if(bs && bs.yes){
5215 db = buttons["yes"];
5217 dlg.setDefaultButton(db);
5219 bwidth = updateButtons(opt.buttons);
5220 this.updateText(opt.msg);
5222 d.el.addClass(opt.cls);
5224 d.proxyDrag = opt.proxyDrag === true;
5225 d.modal = opt.modal !== false;
5226 d.mask = opt.modal !== false ? mask : false;
5228 // force it to the end of the z-index stack so it gets a cursor in FF
5229 document.body.appendChild(dlg.el.dom);
5230 d.animateTarget = null;
5231 d.show(options.animEl);
5237 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5238 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5239 * and closing the message box when the process is complete.
5240 * @param {String} title The title bar text
5241 * @param {String} msg The message box body text
5242 * @return {Roo.MessageBox} This message box
5244 progress : function(title, msg){
5251 minWidth: this.minProgressWidth,
5258 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5259 * If a callback function is passed it will be called after the user clicks the button, and the
5260 * id of the button that was clicked will be passed as the only parameter to the callback
5261 * (could also be the top-right close button).
5262 * @param {String} title The title bar text
5263 * @param {String} msg The message box body text
5264 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5265 * @param {Object} scope (optional) The scope of the callback function
5266 * @return {Roo.MessageBox} This message box
5268 alert : function(title, msg, fn, scope)
5283 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5284 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5285 * You are responsible for closing the message box when the process is complete.
5286 * @param {String} msg The message box body text
5287 * @param {String} title (optional) The title bar text
5288 * @return {Roo.MessageBox} This message box
5290 wait : function(msg, title){
5301 waitTimer = Roo.TaskMgr.start({
5303 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5311 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5312 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5313 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5314 * @param {String} title The title bar text
5315 * @param {String} msg The message box body text
5316 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5317 * @param {Object} scope (optional) The scope of the callback function
5318 * @return {Roo.MessageBox} This message box
5320 confirm : function(title, msg, fn, scope){
5324 buttons: this.YESNO,
5333 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5334 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5335 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5336 * (could also be the top-right close button) and the text that was entered will be passed as the two
5337 * parameters to the callback.
5338 * @param {String} title The title bar text
5339 * @param {String} msg The message box body text
5340 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5341 * @param {Object} scope (optional) The scope of the callback function
5342 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5343 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5344 * @return {Roo.MessageBox} This message box
5346 prompt : function(title, msg, fn, scope, multiline){
5350 buttons: this.OKCANCEL,
5355 multiline: multiline,
5362 * Button config that displays a single OK button
5367 * Button config that displays Yes and No buttons
5370 YESNO : {yes:true, no:true},
5372 * Button config that displays OK and Cancel buttons
5375 OKCANCEL : {ok:true, cancel:true},
5377 * Button config that displays Yes, No and Cancel buttons
5380 YESNOCANCEL : {yes:true, no:true, cancel:true},
5383 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5386 defaultTextHeight : 75,
5388 * The maximum width in pixels of the message box (defaults to 600)
5393 * The minimum width in pixels of the message box (defaults to 100)
5398 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5399 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5402 minProgressWidth : 250,
5404 * An object containing the default button text strings that can be overriden for localized language support.
5405 * Supported properties are: ok, cancel, yes and no.
5406 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5419 * Shorthand for {@link Roo.MessageBox}
5421 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5422 Roo.Msg = Roo.Msg || Roo.MessageBox;
5431 * @class Roo.bootstrap.Navbar
5432 * @extends Roo.bootstrap.Component
5433 * Bootstrap Navbar class
5436 * Create a new Navbar
5437 * @param {Object} config The config object
5441 Roo.bootstrap.Navbar = function(config){
5442 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5446 * @event beforetoggle
5447 * Fire before toggle the menu
5448 * @param {Roo.EventObject} e
5450 "beforetoggle" : true
5454 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5463 getAutoCreate : function(){
5466 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5470 initEvents :function ()
5472 //Roo.log(this.el.select('.navbar-toggle',true));
5473 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5480 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5482 var size = this.el.getSize();
5483 this.maskEl.setSize(size.width, size.height);
5484 this.maskEl.enableDisplayMode("block");
5493 getChildContainer : function()
5495 if (this.el && this.el.select('.collapse').getCount()) {
5496 return this.el.select('.collapse',true).first();
5511 onToggle : function()
5514 if(this.fireEvent('beforetoggle', this) === false){
5517 var ce = this.el.select('.navbar-collapse',true).first();
5519 if (!ce.hasClass('show')) {
5529 * Expand the navbar pulldown
5531 expand : function ()
5534 var ce = this.el.select('.navbar-collapse',true).first();
5535 if (ce.hasClass('collapsing')) {
5538 ce.dom.style.height = '';
5540 ce.addClass('in'); // old...
5541 ce.removeClass('collapse');
5542 ce.addClass('show');
5543 var h = ce.getHeight();
5545 ce.removeClass('show');
5546 // at this point we should be able to see it..
5547 ce.addClass('collapsing');
5549 ce.setHeight(0); // resize it ...
5550 ce.on('transitionend', function() {
5551 //Roo.log('done transition');
5552 ce.removeClass('collapsing');
5553 ce.addClass('show');
5554 ce.removeClass('collapse');
5556 ce.dom.style.height = '';
5557 }, this, { single: true} );
5559 ce.dom.scrollTop = 0;
5562 * Collapse the navbar pulldown
5564 collapse : function()
5566 var ce = this.el.select('.navbar-collapse',true).first();
5568 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5569 // it's collapsed or collapsing..
5572 ce.removeClass('in'); // old...
5573 ce.setHeight(ce.getHeight());
5574 ce.removeClass('show');
5575 ce.addClass('collapsing');
5577 ce.on('transitionend', function() {
5578 ce.dom.style.height = '';
5579 ce.removeClass('collapsing');
5580 ce.addClass('collapse');
5581 }, this, { single: true} );
5601 * @class Roo.bootstrap.NavSimplebar
5602 * @extends Roo.bootstrap.Navbar
5603 * Bootstrap Sidebar class
5605 * @cfg {Boolean} inverse is inverted color
5607 * @cfg {String} type (nav | pills | tabs)
5608 * @cfg {Boolean} arrangement stacked | justified
5609 * @cfg {String} align (left | right) alignment
5611 * @cfg {Boolean} main (true|false) main nav bar? default false
5612 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5614 * @cfg {String} tag (header|footer|nav|div) default is nav
5616 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5620 * Create a new Sidebar
5621 * @param {Object} config The config object
5625 Roo.bootstrap.NavSimplebar = function(config){
5626 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5629 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5645 getAutoCreate : function(){
5649 tag : this.tag || 'div',
5650 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5652 if (['light','white'].indexOf(this.weight) > -1) {
5653 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5655 cfg.cls += ' bg-' + this.weight;
5658 cfg.cls += ' navbar-inverse';
5662 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5664 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5673 cls: 'nav nav-' + this.xtype,
5679 this.type = this.type || 'nav';
5680 if (['tabs','pills'].indexOf(this.type) != -1) {
5681 cfg.cn[0].cls += ' nav-' + this.type
5685 if (this.type!=='nav') {
5686 Roo.log('nav type must be nav/tabs/pills')
5688 cfg.cn[0].cls += ' navbar-nav'
5694 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5695 cfg.cn[0].cls += ' nav-' + this.arrangement;
5699 if (this.align === 'right') {
5700 cfg.cn[0].cls += ' navbar-right';
5725 * navbar-expand-md fixed-top
5729 * @class Roo.bootstrap.NavHeaderbar
5730 * @extends Roo.bootstrap.NavSimplebar
5731 * Bootstrap Sidebar class
5733 * @cfg {String} brand what is brand
5734 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5735 * @cfg {String} brand_href href of the brand
5736 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5737 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5738 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5739 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5742 * Create a new Sidebar
5743 * @param {Object} config The config object
5747 Roo.bootstrap.NavHeaderbar = function(config){
5748 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5752 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5759 desktopCenter : false,
5762 getAutoCreate : function(){
5765 tag: this.nav || 'nav',
5766 cls: 'navbar navbar-expand-md',
5772 if (this.desktopCenter) {
5773 cn.push({cls : 'container', cn : []});
5781 cls: 'navbar-toggle navbar-toggler',
5782 'data-toggle': 'collapse',
5787 html: 'Toggle navigation'
5791 cls: 'icon-bar navbar-toggler-icon'
5804 cn.push( Roo.bootstrap.version == 4 ? btn : {
5806 cls: 'navbar-header',
5815 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5819 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5821 if (['light','white'].indexOf(this.weight) > -1) {
5822 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5824 cfg.cls += ' bg-' + this.weight;
5827 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5828 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5830 // tag can override this..
5832 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5835 if (this.brand !== '') {
5836 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5837 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5839 href: this.brand_href ? this.brand_href : '#',
5840 cls: 'navbar-brand',
5848 cfg.cls += ' main-nav';
5856 getHeaderChildContainer : function()
5858 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5859 return this.el.select('.navbar-header',true).first();
5862 return this.getChildContainer();
5865 getChildContainer : function()
5868 return this.el.select('.roo-navbar-collapse',true).first();
5873 initEvents : function()
5875 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5877 if (this.autohide) {
5882 Roo.get(document).on('scroll',function(e) {
5883 var ns = Roo.get(document).getScroll().top;
5884 var os = prevScroll;
5888 ft.removeClass('slideDown');
5889 ft.addClass('slideUp');
5892 ft.removeClass('slideUp');
5893 ft.addClass('slideDown');
5914 * @class Roo.bootstrap.NavSidebar
5915 * @extends Roo.bootstrap.Navbar
5916 * Bootstrap Sidebar class
5919 * Create a new Sidebar
5920 * @param {Object} config The config object
5924 Roo.bootstrap.NavSidebar = function(config){
5925 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5928 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5930 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5932 getAutoCreate : function(){
5937 cls: 'sidebar sidebar-nav'
5959 * @class Roo.bootstrap.NavGroup
5960 * @extends Roo.bootstrap.Component
5961 * Bootstrap NavGroup class
5962 * @cfg {String} align (left|right)
5963 * @cfg {Boolean} inverse
5964 * @cfg {String} type (nav|pills|tab) default nav
5965 * @cfg {String} navId - reference Id for navbar.
5966 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5969 * Create a new nav group
5970 * @param {Object} config The config object
5973 Roo.bootstrap.NavGroup = function(config){
5974 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5977 Roo.bootstrap.NavGroup.register(this);
5981 * Fires when the active item changes
5982 * @param {Roo.bootstrap.NavGroup} this
5983 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5984 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5991 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6003 getAutoCreate : function()
6005 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6011 if (Roo.bootstrap.version == 4) {
6012 if (['tabs','pills'].indexOf(this.type) != -1) {
6013 cfg.cls += ' nav-' + this.type;
6015 // trying to remove so header bar can right align top?
6016 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6017 // do not use on header bar...
6018 cfg.cls += ' navbar-nav';
6023 if (['tabs','pills'].indexOf(this.type) != -1) {
6024 cfg.cls += ' nav-' + this.type
6026 if (this.type !== 'nav') {
6027 Roo.log('nav type must be nav/tabs/pills')
6029 cfg.cls += ' navbar-nav'
6033 if (this.parent() && this.parent().sidebar) {
6036 cls: 'dashboard-menu sidebar-menu'
6042 if (this.form === true) {
6045 cls: 'navbar-form form-inline'
6047 //nav navbar-right ml-md-auto
6048 if (this.align === 'right') {
6049 cfg.cls += ' navbar-right ml-md-auto';
6051 cfg.cls += ' navbar-left';
6055 if (this.align === 'right') {
6056 cfg.cls += ' navbar-right ml-md-auto';
6058 cfg.cls += ' mr-auto';
6062 cfg.cls += ' navbar-inverse';
6070 * sets the active Navigation item
6071 * @param {Roo.bootstrap.NavItem} the new current navitem
6073 setActiveItem : function(item)
6076 Roo.each(this.navItems, function(v){
6081 v.setActive(false, true);
6088 item.setActive(true, true);
6089 this.fireEvent('changed', this, item, prev);
6094 * gets the active Navigation item
6095 * @return {Roo.bootstrap.NavItem} the current navitem
6097 getActive : function()
6101 Roo.each(this.navItems, function(v){
6112 indexOfNav : function()
6116 Roo.each(this.navItems, function(v,i){
6127 * adds a Navigation item
6128 * @param {Roo.bootstrap.NavItem} the navitem to add
6130 addItem : function(cfg)
6132 if (this.form && Roo.bootstrap.version == 4) {
6135 var cn = new Roo.bootstrap.NavItem(cfg);
6137 cn.parentId = this.id;
6138 cn.onRender(this.el, null);
6142 * register a Navigation item
6143 * @param {Roo.bootstrap.NavItem} the navitem to add
6145 register : function(item)
6147 this.navItems.push( item);
6148 item.navId = this.navId;
6153 * clear all the Navigation item
6156 clearAll : function()
6159 this.el.dom.innerHTML = '';
6162 getNavItem: function(tabId)
6165 Roo.each(this.navItems, function(e) {
6166 if (e.tabId == tabId) {
6176 setActiveNext : function()
6178 var i = this.indexOfNav(this.getActive());
6179 if (i > this.navItems.length) {
6182 this.setActiveItem(this.navItems[i+1]);
6184 setActivePrev : function()
6186 var i = this.indexOfNav(this.getActive());
6190 this.setActiveItem(this.navItems[i-1]);
6192 clearWasActive : function(except) {
6193 Roo.each(this.navItems, function(e) {
6194 if (e.tabId != except.tabId && e.was_active) {
6195 e.was_active = false;
6202 getWasActive : function ()
6205 Roo.each(this.navItems, function(e) {
6220 Roo.apply(Roo.bootstrap.NavGroup, {
6224 * register a Navigation Group
6225 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6227 register : function(navgrp)
6229 this.groups[navgrp.navId] = navgrp;
6233 * fetch a Navigation Group based on the navigation ID
6234 * @param {string} the navgroup to add
6235 * @returns {Roo.bootstrap.NavGroup} the navgroup
6237 get: function(navId) {
6238 if (typeof(this.groups[navId]) == 'undefined') {
6240 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6242 return this.groups[navId] ;
6257 * @class Roo.bootstrap.NavItem
6258 * @extends Roo.bootstrap.Component
6259 * Bootstrap Navbar.NavItem class
6260 * @cfg {String} href link to
6261 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6262 * @cfg {Boolean} button_outline show and outlined button
6263 * @cfg {String} html content of button
6264 * @cfg {String} badge text inside badge
6265 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6266 * @cfg {String} glyphicon DEPRICATED - use fa
6267 * @cfg {String} icon DEPRICATED - use fa
6268 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6269 * @cfg {Boolean} active Is item active
6270 * @cfg {Boolean} disabled Is item disabled
6271 * @cfg {String} linkcls Link Class
6272 * @cfg {Boolean} preventDefault (true | false) default false
6273 * @cfg {String} tabId the tab that this item activates.
6274 * @cfg {String} tagtype (a|span) render as a href or span?
6275 * @cfg {Boolean} animateRef (true|false) link to element default false
6278 * Create a new Navbar Item
6279 * @param {Object} config The config object
6281 Roo.bootstrap.NavItem = function(config){
6282 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6287 * The raw click event for the entire grid.
6288 * @param {Roo.EventObject} e
6293 * Fires when the active item active state changes
6294 * @param {Roo.bootstrap.NavItem} this
6295 * @param {boolean} state the new state
6301 * Fires when scroll to element
6302 * @param {Roo.bootstrap.NavItem} this
6303 * @param {Object} options
6304 * @param {Roo.EventObject} e
6312 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6321 preventDefault : false,
6329 button_outline : false,
6333 getAutoCreate : function(){
6340 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6343 cfg.cls += ' active' ;
6345 if (this.disabled) {
6346 cfg.cls += ' disabled';
6350 if (this.button_weight.length) {
6351 cfg.tag = this.href ? 'a' : 'button';
6352 cfg.html = this.html || '';
6353 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6355 cfg.href = this.href;
6358 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6360 cfg.cls += " nav-html";
6363 // menu .. should add dropdown-menu class - so no need for carat..
6365 if (this.badge !== '') {
6367 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6372 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6376 href : this.href || "#",
6377 html: this.html || '',
6381 if (this.tagtype == 'a') {
6382 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6386 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6387 } else if (this.fa) {
6388 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6389 } else if(this.glyphicon) {
6390 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6392 cfg.cn[0].cls += " nav-html";
6396 cfg.cn[0].html += " <span class='caret'></span>";
6400 if (this.badge !== '') {
6401 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6409 onRender : function(ct, position)
6411 // Roo.log("Call onRender: " + this.xtype);
6412 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6416 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6417 this.navLink = this.el.select('.nav-link',true).first();
6418 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6423 initEvents: function()
6425 if (typeof (this.menu) != 'undefined') {
6426 this.menu.parentType = this.xtype;
6427 this.menu.triggerEl = this.el;
6428 this.menu = this.addxtype(Roo.apply({}, this.menu));
6431 this.el.on('click', this.onClick, this);
6433 //if(this.tagtype == 'span'){
6434 // this.el.select('span',true).on('click', this.onClick, this);
6437 // at this point parent should be available..
6438 this.parent().register(this);
6441 onClick : function(e)
6443 if (e.getTarget('.dropdown-menu-item')) {
6444 // did you click on a menu itemm.... - then don't trigger onclick..
6449 this.preventDefault ||
6452 Roo.log("NavItem - prevent Default?");
6456 if (this.disabled) {
6460 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6461 if (tg && tg.transition) {
6462 Roo.log("waiting for the transitionend");
6468 //Roo.log("fire event clicked");
6469 if(this.fireEvent('click', this, e) === false){
6473 if(this.tagtype == 'span'){
6477 //Roo.log(this.href);
6478 var ael = this.el.select('a',true).first();
6481 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6482 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6483 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6484 return; // ignore... - it's a 'hash' to another page.
6486 Roo.log("NavItem - prevent Default?");
6488 this.scrollToElement(e);
6492 var p = this.parent();
6494 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6495 if (typeof(p.setActiveItem) !== 'undefined') {
6496 p.setActiveItem(this);
6500 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6501 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6502 // remove the collapsed menu expand...
6503 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6507 isActive: function () {
6510 setActive : function(state, fire, is_was_active)
6512 if (this.active && !state && this.navId) {
6513 this.was_active = true;
6514 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6516 nv.clearWasActive(this);
6520 this.active = state;
6523 this.el.removeClass('active');
6524 this.navLink ? this.navLink.removeClass('active') : false;
6525 } else if (!this.el.hasClass('active')) {
6527 this.el.addClass('active');
6528 if (Roo.bootstrap.version == 4 && this.navLink ) {
6529 this.navLink.addClass('active');
6534 this.fireEvent('changed', this, state);
6537 // show a panel if it's registered and related..
6539 if (!this.navId || !this.tabId || !state || is_was_active) {
6543 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6547 var pan = tg.getPanelByName(this.tabId);
6551 // if we can not flip to new panel - go back to old nav highlight..
6552 if (false == tg.showPanel(pan)) {
6553 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6555 var onav = nv.getWasActive();
6557 onav.setActive(true, false, true);
6566 // this should not be here...
6567 setDisabled : function(state)
6569 this.disabled = state;
6571 this.el.removeClass('disabled');
6572 } else if (!this.el.hasClass('disabled')) {
6573 this.el.addClass('disabled');
6579 * Fetch the element to display the tooltip on.
6580 * @return {Roo.Element} defaults to this.el
6582 tooltipEl : function()
6584 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6587 scrollToElement : function(e)
6589 var c = document.body;
6592 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6594 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6595 c = document.documentElement;
6598 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6604 var o = target.calcOffsetsTo(c);
6611 this.fireEvent('scrollto', this, options, e);
6613 Roo.get(c).scrollTo('top', options.value, true);
6618 * Set the HTML (text content) of the item
6619 * @param {string} html content for the nav item
6621 setHtml : function(html)
6624 this.htmlEl.dom.innerHTML = html;
6636 * <span> icon </span>
6637 * <span> text </span>
6638 * <span>badge </span>
6642 * @class Roo.bootstrap.NavSidebarItem
6643 * @extends Roo.bootstrap.NavItem
6644 * Bootstrap Navbar.NavSidebarItem class
6645 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6646 * {Boolean} open is the menu open
6647 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6648 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6649 * {String} buttonSize (sm|md|lg)the extra classes for the button
6650 * {Boolean} showArrow show arrow next to the text (default true)
6652 * Create a new Navbar Button
6653 * @param {Object} config The config object
6655 Roo.bootstrap.NavSidebarItem = function(config){
6656 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6661 * The raw click event for the entire grid.
6662 * @param {Roo.EventObject} e
6667 * Fires when the active item active state changes
6668 * @param {Roo.bootstrap.NavSidebarItem} this
6669 * @param {boolean} state the new state
6677 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6679 badgeWeight : 'default',
6685 buttonWeight : 'default',
6691 getAutoCreate : function(){
6696 href : this.href || '#',
6702 if(this.buttonView){
6705 href : this.href || '#',
6706 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6719 cfg.cls += ' active';
6722 if (this.disabled) {
6723 cfg.cls += ' disabled';
6726 cfg.cls += ' open x-open';
6729 if (this.glyphicon || this.icon) {
6730 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6731 a.cn.push({ tag : 'i', cls : c }) ;
6734 if(!this.buttonView){
6737 html : this.html || ''
6744 if (this.badge !== '') {
6745 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6751 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6754 a.cls += ' dropdown-toggle treeview' ;
6760 initEvents : function()
6762 if (typeof (this.menu) != 'undefined') {
6763 this.menu.parentType = this.xtype;
6764 this.menu.triggerEl = this.el;
6765 this.menu = this.addxtype(Roo.apply({}, this.menu));
6768 this.el.on('click', this.onClick, this);
6770 if(this.badge !== ''){
6771 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6776 onClick : function(e)
6783 if(this.preventDefault){
6787 this.fireEvent('click', this, e);
6790 disable : function()
6792 this.setDisabled(true);
6797 this.setDisabled(false);
6800 setDisabled : function(state)
6802 if(this.disabled == state){
6806 this.disabled = state;
6809 this.el.addClass('disabled');
6813 this.el.removeClass('disabled');
6818 setActive : function(state)
6820 if(this.active == state){
6824 this.active = state;
6827 this.el.addClass('active');
6831 this.el.removeClass('active');
6836 isActive: function ()
6841 setBadge : function(str)
6847 this.badgeEl.dom.innerHTML = str;
6862 Roo.namespace('Roo.bootstrap.breadcrumb');
6866 * @class Roo.bootstrap.breadcrumb.Nav
6867 * @extends Roo.bootstrap.Component
6868 * Bootstrap Breadcrumb Nav Class
6870 * @children Roo.bootstrap.breadcrumb.Item
6873 * Create a new breadcrumb.Nav
6874 * @param {Object} config The config object
6878 Roo.bootstrap.breadcrumb.Nav = function(config){
6879 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6884 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6886 getAutoCreate : function()
6903 initEvents: function()
6905 this.olEl = this.el.select('ol',true).first();
6907 getChildContainer : function()
6923 * @class Roo.bootstrap.breadcrumb.Nav
6924 * @extends Roo.bootstrap.Component
6925 * Bootstrap Breadcrumb Nav Class
6927 * @children Roo.bootstrap.breadcrumb.Component
6928 * @cfg {String} html the content of the link.
6929 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6930 * @cfg {Boolean} active is it active
6934 * Create a new breadcrumb.Nav
6935 * @param {Object} config The config object
6938 Roo.bootstrap.breadcrumb.Item = function(config){
6939 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6944 * The img click event for the img.
6945 * @param {Roo.EventObject} e
6952 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6957 getAutoCreate : function()
6962 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6964 if (this.href !== false) {
6971 cfg.html = this.html;
6977 initEvents: function()
6980 this.el.select('a', true).first().on('click',this.onClick, this)
6984 onClick : function(e)
6987 this.fireEvent('click',this, e);
7000 * @class Roo.bootstrap.Row
7001 * @extends Roo.bootstrap.Component
7002 * Bootstrap Row class (contains columns...)
7006 * @param {Object} config The config object
7009 Roo.bootstrap.Row = function(config){
7010 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7013 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7015 getAutoCreate : function(){
7034 * @class Roo.bootstrap.Pagination
7035 * @extends Roo.bootstrap.Component
7036 * Bootstrap Pagination class
7037 * @cfg {String} size xs | sm | md | lg
7038 * @cfg {Boolean} inverse false | true
7041 * Create a new Pagination
7042 * @param {Object} config The config object
7045 Roo.bootstrap.Pagination = function(config){
7046 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7049 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7055 getAutoCreate : function(){
7061 cfg.cls += ' inverse';
7067 cfg.cls += " " + this.cls;
7085 * @class Roo.bootstrap.PaginationItem
7086 * @extends Roo.bootstrap.Component
7087 * Bootstrap PaginationItem class
7088 * @cfg {String} html text
7089 * @cfg {String} href the link
7090 * @cfg {Boolean} preventDefault (true | false) default true
7091 * @cfg {Boolean} active (true | false) default false
7092 * @cfg {Boolean} disabled default false
7096 * Create a new PaginationItem
7097 * @param {Object} config The config object
7101 Roo.bootstrap.PaginationItem = function(config){
7102 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7107 * The raw click event for the entire grid.
7108 * @param {Roo.EventObject} e
7114 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7118 preventDefault: true,
7123 getAutoCreate : function(){
7129 href : this.href ? this.href : '#',
7130 html : this.html ? this.html : ''
7140 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7144 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7150 initEvents: function() {
7152 this.el.on('click', this.onClick, this);
7155 onClick : function(e)
7157 Roo.log('PaginationItem on click ');
7158 if(this.preventDefault){
7166 this.fireEvent('click', this, e);
7182 * @class Roo.bootstrap.Slider
7183 * @extends Roo.bootstrap.Component
7184 * Bootstrap Slider class
7187 * Create a new Slider
7188 * @param {Object} config The config object
7191 Roo.bootstrap.Slider = function(config){
7192 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7195 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7197 getAutoCreate : function(){
7201 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7205 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7217 * Ext JS Library 1.1.1
7218 * Copyright(c) 2006-2007, Ext JS, LLC.
7220 * Originally Released Under LGPL - original licence link has changed is not relivant.
7223 * <script type="text/javascript">
7228 * @class Roo.grid.ColumnModel
7229 * @extends Roo.util.Observable
7230 * This is the default implementation of a ColumnModel used by the Grid. It defines
7231 * the columns in the grid.
7234 var colModel = new Roo.grid.ColumnModel([
7235 {header: "Ticker", width: 60, sortable: true, locked: true},
7236 {header: "Company Name", width: 150, sortable: true},
7237 {header: "Market Cap.", width: 100, sortable: true},
7238 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7239 {header: "Employees", width: 100, sortable: true, resizable: false}
7244 * The config options listed for this class are options which may appear in each
7245 * individual column definition.
7246 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7248 * @param {Object} config An Array of column config objects. See this class's
7249 * config objects for details.
7251 Roo.grid.ColumnModel = function(config){
7253 * The config passed into the constructor
7255 this.config = config;
7258 // if no id, create one
7259 // if the column does not have a dataIndex mapping,
7260 // map it to the order it is in the config
7261 for(var i = 0, len = config.length; i < len; i++){
7263 if(typeof c.dataIndex == "undefined"){
7266 if(typeof c.renderer == "string"){
7267 c.renderer = Roo.util.Format[c.renderer];
7269 if(typeof c.id == "undefined"){
7272 if(c.editor && c.editor.xtype){
7273 c.editor = Roo.factory(c.editor, Roo.grid);
7275 if(c.editor && c.editor.isFormField){
7276 c.editor = new Roo.grid.GridEditor(c.editor);
7278 this.lookup[c.id] = c;
7282 * The width of columns which have no width specified (defaults to 100)
7285 this.defaultWidth = 100;
7288 * Default sortable of columns which have no sortable specified (defaults to false)
7291 this.defaultSortable = false;
7295 * @event widthchange
7296 * Fires when the width of a column changes.
7297 * @param {ColumnModel} this
7298 * @param {Number} columnIndex The column index
7299 * @param {Number} newWidth The new width
7301 "widthchange": true,
7303 * @event headerchange
7304 * Fires when the text of a header changes.
7305 * @param {ColumnModel} this
7306 * @param {Number} columnIndex The column index
7307 * @param {Number} newText The new header text
7309 "headerchange": true,
7311 * @event hiddenchange
7312 * Fires when a column is hidden or "unhidden".
7313 * @param {ColumnModel} this
7314 * @param {Number} columnIndex The column index
7315 * @param {Boolean} hidden true if hidden, false otherwise
7317 "hiddenchange": true,
7319 * @event columnmoved
7320 * Fires when a column is moved.
7321 * @param {ColumnModel} this
7322 * @param {Number} oldIndex
7323 * @param {Number} newIndex
7325 "columnmoved" : true,
7327 * @event columlockchange
7328 * Fires when a column's locked state is changed
7329 * @param {ColumnModel} this
7330 * @param {Number} colIndex
7331 * @param {Boolean} locked true if locked
7333 "columnlockchange" : true
7335 Roo.grid.ColumnModel.superclass.constructor.call(this);
7337 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7339 * @cfg {String} header The header text to display in the Grid view.
7342 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7343 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7344 * specified, the column's index is used as an index into the Record's data Array.
7347 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7348 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7351 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7352 * Defaults to the value of the {@link #defaultSortable} property.
7353 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7356 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7359 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7362 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7365 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7368 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7369 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7370 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7371 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7374 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7377 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7380 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7383 * @cfg {String} cursor (Optional)
7386 * @cfg {String} tooltip (Optional)
7389 * @cfg {Number} xs (Optional)
7392 * @cfg {Number} sm (Optional)
7395 * @cfg {Number} md (Optional)
7398 * @cfg {Number} lg (Optional)
7401 * Returns the id of the column at the specified index.
7402 * @param {Number} index The column index
7403 * @return {String} the id
7405 getColumnId : function(index){
7406 return this.config[index].id;
7410 * Returns the column for a specified id.
7411 * @param {String} id The column id
7412 * @return {Object} the column
7414 getColumnById : function(id){
7415 return this.lookup[id];
7420 * Returns the column for a specified dataIndex.
7421 * @param {String} dataIndex The column dataIndex
7422 * @return {Object|Boolean} the column or false if not found
7424 getColumnByDataIndex: function(dataIndex){
7425 var index = this.findColumnIndex(dataIndex);
7426 return index > -1 ? this.config[index] : false;
7430 * Returns the index for a specified column id.
7431 * @param {String} id The column id
7432 * @return {Number} the index, or -1 if not found
7434 getIndexById : function(id){
7435 for(var i = 0, len = this.config.length; i < len; i++){
7436 if(this.config[i].id == id){
7444 * Returns the index for a specified column dataIndex.
7445 * @param {String} dataIndex The column dataIndex
7446 * @return {Number} the index, or -1 if not found
7449 findColumnIndex : function(dataIndex){
7450 for(var i = 0, len = this.config.length; i < len; i++){
7451 if(this.config[i].dataIndex == dataIndex){
7459 moveColumn : function(oldIndex, newIndex){
7460 var c = this.config[oldIndex];
7461 this.config.splice(oldIndex, 1);
7462 this.config.splice(newIndex, 0, c);
7463 this.dataMap = null;
7464 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7467 isLocked : function(colIndex){
7468 return this.config[colIndex].locked === true;
7471 setLocked : function(colIndex, value, suppressEvent){
7472 if(this.isLocked(colIndex) == value){
7475 this.config[colIndex].locked = value;
7477 this.fireEvent("columnlockchange", this, colIndex, value);
7481 getTotalLockedWidth : function(){
7483 for(var i = 0; i < this.config.length; i++){
7484 if(this.isLocked(i) && !this.isHidden(i)){
7485 this.totalWidth += this.getColumnWidth(i);
7491 getLockedCount : function(){
7492 for(var i = 0, len = this.config.length; i < len; i++){
7493 if(!this.isLocked(i)){
7498 return this.config.length;
7502 * Returns the number of columns.
7505 getColumnCount : function(visibleOnly){
7506 if(visibleOnly === true){
7508 for(var i = 0, len = this.config.length; i < len; i++){
7509 if(!this.isHidden(i)){
7515 return this.config.length;
7519 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7520 * @param {Function} fn
7521 * @param {Object} scope (optional)
7522 * @return {Array} result
7524 getColumnsBy : function(fn, scope){
7526 for(var i = 0, len = this.config.length; i < len; i++){
7527 var c = this.config[i];
7528 if(fn.call(scope||this, c, i) === true){
7536 * Returns true if the specified column is sortable.
7537 * @param {Number} col The column index
7540 isSortable : function(col){
7541 if(typeof this.config[col].sortable == "undefined"){
7542 return this.defaultSortable;
7544 return this.config[col].sortable;
7548 * Returns the rendering (formatting) function defined for the column.
7549 * @param {Number} col The column index.
7550 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7552 getRenderer : function(col){
7553 if(!this.config[col].renderer){
7554 return Roo.grid.ColumnModel.defaultRenderer;
7556 return this.config[col].renderer;
7560 * Sets the rendering (formatting) function for a column.
7561 * @param {Number} col The column index
7562 * @param {Function} fn The function to use to process the cell's raw data
7563 * to return HTML markup for the grid view. The render function is called with
7564 * the following parameters:<ul>
7565 * <li>Data value.</li>
7566 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7567 * <li>css A CSS style string to apply to the table cell.</li>
7568 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7569 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7570 * <li>Row index</li>
7571 * <li>Column index</li>
7572 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7574 setRenderer : function(col, fn){
7575 this.config[col].renderer = fn;
7579 * Returns the width for the specified column.
7580 * @param {Number} col The column index
7583 getColumnWidth : function(col){
7584 return this.config[col].width * 1 || this.defaultWidth;
7588 * Sets the width for a column.
7589 * @param {Number} col The column index
7590 * @param {Number} width The new width
7592 setColumnWidth : function(col, width, suppressEvent){
7593 this.config[col].width = width;
7594 this.totalWidth = null;
7596 this.fireEvent("widthchange", this, col, width);
7601 * Returns the total width of all columns.
7602 * @param {Boolean} includeHidden True to include hidden column widths
7605 getTotalWidth : function(includeHidden){
7606 if(!this.totalWidth){
7607 this.totalWidth = 0;
7608 for(var i = 0, len = this.config.length; i < len; i++){
7609 if(includeHidden || !this.isHidden(i)){
7610 this.totalWidth += this.getColumnWidth(i);
7614 return this.totalWidth;
7618 * Returns the header for the specified column.
7619 * @param {Number} col The column index
7622 getColumnHeader : function(col){
7623 return this.config[col].header;
7627 * Sets the header for a column.
7628 * @param {Number} col The column index
7629 * @param {String} header The new header
7631 setColumnHeader : function(col, header){
7632 this.config[col].header = header;
7633 this.fireEvent("headerchange", this, col, header);
7637 * Returns the tooltip for the specified column.
7638 * @param {Number} col The column index
7641 getColumnTooltip : function(col){
7642 return this.config[col].tooltip;
7645 * Sets the tooltip for a column.
7646 * @param {Number} col The column index
7647 * @param {String} tooltip The new tooltip
7649 setColumnTooltip : function(col, tooltip){
7650 this.config[col].tooltip = tooltip;
7654 * Returns the dataIndex for the specified column.
7655 * @param {Number} col The column index
7658 getDataIndex : function(col){
7659 return this.config[col].dataIndex;
7663 * Sets the dataIndex for a column.
7664 * @param {Number} col The column index
7665 * @param {Number} dataIndex The new dataIndex
7667 setDataIndex : function(col, dataIndex){
7668 this.config[col].dataIndex = dataIndex;
7674 * Returns true if the cell is editable.
7675 * @param {Number} colIndex The column index
7676 * @param {Number} rowIndex The row index - this is nto actually used..?
7679 isCellEditable : function(colIndex, rowIndex){
7680 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7684 * Returns the editor defined for the cell/column.
7685 * return false or null to disable editing.
7686 * @param {Number} colIndex The column index
7687 * @param {Number} rowIndex The row index
7690 getCellEditor : function(colIndex, rowIndex){
7691 return this.config[colIndex].editor;
7695 * Sets if a column is editable.
7696 * @param {Number} col The column index
7697 * @param {Boolean} editable True if the column is editable
7699 setEditable : function(col, editable){
7700 this.config[col].editable = editable;
7705 * Returns true if the column is hidden.
7706 * @param {Number} colIndex The column index
7709 isHidden : function(colIndex){
7710 return this.config[colIndex].hidden;
7715 * Returns true if the column width cannot be changed
7717 isFixed : function(colIndex){
7718 return this.config[colIndex].fixed;
7722 * Returns true if the column can be resized
7725 isResizable : function(colIndex){
7726 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7729 * Sets if a column is hidden.
7730 * @param {Number} colIndex The column index
7731 * @param {Boolean} hidden True if the column is hidden
7733 setHidden : function(colIndex, hidden){
7734 this.config[colIndex].hidden = hidden;
7735 this.totalWidth = null;
7736 this.fireEvent("hiddenchange", this, colIndex, hidden);
7740 * Sets the editor for a column.
7741 * @param {Number} col The column index
7742 * @param {Object} editor The editor object
7744 setEditor : function(col, editor){
7745 this.config[col].editor = editor;
7749 Roo.grid.ColumnModel.defaultRenderer = function(value)
7751 if(typeof value == "object") {
7754 if(typeof value == "string" && value.length < 1){
7758 return String.format("{0}", value);
7761 // Alias for backwards compatibility
7762 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7765 * Ext JS Library 1.1.1
7766 * Copyright(c) 2006-2007, Ext JS, LLC.
7768 * Originally Released Under LGPL - original licence link has changed is not relivant.
7771 * <script type="text/javascript">
7775 * @class Roo.LoadMask
7776 * A simple utility class for generically masking elements while loading data. If the element being masked has
7777 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7778 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7779 * element's UpdateManager load indicator and will be destroyed after the initial load.
7781 * Create a new LoadMask
7782 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7783 * @param {Object} config The config object
7785 Roo.LoadMask = function(el, config){
7786 this.el = Roo.get(el);
7787 Roo.apply(this, config);
7789 this.store.on('beforeload', this.onBeforeLoad, this);
7790 this.store.on('load', this.onLoad, this);
7791 this.store.on('loadexception', this.onLoadException, this);
7792 this.removeMask = false;
7794 var um = this.el.getUpdateManager();
7795 um.showLoadIndicator = false; // disable the default indicator
7796 um.on('beforeupdate', this.onBeforeLoad, this);
7797 um.on('update', this.onLoad, this);
7798 um.on('failure', this.onLoad, this);
7799 this.removeMask = true;
7803 Roo.LoadMask.prototype = {
7805 * @cfg {Boolean} removeMask
7806 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7807 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7811 * The text to display in a centered loading message box (defaults to 'Loading...')
7815 * @cfg {String} msgCls
7816 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7818 msgCls : 'x-mask-loading',
7821 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7827 * Disables the mask to prevent it from being displayed
7829 disable : function(){
7830 this.disabled = true;
7834 * Enables the mask so that it can be displayed
7836 enable : function(){
7837 this.disabled = false;
7840 onLoadException : function()
7844 if (typeof(arguments[3]) != 'undefined') {
7845 Roo.MessageBox.alert("Error loading",arguments[3]);
7849 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7850 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7857 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7862 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7866 onBeforeLoad : function(){
7868 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7873 destroy : function(){
7875 this.store.un('beforeload', this.onBeforeLoad, this);
7876 this.store.un('load', this.onLoad, this);
7877 this.store.un('loadexception', this.onLoadException, this);
7879 var um = this.el.getUpdateManager();
7880 um.un('beforeupdate', this.onBeforeLoad, this);
7881 um.un('update', this.onLoad, this);
7882 um.un('failure', this.onLoad, this);
7893 * @class Roo.bootstrap.Table
7894 * @extends Roo.bootstrap.Component
7895 * Bootstrap Table class
7896 * @cfg {String} cls table class
7897 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7898 * @cfg {String} bgcolor Specifies the background color for a table
7899 * @cfg {Number} border Specifies whether the table cells should have borders or not
7900 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7901 * @cfg {Number} cellspacing Specifies the space between cells
7902 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7903 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7904 * @cfg {String} sortable Specifies that the table should be sortable
7905 * @cfg {String} summary Specifies a summary of the content of a table
7906 * @cfg {Number} width Specifies the width of a table
7907 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7909 * @cfg {boolean} striped Should the rows be alternative striped
7910 * @cfg {boolean} bordered Add borders to the table
7911 * @cfg {boolean} hover Add hover highlighting
7912 * @cfg {boolean} condensed Format condensed
7913 * @cfg {boolean} responsive Format condensed
7914 * @cfg {Boolean} loadMask (true|false) default false
7915 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7916 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7917 * @cfg {Boolean} rowSelection (true|false) default false
7918 * @cfg {Boolean} cellSelection (true|false) default false
7919 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7920 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7921 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7922 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7926 * Create a new Table
7927 * @param {Object} config The config object
7930 Roo.bootstrap.Table = function(config){
7931 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7936 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7937 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7938 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7939 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7941 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7943 this.sm.grid = this;
7944 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7945 this.sm = this.selModel;
7946 this.sm.xmodule = this.xmodule || false;
7949 if (this.cm && typeof(this.cm.config) == 'undefined') {
7950 this.colModel = new Roo.grid.ColumnModel(this.cm);
7951 this.cm = this.colModel;
7952 this.cm.xmodule = this.xmodule || false;
7955 this.store= Roo.factory(this.store, Roo.data);
7956 this.ds = this.store;
7957 this.ds.xmodule = this.xmodule || false;
7960 if (this.footer && this.store) {
7961 this.footer.dataSource = this.ds;
7962 this.footer = Roo.factory(this.footer);
7969 * Fires when a cell is clicked
7970 * @param {Roo.bootstrap.Table} this
7971 * @param {Roo.Element} el
7972 * @param {Number} rowIndex
7973 * @param {Number} columnIndex
7974 * @param {Roo.EventObject} e
7978 * @event celldblclick
7979 * Fires when a cell is double clicked
7980 * @param {Roo.bootstrap.Table} this
7981 * @param {Roo.Element} el
7982 * @param {Number} rowIndex
7983 * @param {Number} columnIndex
7984 * @param {Roo.EventObject} e
7986 "celldblclick" : true,
7989 * Fires when a row is clicked
7990 * @param {Roo.bootstrap.Table} this
7991 * @param {Roo.Element} el
7992 * @param {Number} rowIndex
7993 * @param {Roo.EventObject} e
7997 * @event rowdblclick
7998 * Fires when a row is double clicked
7999 * @param {Roo.bootstrap.Table} this
8000 * @param {Roo.Element} el
8001 * @param {Number} rowIndex
8002 * @param {Roo.EventObject} e
8004 "rowdblclick" : true,
8007 * Fires when a mouseover occur
8008 * @param {Roo.bootstrap.Table} this
8009 * @param {Roo.Element} el
8010 * @param {Number} rowIndex
8011 * @param {Number} columnIndex
8012 * @param {Roo.EventObject} e
8017 * Fires when a mouseout occur
8018 * @param {Roo.bootstrap.Table} this
8019 * @param {Roo.Element} el
8020 * @param {Number} rowIndex
8021 * @param {Number} columnIndex
8022 * @param {Roo.EventObject} e
8027 * Fires when a row is rendered, so you can change add a style to it.
8028 * @param {Roo.bootstrap.Table} this
8029 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8033 * @event rowsrendered
8034 * Fires when all the rows have been rendered
8035 * @param {Roo.bootstrap.Table} this
8037 'rowsrendered' : true,
8039 * @event contextmenu
8040 * The raw contextmenu event for the entire grid.
8041 * @param {Roo.EventObject} e
8043 "contextmenu" : true,
8045 * @event rowcontextmenu
8046 * Fires when a row is right clicked
8047 * @param {Roo.bootstrap.Table} this
8048 * @param {Number} rowIndex
8049 * @param {Roo.EventObject} e
8051 "rowcontextmenu" : true,
8053 * @event cellcontextmenu
8054 * Fires when a cell is right clicked
8055 * @param {Roo.bootstrap.Table} this
8056 * @param {Number} rowIndex
8057 * @param {Number} cellIndex
8058 * @param {Roo.EventObject} e
8060 "cellcontextmenu" : true,
8062 * @event headercontextmenu
8063 * Fires when a header is right clicked
8064 * @param {Roo.bootstrap.Table} this
8065 * @param {Number} columnIndex
8066 * @param {Roo.EventObject} e
8068 "headercontextmenu" : true
8072 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8098 rowSelection : false,
8099 cellSelection : false,
8102 // Roo.Element - the tbody
8104 // Roo.Element - thead element
8107 container: false, // used by gridpanel...
8113 auto_hide_footer : false,
8115 getAutoCreate : function()
8117 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8124 if (this.scrollBody) {
8125 cfg.cls += ' table-body-fixed';
8128 cfg.cls += ' table-striped';
8132 cfg.cls += ' table-hover';
8134 if (this.bordered) {
8135 cfg.cls += ' table-bordered';
8137 if (this.condensed) {
8138 cfg.cls += ' table-condensed';
8140 if (this.responsive) {
8141 cfg.cls += ' table-responsive';
8145 cfg.cls+= ' ' +this.cls;
8148 // this lot should be simplifed...
8161 ].forEach(function(k) {
8169 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8172 if(this.store || this.cm){
8173 if(this.headerShow){
8174 cfg.cn.push(this.renderHeader());
8177 cfg.cn.push(this.renderBody());
8179 if(this.footerShow){
8180 cfg.cn.push(this.renderFooter());
8182 // where does this come from?
8183 //cfg.cls+= ' TableGrid';
8186 return { cn : [ cfg ] };
8189 initEvents : function()
8191 if(!this.store || !this.cm){
8194 if (this.selModel) {
8195 this.selModel.initEvents();
8199 //Roo.log('initEvents with ds!!!!');
8201 this.mainBody = this.el.select('tbody', true).first();
8202 this.mainHead = this.el.select('thead', true).first();
8203 this.mainFoot = this.el.select('tfoot', true).first();
8209 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8210 e.on('click', _this.sort, _this);
8213 this.mainBody.on("click", this.onClick, this);
8214 this.mainBody.on("dblclick", this.onDblClick, this);
8216 // why is this done????? = it breaks dialogs??
8217 //this.parent().el.setStyle('position', 'relative');
8221 this.footer.parentId = this.id;
8222 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8225 this.el.select('tfoot tr td').first().addClass('hide');
8230 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8233 this.store.on('load', this.onLoad, this);
8234 this.store.on('beforeload', this.onBeforeLoad, this);
8235 this.store.on('update', this.onUpdate, this);
8236 this.store.on('add', this.onAdd, this);
8237 this.store.on("clear", this.clear, this);
8239 this.el.on("contextmenu", this.onContextMenu, this);
8241 this.mainBody.on('scroll', this.onBodyScroll, this);
8243 this.cm.on("headerchange", this.onHeaderChange, this);
8245 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8249 onContextMenu : function(e, t)
8251 this.processEvent("contextmenu", e);
8254 processEvent : function(name, e)
8256 if (name != 'touchstart' ) {
8257 this.fireEvent(name, e);
8260 var t = e.getTarget();
8262 var cell = Roo.get(t);
8268 if(cell.findParent('tfoot', false, true)){
8272 if(cell.findParent('thead', false, true)){
8274 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8275 cell = Roo.get(t).findParent('th', false, true);
8277 Roo.log("failed to find th in thead?");
8278 Roo.log(e.getTarget());
8283 var cellIndex = cell.dom.cellIndex;
8285 var ename = name == 'touchstart' ? 'click' : name;
8286 this.fireEvent("header" + ename, this, cellIndex, e);
8291 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8292 cell = Roo.get(t).findParent('td', false, true);
8294 Roo.log("failed to find th in tbody?");
8295 Roo.log(e.getTarget());
8300 var row = cell.findParent('tr', false, true);
8301 var cellIndex = cell.dom.cellIndex;
8302 var rowIndex = row.dom.rowIndex - 1;
8306 this.fireEvent("row" + name, this, rowIndex, e);
8310 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8316 onMouseover : function(e, el)
8318 var cell = Roo.get(el);
8324 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8325 cell = cell.findParent('td', false, true);
8328 var row = cell.findParent('tr', false, true);
8329 var cellIndex = cell.dom.cellIndex;
8330 var rowIndex = row.dom.rowIndex - 1; // start from 0
8332 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8336 onMouseout : function(e, el)
8338 var cell = Roo.get(el);
8344 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8345 cell = cell.findParent('td', false, true);
8348 var row = cell.findParent('tr', false, true);
8349 var cellIndex = cell.dom.cellIndex;
8350 var rowIndex = row.dom.rowIndex - 1; // start from 0
8352 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8356 onClick : function(e, el)
8358 var cell = Roo.get(el);
8360 if(!cell || (!this.cellSelection && !this.rowSelection)){
8364 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8365 cell = cell.findParent('td', false, true);
8368 if(!cell || typeof(cell) == 'undefined'){
8372 var row = cell.findParent('tr', false, true);
8374 if(!row || typeof(row) == 'undefined'){
8378 var cellIndex = cell.dom.cellIndex;
8379 var rowIndex = this.getRowIndex(row);
8381 // why??? - should these not be based on SelectionModel?
8382 if(this.cellSelection){
8383 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8386 if(this.rowSelection){
8387 this.fireEvent('rowclick', this, row, rowIndex, e);
8393 onDblClick : function(e,el)
8395 var cell = Roo.get(el);
8397 if(!cell || (!this.cellSelection && !this.rowSelection)){
8401 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8402 cell = cell.findParent('td', false, true);
8405 if(!cell || typeof(cell) == 'undefined'){
8409 var row = cell.findParent('tr', false, true);
8411 if(!row || typeof(row) == 'undefined'){
8415 var cellIndex = cell.dom.cellIndex;
8416 var rowIndex = this.getRowIndex(row);
8418 if(this.cellSelection){
8419 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8422 if(this.rowSelection){
8423 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8427 sort : function(e,el)
8429 var col = Roo.get(el);
8431 if(!col.hasClass('sortable')){
8435 var sort = col.attr('sort');
8438 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8442 this.store.sortInfo = {field : sort, direction : dir};
8445 Roo.log("calling footer first");
8446 this.footer.onClick('first');
8449 this.store.load({ params : { start : 0 } });
8453 renderHeader : function()
8461 this.totalWidth = 0;
8463 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8465 var config = cm.config[i];
8469 cls : 'x-hcol-' + i,
8471 html: cm.getColumnHeader(i)
8476 if(typeof(config.sortable) != 'undefined' && config.sortable){
8478 c.html = '<i class="glyphicon"></i>' + c.html;
8481 // could use BS4 hidden-..-down
8483 if(typeof(config.lgHeader) != 'undefined'){
8484 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8487 if(typeof(config.mdHeader) != 'undefined'){
8488 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8491 if(typeof(config.smHeader) != 'undefined'){
8492 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8495 if(typeof(config.xsHeader) != 'undefined'){
8496 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8503 if(typeof(config.tooltip) != 'undefined'){
8504 c.tooltip = config.tooltip;
8507 if(typeof(config.colspan) != 'undefined'){
8508 c.colspan = config.colspan;
8511 if(typeof(config.hidden) != 'undefined' && config.hidden){
8512 c.style += ' display:none;';
8515 if(typeof(config.dataIndex) != 'undefined'){
8516 c.sort = config.dataIndex;
8521 if(typeof(config.align) != 'undefined' && config.align.length){
8522 c.style += ' text-align:' + config.align + ';';
8525 if(typeof(config.width) != 'undefined'){
8526 c.style += ' width:' + config.width + 'px;';
8527 this.totalWidth += config.width;
8529 this.totalWidth += 100; // assume minimum of 100 per column?
8532 if(typeof(config.cls) != 'undefined'){
8533 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8536 ['xs','sm','md','lg'].map(function(size){
8538 if(typeof(config[size]) == 'undefined'){
8542 if (!config[size]) { // 0 = hidden
8543 // BS 4 '0' is treated as hide that column and below.
8544 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8548 c.cls += ' col-' + size + '-' + config[size] + (
8549 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8561 renderBody : function()
8571 colspan : this.cm.getColumnCount()
8581 renderFooter : function()
8591 colspan : this.cm.getColumnCount()
8605 // Roo.log('ds onload');
8610 var ds = this.store;
8612 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8613 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8614 if (_this.store.sortInfo) {
8616 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8617 e.select('i', true).addClass(['glyphicon-arrow-up']);
8620 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8621 e.select('i', true).addClass(['glyphicon-arrow-down']);
8626 var tbody = this.mainBody;
8628 if(ds.getCount() > 0){
8629 ds.data.each(function(d,rowIndex){
8630 var row = this.renderRow(cm, ds, rowIndex);
8632 tbody.createChild(row);
8636 if(row.cellObjects.length){
8637 Roo.each(row.cellObjects, function(r){
8638 _this.renderCellObject(r);
8645 var tfoot = this.el.select('tfoot', true).first();
8647 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8649 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8651 var total = this.ds.getTotalCount();
8653 if(this.footer.pageSize < total){
8654 this.mainFoot.show();
8658 Roo.each(this.el.select('tbody td', true).elements, function(e){
8659 e.on('mouseover', _this.onMouseover, _this);
8662 Roo.each(this.el.select('tbody td', true).elements, function(e){
8663 e.on('mouseout', _this.onMouseout, _this);
8665 this.fireEvent('rowsrendered', this);
8671 onUpdate : function(ds,record)
8673 this.refreshRow(record);
8677 onRemove : function(ds, record, index, isUpdate){
8678 if(isUpdate !== true){
8679 this.fireEvent("beforerowremoved", this, index, record);
8681 var bt = this.mainBody.dom;
8683 var rows = this.el.select('tbody > tr', true).elements;
8685 if(typeof(rows[index]) != 'undefined'){
8686 bt.removeChild(rows[index].dom);
8689 // if(bt.rows[index]){
8690 // bt.removeChild(bt.rows[index]);
8693 if(isUpdate !== true){
8694 //this.stripeRows(index);
8695 //this.syncRowHeights(index, index);
8697 this.fireEvent("rowremoved", this, index, record);
8701 onAdd : function(ds, records, rowIndex)
8703 //Roo.log('on Add called');
8704 // - note this does not handle multiple adding very well..
8705 var bt = this.mainBody.dom;
8706 for (var i =0 ; i < records.length;i++) {
8707 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8708 //Roo.log(records[i]);
8709 //Roo.log(this.store.getAt(rowIndex+i));
8710 this.insertRow(this.store, rowIndex + i, false);
8717 refreshRow : function(record){
8718 var ds = this.store, index;
8719 if(typeof record == 'number'){
8721 record = ds.getAt(index);
8723 index = ds.indexOf(record);
8725 return; // should not happen - but seems to
8728 this.insertRow(ds, index, true);
8730 this.onRemove(ds, record, index+1, true);
8732 //this.syncRowHeights(index, index);
8734 this.fireEvent("rowupdated", this, index, record);
8737 insertRow : function(dm, rowIndex, isUpdate){
8740 this.fireEvent("beforerowsinserted", this, rowIndex);
8742 //var s = this.getScrollState();
8743 var row = this.renderRow(this.cm, this.store, rowIndex);
8744 // insert before rowIndex..
8745 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8749 if(row.cellObjects.length){
8750 Roo.each(row.cellObjects, function(r){
8751 _this.renderCellObject(r);
8756 this.fireEvent("rowsinserted", this, rowIndex);
8757 //this.syncRowHeights(firstRow, lastRow);
8758 //this.stripeRows(firstRow);
8765 getRowDom : function(rowIndex)
8767 var rows = this.el.select('tbody > tr', true).elements;
8769 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8772 // returns the object tree for a tr..
8775 renderRow : function(cm, ds, rowIndex)
8777 var d = ds.getAt(rowIndex);
8781 cls : 'x-row-' + rowIndex,
8785 var cellObjects = [];
8787 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8788 var config = cm.config[i];
8790 var renderer = cm.getRenderer(i);
8794 if(typeof(renderer) !== 'undefined'){
8795 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8797 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8798 // and are rendered into the cells after the row is rendered - using the id for the element.
8800 if(typeof(value) === 'object'){
8810 rowIndex : rowIndex,
8815 this.fireEvent('rowclass', this, rowcfg);
8819 cls : rowcfg.rowClass + ' x-col-' + i,
8821 html: (typeof(value) === 'object') ? '' : value
8828 if(typeof(config.colspan) != 'undefined'){
8829 td.colspan = config.colspan;
8832 if(typeof(config.hidden) != 'undefined' && config.hidden){
8833 td.style += ' display:none;';
8836 if(typeof(config.align) != 'undefined' && config.align.length){
8837 td.style += ' text-align:' + config.align + ';';
8839 if(typeof(config.valign) != 'undefined' && config.valign.length){
8840 td.style += ' vertical-align:' + config.valign + ';';
8843 if(typeof(config.width) != 'undefined'){
8844 td.style += ' width:' + config.width + 'px;';
8847 if(typeof(config.cursor) != 'undefined'){
8848 td.style += ' cursor:' + config.cursor + ';';
8851 if(typeof(config.cls) != 'undefined'){
8852 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8855 ['xs','sm','md','lg'].map(function(size){
8857 if(typeof(config[size]) == 'undefined'){
8863 if (!config[size]) { // 0 = hidden
8864 // BS 4 '0' is treated as hide that column and below.
8865 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8869 td.cls += ' col-' + size + '-' + config[size] + (
8870 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8880 row.cellObjects = cellObjects;
8888 onBeforeLoad : function()
8897 this.el.select('tbody', true).first().dom.innerHTML = '';
8900 * Show or hide a row.
8901 * @param {Number} rowIndex to show or hide
8902 * @param {Boolean} state hide
8904 setRowVisibility : function(rowIndex, state)
8906 var bt = this.mainBody.dom;
8908 var rows = this.el.select('tbody > tr', true).elements;
8910 if(typeof(rows[rowIndex]) == 'undefined'){
8913 rows[rowIndex].dom.style.display = state ? '' : 'none';
8917 getSelectionModel : function(){
8919 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8921 return this.selModel;
8924 * Render the Roo.bootstrap object from renderder
8926 renderCellObject : function(r)
8930 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8932 var t = r.cfg.render(r.container);
8935 Roo.each(r.cfg.cn, function(c){
8937 container: t.getChildContainer(),
8940 _this.renderCellObject(child);
8945 getRowIndex : function(row)
8949 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8960 * Returns the grid's underlying element = used by panel.Grid
8961 * @return {Element} The element
8963 getGridEl : function(){
8967 * Forces a resize - used by panel.Grid
8968 * @return {Element} The element
8970 autoSize : function()
8972 //var ctr = Roo.get(this.container.dom.parentElement);
8973 var ctr = Roo.get(this.el.dom);
8975 var thd = this.getGridEl().select('thead',true).first();
8976 var tbd = this.getGridEl().select('tbody', true).first();
8977 var tfd = this.getGridEl().select('tfoot', true).first();
8979 var cw = ctr.getWidth();
8980 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8984 tbd.setWidth(ctr.getWidth());
8985 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8986 // this needs fixing for various usage - currently only hydra job advers I think..
8988 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8990 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8993 cw = Math.max(cw, this.totalWidth);
8994 this.getGridEl().select('tbody tr',true).setWidth(cw);
8996 // resize 'expandable coloumn?
8998 return; // we doe not have a view in this design..
9001 onBodyScroll: function()
9003 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9005 this.mainHead.setStyle({
9006 'position' : 'relative',
9007 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9013 var scrollHeight = this.mainBody.dom.scrollHeight;
9015 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9017 var height = this.mainBody.getHeight();
9019 if(scrollHeight - height == scrollTop) {
9021 var total = this.ds.getTotalCount();
9023 if(this.footer.cursor + this.footer.pageSize < total){
9025 this.footer.ds.load({
9027 start : this.footer.cursor + this.footer.pageSize,
9028 limit : this.footer.pageSize
9038 onHeaderChange : function()
9040 var header = this.renderHeader();
9041 var table = this.el.select('table', true).first();
9043 this.mainHead.remove();
9044 this.mainHead = table.createChild(header, this.mainBody, false);
9047 onHiddenChange : function(colModel, colIndex, hidden)
9049 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9050 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9052 this.CSS.updateRule(thSelector, "display", "");
9053 this.CSS.updateRule(tdSelector, "display", "");
9056 this.CSS.updateRule(thSelector, "display", "none");
9057 this.CSS.updateRule(tdSelector, "display", "none");
9060 this.onHeaderChange();
9064 setColumnWidth: function(col_index, width)
9066 // width = "md-2 xs-2..."
9067 if(!this.colModel.config[col_index]) {
9071 var w = width.split(" ");
9073 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9075 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9078 for(var j = 0; j < w.length; j++) {
9084 var size_cls = w[j].split("-");
9086 if(!Number.isInteger(size_cls[1] * 1)) {
9090 if(!this.colModel.config[col_index][size_cls[0]]) {
9094 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9098 h_row[0].classList.replace(
9099 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9100 "col-"+size_cls[0]+"-"+size_cls[1]
9103 for(var i = 0; i < rows.length; i++) {
9105 var size_cls = w[j].split("-");
9107 if(!Number.isInteger(size_cls[1] * 1)) {
9111 if(!this.colModel.config[col_index][size_cls[0]]) {
9115 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9119 rows[i].classList.replace(
9120 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9121 "col-"+size_cls[0]+"-"+size_cls[1]
9125 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9140 * @class Roo.bootstrap.TableCell
9141 * @extends Roo.bootstrap.Component
9142 * Bootstrap TableCell class
9143 * @cfg {String} html cell contain text
9144 * @cfg {String} cls cell class
9145 * @cfg {String} tag cell tag (td|th) default td
9146 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9147 * @cfg {String} align Aligns the content in a cell
9148 * @cfg {String} axis Categorizes cells
9149 * @cfg {String} bgcolor Specifies the background color of a cell
9150 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9151 * @cfg {Number} colspan Specifies the number of columns a cell should span
9152 * @cfg {String} headers Specifies one or more header cells a cell is related to
9153 * @cfg {Number} height Sets the height of a cell
9154 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9155 * @cfg {Number} rowspan Sets the number of rows a cell should span
9156 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9157 * @cfg {String} valign Vertical aligns the content in a cell
9158 * @cfg {Number} width Specifies the width of a cell
9161 * Create a new TableCell
9162 * @param {Object} config The config object
9165 Roo.bootstrap.TableCell = function(config){
9166 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9169 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9189 getAutoCreate : function(){
9190 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9210 cfg.align=this.align
9216 cfg.bgcolor=this.bgcolor
9219 cfg.charoff=this.charoff
9222 cfg.colspan=this.colspan
9225 cfg.headers=this.headers
9228 cfg.height=this.height
9231 cfg.nowrap=this.nowrap
9234 cfg.rowspan=this.rowspan
9237 cfg.scope=this.scope
9240 cfg.valign=this.valign
9243 cfg.width=this.width
9262 * @class Roo.bootstrap.TableRow
9263 * @extends Roo.bootstrap.Component
9264 * Bootstrap TableRow class
9265 * @cfg {String} cls row class
9266 * @cfg {String} align Aligns the content in a table row
9267 * @cfg {String} bgcolor Specifies a background color for a table row
9268 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9269 * @cfg {String} valign Vertical aligns the content in a table row
9272 * Create a new TableRow
9273 * @param {Object} config The config object
9276 Roo.bootstrap.TableRow = function(config){
9277 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9280 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9288 getAutoCreate : function(){
9289 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9299 cfg.align = this.align;
9302 cfg.bgcolor = this.bgcolor;
9305 cfg.charoff = this.charoff;
9308 cfg.valign = this.valign;
9326 * @class Roo.bootstrap.TableBody
9327 * @extends Roo.bootstrap.Component
9328 * Bootstrap TableBody class
9329 * @cfg {String} cls element class
9330 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9331 * @cfg {String} align Aligns the content inside the element
9332 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9333 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9336 * Create a new TableBody
9337 * @param {Object} config The config object
9340 Roo.bootstrap.TableBody = function(config){
9341 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9344 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9352 getAutoCreate : function(){
9353 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9367 cfg.align = this.align;
9370 cfg.charoff = this.charoff;
9373 cfg.valign = this.valign;
9380 // initEvents : function()
9387 // this.store = Roo.factory(this.store, Roo.data);
9388 // this.store.on('load', this.onLoad, this);
9390 // this.store.load();
9394 // onLoad: function ()
9396 // this.fireEvent('load', this);
9406 * Ext JS Library 1.1.1
9407 * Copyright(c) 2006-2007, Ext JS, LLC.
9409 * Originally Released Under LGPL - original licence link has changed is not relivant.
9412 * <script type="text/javascript">
9415 // as we use this in bootstrap.
9416 Roo.namespace('Roo.form');
9418 * @class Roo.form.Action
9419 * Internal Class used to handle form actions
9421 * @param {Roo.form.BasicForm} el The form element or its id
9422 * @param {Object} config Configuration options
9427 // define the action interface
9428 Roo.form.Action = function(form, options){
9430 this.options = options || {};
9433 * Client Validation Failed
9436 Roo.form.Action.CLIENT_INVALID = 'client';
9438 * Server Validation Failed
9441 Roo.form.Action.SERVER_INVALID = 'server';
9443 * Connect to Server Failed
9446 Roo.form.Action.CONNECT_FAILURE = 'connect';
9448 * Reading Data from Server Failed
9451 Roo.form.Action.LOAD_FAILURE = 'load';
9453 Roo.form.Action.prototype = {
9455 failureType : undefined,
9456 response : undefined,
9460 run : function(options){
9465 success : function(response){
9470 handleResponse : function(response){
9474 // default connection failure
9475 failure : function(response){
9477 this.response = response;
9478 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9479 this.form.afterAction(this, false);
9482 processResponse : function(response){
9483 this.response = response;
9484 if(!response.responseText){
9487 this.result = this.handleResponse(response);
9491 // utility functions used internally
9492 getUrl : function(appendParams){
9493 var url = this.options.url || this.form.url || this.form.el.dom.action;
9495 var p = this.getParams();
9497 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9503 getMethod : function(){
9504 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9507 getParams : function(){
9508 var bp = this.form.baseParams;
9509 var p = this.options.params;
9511 if(typeof p == "object"){
9512 p = Roo.urlEncode(Roo.applyIf(p, bp));
9513 }else if(typeof p == 'string' && bp){
9514 p += '&' + Roo.urlEncode(bp);
9517 p = Roo.urlEncode(bp);
9522 createCallback : function(){
9524 success: this.success,
9525 failure: this.failure,
9527 timeout: (this.form.timeout*1000),
9528 upload: this.form.fileUpload ? this.success : undefined
9533 Roo.form.Action.Submit = function(form, options){
9534 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9537 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9540 haveProgress : false,
9541 uploadComplete : false,
9543 // uploadProgress indicator.
9544 uploadProgress : function()
9546 if (!this.form.progressUrl) {
9550 if (!this.haveProgress) {
9551 Roo.MessageBox.progress("Uploading", "Uploading");
9553 if (this.uploadComplete) {
9554 Roo.MessageBox.hide();
9558 this.haveProgress = true;
9560 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9562 var c = new Roo.data.Connection();
9564 url : this.form.progressUrl,
9569 success : function(req){
9570 //console.log(data);
9574 rdata = Roo.decode(req.responseText)
9576 Roo.log("Invalid data from server..");
9580 if (!rdata || !rdata.success) {
9582 Roo.MessageBox.alert(Roo.encode(rdata));
9585 var data = rdata.data;
9587 if (this.uploadComplete) {
9588 Roo.MessageBox.hide();
9593 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9594 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9597 this.uploadProgress.defer(2000,this);
9600 failure: function(data) {
9601 Roo.log('progress url failed ');
9612 // run get Values on the form, so it syncs any secondary forms.
9613 this.form.getValues();
9615 var o = this.options;
9616 var method = this.getMethod();
9617 var isPost = method == 'POST';
9618 if(o.clientValidation === false || this.form.isValid()){
9620 if (this.form.progressUrl) {
9621 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9622 (new Date() * 1) + '' + Math.random());
9627 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9628 form:this.form.el.dom,
9629 url:this.getUrl(!isPost),
9631 params:isPost ? this.getParams() : null,
9632 isUpload: this.form.fileUpload,
9633 formData : this.form.formData
9636 this.uploadProgress();
9638 }else if (o.clientValidation !== false){ // client validation failed
9639 this.failureType = Roo.form.Action.CLIENT_INVALID;
9640 this.form.afterAction(this, false);
9644 success : function(response)
9646 this.uploadComplete= true;
9647 if (this.haveProgress) {
9648 Roo.MessageBox.hide();
9652 var result = this.processResponse(response);
9653 if(result === true || result.success){
9654 this.form.afterAction(this, true);
9658 this.form.markInvalid(result.errors);
9659 this.failureType = Roo.form.Action.SERVER_INVALID;
9661 this.form.afterAction(this, false);
9663 failure : function(response)
9665 this.uploadComplete= true;
9666 if (this.haveProgress) {
9667 Roo.MessageBox.hide();
9670 this.response = response;
9671 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9672 this.form.afterAction(this, false);
9675 handleResponse : function(response){
9676 if(this.form.errorReader){
9677 var rs = this.form.errorReader.read(response);
9680 for(var i = 0, len = rs.records.length; i < len; i++) {
9681 var r = rs.records[i];
9685 if(errors.length < 1){
9689 success : rs.success,
9695 ret = Roo.decode(response.responseText);
9699 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9709 Roo.form.Action.Load = function(form, options){
9710 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9711 this.reader = this.form.reader;
9714 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9719 Roo.Ajax.request(Roo.apply(
9720 this.createCallback(), {
9721 method:this.getMethod(),
9722 url:this.getUrl(false),
9723 params:this.getParams()
9727 success : function(response){
9729 var result = this.processResponse(response);
9730 if(result === true || !result.success || !result.data){
9731 this.failureType = Roo.form.Action.LOAD_FAILURE;
9732 this.form.afterAction(this, false);
9735 this.form.clearInvalid();
9736 this.form.setValues(result.data);
9737 this.form.afterAction(this, true);
9740 handleResponse : function(response){
9741 if(this.form.reader){
9742 var rs = this.form.reader.read(response);
9743 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9745 success : rs.success,
9749 return Roo.decode(response.responseText);
9753 Roo.form.Action.ACTION_TYPES = {
9754 'load' : Roo.form.Action.Load,
9755 'submit' : Roo.form.Action.Submit
9764 * @class Roo.bootstrap.Form
9765 * @extends Roo.bootstrap.Component
9766 * Bootstrap Form class
9767 * @cfg {String} method GET | POST (default POST)
9768 * @cfg {String} labelAlign top | left (default top)
9769 * @cfg {String} align left | right - for navbars
9770 * @cfg {Boolean} loadMask load mask when submit (default true)
9775 * @param {Object} config The config object
9779 Roo.bootstrap.Form = function(config){
9781 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9783 Roo.bootstrap.Form.popover.apply();
9787 * @event clientvalidation
9788 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9789 * @param {Form} this
9790 * @param {Boolean} valid true if the form has passed client-side validation
9792 clientvalidation: true,
9794 * @event beforeaction
9795 * Fires before any action is performed. Return false to cancel the action.
9796 * @param {Form} this
9797 * @param {Action} action The action to be performed
9801 * @event actionfailed
9802 * Fires when an action fails.
9803 * @param {Form} this
9804 * @param {Action} action The action that failed
9806 actionfailed : true,
9808 * @event actioncomplete
9809 * Fires when an action is completed.
9810 * @param {Form} this
9811 * @param {Action} action The action that completed
9813 actioncomplete : true
9817 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9820 * @cfg {String} method
9821 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9826 * The URL to use for form actions if one isn't supplied in the action options.
9829 * @cfg {Boolean} fileUpload
9830 * Set to true if this form is a file upload.
9834 * @cfg {Object} baseParams
9835 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9839 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9843 * @cfg {Sting} align (left|right) for navbar forms
9848 activeAction : null,
9851 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9852 * element by passing it or its id or mask the form itself by passing in true.
9855 waitMsgTarget : false,
9860 * @cfg {Boolean} errorMask (true|false) default false
9865 * @cfg {Number} maskOffset Default 100
9870 * @cfg {Boolean} maskBody
9874 getAutoCreate : function(){
9878 method : this.method || 'POST',
9879 id : this.id || Roo.id(),
9882 if (this.parent().xtype.match(/^Nav/)) {
9883 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9887 if (this.labelAlign == 'left' ) {
9888 cfg.cls += ' form-horizontal';
9894 initEvents : function()
9896 this.el.on('submit', this.onSubmit, this);
9897 // this was added as random key presses on the form where triggering form submit.
9898 this.el.on('keypress', function(e) {
9899 if (e.getCharCode() != 13) {
9902 // we might need to allow it for textareas.. and some other items.
9903 // check e.getTarget().
9905 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9909 Roo.log("keypress blocked");
9917 onSubmit : function(e){
9922 * Returns true if client-side validation on the form is successful.
9925 isValid : function(){
9926 var items = this.getItems();
9930 items.each(function(f){
9936 Roo.log('invalid field: ' + f.name);
9940 if(!target && f.el.isVisible(true)){
9946 if(this.errorMask && !valid){
9947 Roo.bootstrap.Form.popover.mask(this, target);
9954 * Returns true if any fields in this form have changed since their original load.
9957 isDirty : function(){
9959 var items = this.getItems();
9960 items.each(function(f){
9970 * Performs a predefined action (submit or load) or custom actions you define on this form.
9971 * @param {String} actionName The name of the action type
9972 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9973 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9974 * accept other config options):
9976 Property Type Description
9977 ---------------- --------------- ----------------------------------------------------------------------------------
9978 url String The url for the action (defaults to the form's url)
9979 method String The form method to use (defaults to the form's method, or POST if not defined)
9980 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9981 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9982 validate the form on the client (defaults to false)
9984 * @return {BasicForm} this
9986 doAction : function(action, options){
9987 if(typeof action == 'string'){
9988 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9990 if(this.fireEvent('beforeaction', this, action) !== false){
9991 this.beforeAction(action);
9992 action.run.defer(100, action);
9998 beforeAction : function(action){
9999 var o = action.options;
10004 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10006 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10009 // not really supported yet.. ??
10011 //if(this.waitMsgTarget === true){
10012 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10013 //}else if(this.waitMsgTarget){
10014 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10015 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10017 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10023 afterAction : function(action, success){
10024 this.activeAction = null;
10025 var o = action.options;
10030 Roo.get(document.body).unmask();
10036 //if(this.waitMsgTarget === true){
10037 // this.el.unmask();
10038 //}else if(this.waitMsgTarget){
10039 // this.waitMsgTarget.unmask();
10041 // Roo.MessageBox.updateProgress(1);
10042 // Roo.MessageBox.hide();
10049 Roo.callback(o.success, o.scope, [this, action]);
10050 this.fireEvent('actioncomplete', this, action);
10054 // failure condition..
10055 // we have a scenario where updates need confirming.
10056 // eg. if a locking scenario exists..
10057 // we look for { errors : { needs_confirm : true }} in the response.
10059 (typeof(action.result) != 'undefined') &&
10060 (typeof(action.result.errors) != 'undefined') &&
10061 (typeof(action.result.errors.needs_confirm) != 'undefined')
10064 Roo.log("not supported yet");
10067 Roo.MessageBox.confirm(
10068 "Change requires confirmation",
10069 action.result.errorMsg,
10074 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10084 Roo.callback(o.failure, o.scope, [this, action]);
10085 // show an error message if no failed handler is set..
10086 if (!this.hasListener('actionfailed')) {
10087 Roo.log("need to add dialog support");
10089 Roo.MessageBox.alert("Error",
10090 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10091 action.result.errorMsg :
10092 "Saving Failed, please check your entries or try again"
10097 this.fireEvent('actionfailed', this, action);
10102 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10103 * @param {String} id The value to search for
10106 findField : function(id){
10107 var items = this.getItems();
10108 var field = items.get(id);
10110 items.each(function(f){
10111 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10118 return field || null;
10121 * Mark fields in this form invalid in bulk.
10122 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10123 * @return {BasicForm} this
10125 markInvalid : function(errors){
10126 if(errors instanceof Array){
10127 for(var i = 0, len = errors.length; i < len; i++){
10128 var fieldError = errors[i];
10129 var f = this.findField(fieldError.id);
10131 f.markInvalid(fieldError.msg);
10137 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10138 field.markInvalid(errors[id]);
10142 //Roo.each(this.childForms || [], function (f) {
10143 // f.markInvalid(errors);
10150 * Set values for fields in this form in bulk.
10151 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10152 * @return {BasicForm} this
10154 setValues : function(values){
10155 if(values instanceof Array){ // array of objects
10156 for(var i = 0, len = values.length; i < len; i++){
10158 var f = this.findField(v.id);
10160 f.setValue(v.value);
10161 if(this.trackResetOnLoad){
10162 f.originalValue = f.getValue();
10166 }else{ // object hash
10169 if(typeof values[id] != 'function' && (field = this.findField(id))){
10171 if (field.setFromData &&
10172 field.valueField &&
10173 field.displayField &&
10174 // combos' with local stores can
10175 // be queried via setValue()
10176 // to set their value..
10177 (field.store && !field.store.isLocal)
10181 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10182 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10183 field.setFromData(sd);
10185 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10187 field.setFromData(values);
10190 field.setValue(values[id]);
10194 if(this.trackResetOnLoad){
10195 field.originalValue = field.getValue();
10201 //Roo.each(this.childForms || [], function (f) {
10202 // f.setValues(values);
10209 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10210 * they are returned as an array.
10211 * @param {Boolean} asString
10214 getValues : function(asString){
10215 //if (this.childForms) {
10216 // copy values from the child forms
10217 // Roo.each(this.childForms, function (f) {
10218 // this.setValues(f.getValues());
10224 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10225 if(asString === true){
10228 return Roo.urlDecode(fs);
10232 * Returns the fields in this form as an object with key/value pairs.
10233 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10236 getFieldValues : function(with_hidden)
10238 var items = this.getItems();
10240 items.each(function(f){
10242 if (!f.getName()) {
10246 var v = f.getValue();
10248 if (f.inputType =='radio') {
10249 if (typeof(ret[f.getName()]) == 'undefined') {
10250 ret[f.getName()] = ''; // empty..
10253 if (!f.el.dom.checked) {
10257 v = f.el.dom.value;
10261 if(f.xtype == 'MoneyField'){
10262 ret[f.currencyName] = f.getCurrency();
10265 // not sure if this supported any more..
10266 if ((typeof(v) == 'object') && f.getRawValue) {
10267 v = f.getRawValue() ; // dates..
10269 // combo boxes where name != hiddenName...
10270 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10271 ret[f.name] = f.getRawValue();
10273 ret[f.getName()] = v;
10280 * Clears all invalid messages in this form.
10281 * @return {BasicForm} this
10283 clearInvalid : function(){
10284 var items = this.getItems();
10286 items.each(function(f){
10294 * Resets this form.
10295 * @return {BasicForm} this
10297 reset : function(){
10298 var items = this.getItems();
10299 items.each(function(f){
10303 Roo.each(this.childForms || [], function (f) {
10311 getItems : function()
10313 var r=new Roo.util.MixedCollection(false, function(o){
10314 return o.id || (o.id = Roo.id());
10316 var iter = function(el) {
10323 Roo.each(el.items,function(e) {
10332 hideFields : function(items)
10334 Roo.each(items, function(i){
10336 var f = this.findField(i);
10347 showFields : function(items)
10349 Roo.each(items, function(i){
10351 var f = this.findField(i);
10364 Roo.apply(Roo.bootstrap.Form, {
10380 intervalID : false,
10386 if(this.isApplied){
10391 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10392 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10393 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10394 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10397 this.maskEl.top.enableDisplayMode("block");
10398 this.maskEl.left.enableDisplayMode("block");
10399 this.maskEl.bottom.enableDisplayMode("block");
10400 this.maskEl.right.enableDisplayMode("block");
10402 this.toolTip = new Roo.bootstrap.Tooltip({
10403 cls : 'roo-form-error-popover',
10405 'left' : ['r-l', [-2,0], 'right'],
10406 'right' : ['l-r', [2,0], 'left'],
10407 'bottom' : ['tl-bl', [0,2], 'top'],
10408 'top' : [ 'bl-tl', [0,-2], 'bottom']
10412 this.toolTip.render(Roo.get(document.body));
10414 this.toolTip.el.enableDisplayMode("block");
10416 Roo.get(document.body).on('click', function(){
10420 Roo.get(document.body).on('touchstart', function(){
10424 this.isApplied = true
10427 mask : function(form, target)
10431 this.target = target;
10433 if(!this.form.errorMask || !target.el){
10437 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10439 Roo.log(scrollable);
10441 var ot = this.target.el.calcOffsetsTo(scrollable);
10443 var scrollTo = ot[1] - this.form.maskOffset;
10445 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10447 scrollable.scrollTo('top', scrollTo);
10449 var box = this.target.el.getBox();
10451 var zIndex = Roo.bootstrap.Modal.zIndex++;
10454 this.maskEl.top.setStyle('position', 'absolute');
10455 this.maskEl.top.setStyle('z-index', zIndex);
10456 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10457 this.maskEl.top.setLeft(0);
10458 this.maskEl.top.setTop(0);
10459 this.maskEl.top.show();
10461 this.maskEl.left.setStyle('position', 'absolute');
10462 this.maskEl.left.setStyle('z-index', zIndex);
10463 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10464 this.maskEl.left.setLeft(0);
10465 this.maskEl.left.setTop(box.y - this.padding);
10466 this.maskEl.left.show();
10468 this.maskEl.bottom.setStyle('position', 'absolute');
10469 this.maskEl.bottom.setStyle('z-index', zIndex);
10470 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10471 this.maskEl.bottom.setLeft(0);
10472 this.maskEl.bottom.setTop(box.bottom + this.padding);
10473 this.maskEl.bottom.show();
10475 this.maskEl.right.setStyle('position', 'absolute');
10476 this.maskEl.right.setStyle('z-index', zIndex);
10477 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10478 this.maskEl.right.setLeft(box.right + this.padding);
10479 this.maskEl.right.setTop(box.y - this.padding);
10480 this.maskEl.right.show();
10482 this.toolTip.bindEl = this.target.el;
10484 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10486 var tip = this.target.blankText;
10488 if(this.target.getValue() !== '' ) {
10490 if (this.target.invalidText.length) {
10491 tip = this.target.invalidText;
10492 } else if (this.target.regexText.length){
10493 tip = this.target.regexText;
10497 this.toolTip.show(tip);
10499 this.intervalID = window.setInterval(function() {
10500 Roo.bootstrap.Form.popover.unmask();
10503 window.onwheel = function(){ return false;};
10505 (function(){ this.isMasked = true; }).defer(500, this);
10509 unmask : function()
10511 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10515 this.maskEl.top.setStyle('position', 'absolute');
10516 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10517 this.maskEl.top.hide();
10519 this.maskEl.left.setStyle('position', 'absolute');
10520 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10521 this.maskEl.left.hide();
10523 this.maskEl.bottom.setStyle('position', 'absolute');
10524 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10525 this.maskEl.bottom.hide();
10527 this.maskEl.right.setStyle('position', 'absolute');
10528 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10529 this.maskEl.right.hide();
10531 this.toolTip.hide();
10533 this.toolTip.el.hide();
10535 window.onwheel = function(){ return true;};
10537 if(this.intervalID){
10538 window.clearInterval(this.intervalID);
10539 this.intervalID = false;
10542 this.isMasked = false;
10552 * Ext JS Library 1.1.1
10553 * Copyright(c) 2006-2007, Ext JS, LLC.
10555 * Originally Released Under LGPL - original licence link has changed is not relivant.
10558 * <script type="text/javascript">
10561 * @class Roo.form.VTypes
10562 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10565 Roo.form.VTypes = function(){
10566 // closure these in so they are only created once.
10567 var alpha = /^[a-zA-Z_]+$/;
10568 var alphanum = /^[a-zA-Z0-9_]+$/;
10569 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10570 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10572 // All these messages and functions are configurable
10575 * The function used to validate email addresses
10576 * @param {String} value The email address
10578 'email' : function(v){
10579 return email.test(v);
10582 * The error text to display when the email validation function returns false
10585 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10587 * The keystroke filter mask to be applied on email input
10590 'emailMask' : /[a-z0-9_\.\-@]/i,
10593 * The function used to validate URLs
10594 * @param {String} value The URL
10596 'url' : function(v){
10597 return url.test(v);
10600 * The error text to display when the url validation function returns false
10603 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10606 * The function used to validate alpha values
10607 * @param {String} value The value
10609 'alpha' : function(v){
10610 return alpha.test(v);
10613 * The error text to display when the alpha validation function returns false
10616 'alphaText' : 'This field should only contain letters and _',
10618 * The keystroke filter mask to be applied on alpha input
10621 'alphaMask' : /[a-z_]/i,
10624 * The function used to validate alphanumeric values
10625 * @param {String} value The value
10627 'alphanum' : function(v){
10628 return alphanum.test(v);
10631 * The error text to display when the alphanumeric validation function returns false
10634 'alphanumText' : 'This field should only contain letters, numbers and _',
10636 * The keystroke filter mask to be applied on alphanumeric input
10639 'alphanumMask' : /[a-z0-9_]/i
10649 * @class Roo.bootstrap.Input
10650 * @extends Roo.bootstrap.Component
10651 * Bootstrap Input class
10652 * @cfg {Boolean} disabled is it disabled
10653 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10654 * @cfg {String} name name of the input
10655 * @cfg {string} fieldLabel - the label associated
10656 * @cfg {string} placeholder - placeholder to put in text.
10657 * @cfg {string} before - input group add on before
10658 * @cfg {string} after - input group add on after
10659 * @cfg {string} size - (lg|sm) or leave empty..
10660 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10661 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10662 * @cfg {Number} md colspan out of 12 for computer-sized screens
10663 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10664 * @cfg {string} value default value of the input
10665 * @cfg {Number} labelWidth set the width of label
10666 * @cfg {Number} labellg set the width of label (1-12)
10667 * @cfg {Number} labelmd set the width of label (1-12)
10668 * @cfg {Number} labelsm set the width of label (1-12)
10669 * @cfg {Number} labelxs set the width of label (1-12)
10670 * @cfg {String} labelAlign (top|left)
10671 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10672 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10673 * @cfg {String} indicatorpos (left|right) default left
10674 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10675 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10676 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10678 * @cfg {String} align (left|center|right) Default left
10679 * @cfg {Boolean} forceFeedback (true|false) Default false
10682 * Create a new Input
10683 * @param {Object} config The config object
10686 Roo.bootstrap.Input = function(config){
10688 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10693 * Fires when this field receives input focus.
10694 * @param {Roo.form.Field} this
10699 * Fires when this field loses input focus.
10700 * @param {Roo.form.Field} this
10704 * @event specialkey
10705 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10706 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10707 * @param {Roo.form.Field} this
10708 * @param {Roo.EventObject} e The event object
10713 * Fires just before the field blurs if the field value has changed.
10714 * @param {Roo.form.Field} this
10715 * @param {Mixed} newValue The new value
10716 * @param {Mixed} oldValue The original value
10721 * Fires after the field has been marked as invalid.
10722 * @param {Roo.form.Field} this
10723 * @param {String} msg The validation message
10728 * Fires after the field has been validated with no errors.
10729 * @param {Roo.form.Field} this
10734 * Fires after the key up
10735 * @param {Roo.form.Field} this
10736 * @param {Roo.EventObject} e The event Object
10741 * Fires after the user pastes into input
10742 * @param {Roo.form.Field} this
10743 * @param {Roo.EventObject} e The event Object
10749 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10751 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10752 automatic validation (defaults to "keyup").
10754 validationEvent : "keyup",
10756 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10758 validateOnBlur : true,
10760 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10762 validationDelay : 250,
10764 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10766 focusClass : "x-form-focus", // not needed???
10770 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10772 invalidClass : "has-warning",
10775 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10777 validClass : "has-success",
10780 * @cfg {Boolean} hasFeedback (true|false) default true
10782 hasFeedback : true,
10785 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10787 invalidFeedbackClass : "glyphicon-warning-sign",
10790 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10792 validFeedbackClass : "glyphicon-ok",
10795 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10797 selectOnFocus : false,
10800 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10804 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10809 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10811 disableKeyFilter : false,
10814 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10818 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10822 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10824 blankText : "Please complete this mandatory field",
10827 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10831 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10833 maxLength : Number.MAX_VALUE,
10835 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10837 minLengthText : "The minimum length for this field is {0}",
10839 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10841 maxLengthText : "The maximum length for this field is {0}",
10845 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10846 * If available, this function will be called only after the basic validators all return true, and will be passed the
10847 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10851 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10852 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10853 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10857 * @cfg {String} regexText -- Depricated - use Invalid Text
10862 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10868 autocomplete: false,
10872 inputType : 'text',
10875 placeholder: false,
10880 preventMark: false,
10881 isFormField : true,
10884 labelAlign : false,
10887 formatedValue : false,
10888 forceFeedback : false,
10890 indicatorpos : 'left',
10900 parentLabelAlign : function()
10903 while (parent.parent()) {
10904 parent = parent.parent();
10905 if (typeof(parent.labelAlign) !='undefined') {
10906 return parent.labelAlign;
10913 getAutoCreate : function()
10915 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10921 if(this.inputType != 'hidden'){
10922 cfg.cls = 'form-group' //input-group
10928 type : this.inputType,
10929 value : this.value,
10930 cls : 'form-control',
10931 placeholder : this.placeholder || '',
10932 autocomplete : this.autocomplete || 'new-password'
10934 if (this.inputType == 'file') {
10935 input.style = 'overflow:hidden'; // why not in CSS?
10938 if(this.capture.length){
10939 input.capture = this.capture;
10942 if(this.accept.length){
10943 input.accept = this.accept + "/*";
10947 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10950 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10951 input.maxLength = this.maxLength;
10954 if (this.disabled) {
10955 input.disabled=true;
10958 if (this.readOnly) {
10959 input.readonly=true;
10963 input.name = this.name;
10967 input.cls += ' input-' + this.size;
10971 ['xs','sm','md','lg'].map(function(size){
10972 if (settings[size]) {
10973 cfg.cls += ' col-' + size + '-' + settings[size];
10977 var inputblock = input;
10981 cls: 'glyphicon form-control-feedback'
10984 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10987 cls : 'has-feedback',
10995 if (this.before || this.after) {
10998 cls : 'input-group',
11002 if (this.before && typeof(this.before) == 'string') {
11004 inputblock.cn.push({
11006 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11010 if (this.before && typeof(this.before) == 'object') {
11011 this.before = Roo.factory(this.before);
11013 inputblock.cn.push({
11015 cls : 'roo-input-before input-group-prepend input-group-' +
11016 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11020 inputblock.cn.push(input);
11022 if (this.after && typeof(this.after) == 'string') {
11023 inputblock.cn.push({
11025 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11029 if (this.after && typeof(this.after) == 'object') {
11030 this.after = Roo.factory(this.after);
11032 inputblock.cn.push({
11034 cls : 'roo-input-after input-group-append input-group-' +
11035 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11039 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11040 inputblock.cls += ' has-feedback';
11041 inputblock.cn.push(feedback);
11046 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11047 tooltip : 'This field is required'
11049 if (this.allowBlank ) {
11050 indicator.style = this.allowBlank ? ' display:none' : '';
11052 if (align ==='left' && this.fieldLabel.length) {
11054 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11061 cls : 'control-label col-form-label',
11062 html : this.fieldLabel
11073 var labelCfg = cfg.cn[1];
11074 var contentCfg = cfg.cn[2];
11076 if(this.indicatorpos == 'right'){
11081 cls : 'control-label col-form-label',
11085 html : this.fieldLabel
11099 labelCfg = cfg.cn[0];
11100 contentCfg = cfg.cn[1];
11104 if(this.labelWidth > 12){
11105 labelCfg.style = "width: " + this.labelWidth + 'px';
11108 if(this.labelWidth < 13 && this.labelmd == 0){
11109 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11112 if(this.labellg > 0){
11113 labelCfg.cls += ' col-lg-' + this.labellg;
11114 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11117 if(this.labelmd > 0){
11118 labelCfg.cls += ' col-md-' + this.labelmd;
11119 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11122 if(this.labelsm > 0){
11123 labelCfg.cls += ' col-sm-' + this.labelsm;
11124 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11127 if(this.labelxs > 0){
11128 labelCfg.cls += ' col-xs-' + this.labelxs;
11129 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11133 } else if ( this.fieldLabel.length) {
11140 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11141 tooltip : 'This field is required',
11142 style : this.allowBlank ? ' display:none' : ''
11146 //cls : 'input-group-addon',
11147 html : this.fieldLabel
11155 if(this.indicatorpos == 'right'){
11160 //cls : 'input-group-addon',
11161 html : this.fieldLabel
11166 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11167 tooltip : 'This field is required',
11168 style : this.allowBlank ? ' display:none' : ''
11188 if (this.parentType === 'Navbar' && this.parent().bar) {
11189 cfg.cls += ' navbar-form';
11192 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11193 // on BS4 we do this only if not form
11194 cfg.cls += ' navbar-form';
11202 * return the real input element.
11204 inputEl: function ()
11206 return this.el.select('input.form-control',true).first();
11209 tooltipEl : function()
11211 return this.inputEl();
11214 indicatorEl : function()
11216 if (Roo.bootstrap.version == 4) {
11217 return false; // not enabled in v4 yet.
11220 var indicator = this.el.select('i.roo-required-indicator',true).first();
11230 setDisabled : function(v)
11232 var i = this.inputEl().dom;
11234 i.removeAttribute('disabled');
11238 i.setAttribute('disabled','true');
11240 initEvents : function()
11243 this.inputEl().on("keydown" , this.fireKey, this);
11244 this.inputEl().on("focus", this.onFocus, this);
11245 this.inputEl().on("blur", this.onBlur, this);
11247 this.inputEl().relayEvent('keyup', this);
11248 this.inputEl().relayEvent('paste', this);
11250 this.indicator = this.indicatorEl();
11252 if(this.indicator){
11253 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11256 // reference to original value for reset
11257 this.originalValue = this.getValue();
11258 //Roo.form.TextField.superclass.initEvents.call(this);
11259 if(this.validationEvent == 'keyup'){
11260 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11261 this.inputEl().on('keyup', this.filterValidation, this);
11263 else if(this.validationEvent !== false){
11264 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11267 if(this.selectOnFocus){
11268 this.on("focus", this.preFocus, this);
11271 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11272 this.inputEl().on("keypress", this.filterKeys, this);
11274 this.inputEl().relayEvent('keypress', this);
11277 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11278 this.el.on("click", this.autoSize, this);
11281 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11282 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11285 if (typeof(this.before) == 'object') {
11286 this.before.render(this.el.select('.roo-input-before',true).first());
11288 if (typeof(this.after) == 'object') {
11289 this.after.render(this.el.select('.roo-input-after',true).first());
11292 this.inputEl().on('change', this.onChange, this);
11295 filterValidation : function(e){
11296 if(!e.isNavKeyPress()){
11297 this.validationTask.delay(this.validationDelay);
11301 * Validates the field value
11302 * @return {Boolean} True if the value is valid, else false
11304 validate : function(){
11305 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11306 if(this.disabled || this.validateValue(this.getRawValue())){
11311 this.markInvalid();
11317 * Validates a value according to the field's validation rules and marks the field as invalid
11318 * if the validation fails
11319 * @param {Mixed} value The value to validate
11320 * @return {Boolean} True if the value is valid, else false
11322 validateValue : function(value)
11324 if(this.getVisibilityEl().hasClass('hidden')){
11328 if(value.length < 1) { // if it's blank
11329 if(this.allowBlank){
11335 if(value.length < this.minLength){
11338 if(value.length > this.maxLength){
11342 var vt = Roo.form.VTypes;
11343 if(!vt[this.vtype](value, this)){
11347 if(typeof this.validator == "function"){
11348 var msg = this.validator(value);
11352 if (typeof(msg) == 'string') {
11353 this.invalidText = msg;
11357 if(this.regex && !this.regex.test(value)){
11365 fireKey : function(e){
11366 //Roo.log('field ' + e.getKey());
11367 if(e.isNavKeyPress()){
11368 this.fireEvent("specialkey", this, e);
11371 focus : function (selectText){
11373 this.inputEl().focus();
11374 if(selectText === true){
11375 this.inputEl().dom.select();
11381 onFocus : function(){
11382 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11383 // this.el.addClass(this.focusClass);
11385 if(!this.hasFocus){
11386 this.hasFocus = true;
11387 this.startValue = this.getValue();
11388 this.fireEvent("focus", this);
11392 beforeBlur : Roo.emptyFn,
11396 onBlur : function(){
11398 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11399 //this.el.removeClass(this.focusClass);
11401 this.hasFocus = false;
11402 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11405 var v = this.getValue();
11406 if(String(v) !== String(this.startValue)){
11407 this.fireEvent('change', this, v, this.startValue);
11409 this.fireEvent("blur", this);
11412 onChange : function(e)
11414 var v = this.getValue();
11415 if(String(v) !== String(this.startValue)){
11416 this.fireEvent('change', this, v, this.startValue);
11422 * Resets the current field value to the originally loaded value and clears any validation messages
11424 reset : function(){
11425 this.setValue(this.originalValue);
11429 * Returns the name of the field
11430 * @return {Mixed} name The name field
11432 getName: function(){
11436 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11437 * @return {Mixed} value The field value
11439 getValue : function(){
11441 var v = this.inputEl().getValue();
11446 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11447 * @return {Mixed} value The field value
11449 getRawValue : function(){
11450 var v = this.inputEl().getValue();
11456 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11457 * @param {Mixed} value The value to set
11459 setRawValue : function(v){
11460 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11463 selectText : function(start, end){
11464 var v = this.getRawValue();
11466 start = start === undefined ? 0 : start;
11467 end = end === undefined ? v.length : end;
11468 var d = this.inputEl().dom;
11469 if(d.setSelectionRange){
11470 d.setSelectionRange(start, end);
11471 }else if(d.createTextRange){
11472 var range = d.createTextRange();
11473 range.moveStart("character", start);
11474 range.moveEnd("character", v.length-end);
11481 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11482 * @param {Mixed} value The value to set
11484 setValue : function(v){
11487 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11493 processValue : function(value){
11494 if(this.stripCharsRe){
11495 var newValue = value.replace(this.stripCharsRe, '');
11496 if(newValue !== value){
11497 this.setRawValue(newValue);
11504 preFocus : function(){
11506 if(this.selectOnFocus){
11507 this.inputEl().dom.select();
11510 filterKeys : function(e){
11511 var k = e.getKey();
11512 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11515 var c = e.getCharCode(), cc = String.fromCharCode(c);
11516 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11519 if(!this.maskRe.test(cc)){
11524 * Clear any invalid styles/messages for this field
11526 clearInvalid : function(){
11528 if(!this.el || this.preventMark){ // not rendered
11533 this.el.removeClass([this.invalidClass, 'is-invalid']);
11535 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11537 var feedback = this.el.select('.form-control-feedback', true).first();
11540 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11545 if(this.indicator){
11546 this.indicator.removeClass('visible');
11547 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11550 this.fireEvent('valid', this);
11554 * Mark this field as valid
11556 markValid : function()
11558 if(!this.el || this.preventMark){ // not rendered...
11562 this.el.removeClass([this.invalidClass, this.validClass]);
11563 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11565 var feedback = this.el.select('.form-control-feedback', true).first();
11568 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11571 if(this.indicator){
11572 this.indicator.removeClass('visible');
11573 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11581 if(this.allowBlank && !this.getRawValue().length){
11584 if (Roo.bootstrap.version == 3) {
11585 this.el.addClass(this.validClass);
11587 this.inputEl().addClass('is-valid');
11590 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11592 var feedback = this.el.select('.form-control-feedback', true).first();
11595 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11596 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11601 this.fireEvent('valid', this);
11605 * Mark this field as invalid
11606 * @param {String} msg The validation message
11608 markInvalid : function(msg)
11610 if(!this.el || this.preventMark){ // not rendered
11614 this.el.removeClass([this.invalidClass, this.validClass]);
11615 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11617 var feedback = this.el.select('.form-control-feedback', true).first();
11620 this.el.select('.form-control-feedback', true).first().removeClass(
11621 [this.invalidFeedbackClass, this.validFeedbackClass]);
11628 if(this.allowBlank && !this.getRawValue().length){
11632 if(this.indicator){
11633 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11634 this.indicator.addClass('visible');
11636 if (Roo.bootstrap.version == 3) {
11637 this.el.addClass(this.invalidClass);
11639 this.inputEl().addClass('is-invalid');
11644 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11646 var feedback = this.el.select('.form-control-feedback', true).first();
11649 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11651 if(this.getValue().length || this.forceFeedback){
11652 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11659 this.fireEvent('invalid', this, msg);
11662 SafariOnKeyDown : function(event)
11664 // this is a workaround for a password hang bug on chrome/ webkit.
11665 if (this.inputEl().dom.type != 'password') {
11669 var isSelectAll = false;
11671 if(this.inputEl().dom.selectionEnd > 0){
11672 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11674 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11675 event.preventDefault();
11680 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11682 event.preventDefault();
11683 // this is very hacky as keydown always get's upper case.
11685 var cc = String.fromCharCode(event.getCharCode());
11686 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11690 adjustWidth : function(tag, w){
11691 tag = tag.toLowerCase();
11692 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11693 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11694 if(tag == 'input'){
11697 if(tag == 'textarea'){
11700 }else if(Roo.isOpera){
11701 if(tag == 'input'){
11704 if(tag == 'textarea'){
11712 setFieldLabel : function(v)
11714 if(!this.rendered){
11718 if(this.indicatorEl()){
11719 var ar = this.el.select('label > span',true);
11721 if (ar.elements.length) {
11722 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11723 this.fieldLabel = v;
11727 var br = this.el.select('label',true);
11729 if(br.elements.length) {
11730 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11731 this.fieldLabel = v;
11735 Roo.log('Cannot Found any of label > span || label in input');
11739 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11740 this.fieldLabel = v;
11755 * @class Roo.bootstrap.TextArea
11756 * @extends Roo.bootstrap.Input
11757 * Bootstrap TextArea class
11758 * @cfg {Number} cols Specifies the visible width of a text area
11759 * @cfg {Number} rows Specifies the visible number of lines in a text area
11760 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11761 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11762 * @cfg {string} html text
11765 * Create a new TextArea
11766 * @param {Object} config The config object
11769 Roo.bootstrap.TextArea = function(config){
11770 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11774 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11784 getAutoCreate : function(){
11786 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11792 if(this.inputType != 'hidden'){
11793 cfg.cls = 'form-group' //input-group
11801 value : this.value || '',
11802 html: this.html || '',
11803 cls : 'form-control',
11804 placeholder : this.placeholder || ''
11808 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11809 input.maxLength = this.maxLength;
11813 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11817 input.cols = this.cols;
11820 if (this.readOnly) {
11821 input.readonly = true;
11825 input.name = this.name;
11829 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11833 ['xs','sm','md','lg'].map(function(size){
11834 if (settings[size]) {
11835 cfg.cls += ' col-' + size + '-' + settings[size];
11839 var inputblock = input;
11841 if(this.hasFeedback && !this.allowBlank){
11845 cls: 'glyphicon form-control-feedback'
11849 cls : 'has-feedback',
11858 if (this.before || this.after) {
11861 cls : 'input-group',
11865 inputblock.cn.push({
11867 cls : 'input-group-addon',
11872 inputblock.cn.push(input);
11874 if(this.hasFeedback && !this.allowBlank){
11875 inputblock.cls += ' has-feedback';
11876 inputblock.cn.push(feedback);
11880 inputblock.cn.push({
11882 cls : 'input-group-addon',
11889 if (align ==='left' && this.fieldLabel.length) {
11894 cls : 'control-label',
11895 html : this.fieldLabel
11906 if(this.labelWidth > 12){
11907 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11910 if(this.labelWidth < 13 && this.labelmd == 0){
11911 this.labelmd = this.labelWidth;
11914 if(this.labellg > 0){
11915 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11916 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11919 if(this.labelmd > 0){
11920 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11921 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11924 if(this.labelsm > 0){
11925 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11926 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11929 if(this.labelxs > 0){
11930 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11931 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11934 } else if ( this.fieldLabel.length) {
11939 //cls : 'input-group-addon',
11940 html : this.fieldLabel
11958 if (this.disabled) {
11959 input.disabled=true;
11966 * return the real textarea element.
11968 inputEl: function ()
11970 return this.el.select('textarea.form-control',true).first();
11974 * Clear any invalid styles/messages for this field
11976 clearInvalid : function()
11979 if(!this.el || this.preventMark){ // not rendered
11983 var label = this.el.select('label', true).first();
11984 var icon = this.el.select('i.fa-star', true).first();
11989 this.el.removeClass( this.validClass);
11990 this.inputEl().removeClass('is-invalid');
11992 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11994 var feedback = this.el.select('.form-control-feedback', true).first();
11997 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12002 this.fireEvent('valid', this);
12006 * Mark this field as valid
12008 markValid : function()
12010 if(!this.el || this.preventMark){ // not rendered
12014 this.el.removeClass([this.invalidClass, this.validClass]);
12015 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12017 var feedback = this.el.select('.form-control-feedback', true).first();
12020 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12023 if(this.disabled || this.allowBlank){
12027 var label = this.el.select('label', true).first();
12028 var icon = this.el.select('i.fa-star', true).first();
12033 if (Roo.bootstrap.version == 3) {
12034 this.el.addClass(this.validClass);
12036 this.inputEl().addClass('is-valid');
12040 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12042 var feedback = this.el.select('.form-control-feedback', true).first();
12045 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12046 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12051 this.fireEvent('valid', this);
12055 * Mark this field as invalid
12056 * @param {String} msg The validation message
12058 markInvalid : function(msg)
12060 if(!this.el || this.preventMark){ // not rendered
12064 this.el.removeClass([this.invalidClass, this.validClass]);
12065 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12067 var feedback = this.el.select('.form-control-feedback', true).first();
12070 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12073 if(this.disabled || this.allowBlank){
12077 var label = this.el.select('label', true).first();
12078 var icon = this.el.select('i.fa-star', true).first();
12080 if(!this.getValue().length && label && !icon){
12081 this.el.createChild({
12083 cls : 'text-danger fa fa-lg fa-star',
12084 tooltip : 'This field is required',
12085 style : 'margin-right:5px;'
12089 if (Roo.bootstrap.version == 3) {
12090 this.el.addClass(this.invalidClass);
12092 this.inputEl().addClass('is-invalid');
12095 // fixme ... this may be depricated need to test..
12096 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12098 var feedback = this.el.select('.form-control-feedback', true).first();
12101 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12103 if(this.getValue().length || this.forceFeedback){
12104 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12111 this.fireEvent('invalid', this, msg);
12119 * trigger field - base class for combo..
12124 * @class Roo.bootstrap.TriggerField
12125 * @extends Roo.bootstrap.Input
12126 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12127 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12128 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12129 * for which you can provide a custom implementation. For example:
12131 var trigger = new Roo.bootstrap.TriggerField();
12132 trigger.onTriggerClick = myTriggerFn;
12133 trigger.applyTo('my-field');
12136 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12137 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12138 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12139 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12140 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12143 * Create a new TriggerField.
12144 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12145 * to the base TextField)
12147 Roo.bootstrap.TriggerField = function(config){
12148 this.mimicing = false;
12149 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12152 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12154 * @cfg {String} triggerClass A CSS class to apply to the trigger
12157 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12162 * @cfg {Boolean} removable (true|false) special filter default false
12166 /** @cfg {Boolean} grow @hide */
12167 /** @cfg {Number} growMin @hide */
12168 /** @cfg {Number} growMax @hide */
12174 autoSize: Roo.emptyFn,
12178 deferHeight : true,
12181 actionMode : 'wrap',
12186 getAutoCreate : function(){
12188 var align = this.labelAlign || this.parentLabelAlign();
12193 cls: 'form-group' //input-group
12200 type : this.inputType,
12201 cls : 'form-control',
12202 autocomplete: 'new-password',
12203 placeholder : this.placeholder || ''
12207 input.name = this.name;
12210 input.cls += ' input-' + this.size;
12213 if (this.disabled) {
12214 input.disabled=true;
12217 var inputblock = input;
12219 if(this.hasFeedback && !this.allowBlank){
12223 cls: 'glyphicon form-control-feedback'
12226 if(this.removable && !this.editable ){
12228 cls : 'has-feedback',
12234 cls : 'roo-combo-removable-btn close'
12241 cls : 'has-feedback',
12250 if(this.removable && !this.editable ){
12252 cls : 'roo-removable',
12258 cls : 'roo-combo-removable-btn close'
12265 if (this.before || this.after) {
12268 cls : 'input-group',
12272 inputblock.cn.push({
12274 cls : 'input-group-addon input-group-prepend input-group-text',
12279 inputblock.cn.push(input);
12281 if(this.hasFeedback && !this.allowBlank){
12282 inputblock.cls += ' has-feedback';
12283 inputblock.cn.push(feedback);
12287 inputblock.cn.push({
12289 cls : 'input-group-addon input-group-append input-group-text',
12298 var ibwrap = inputblock;
12303 cls: 'roo-select2-choices',
12307 cls: 'roo-select2-search-field',
12319 cls: 'roo-select2-container input-group',
12324 cls: 'form-hidden-field'
12330 if(!this.multiple && this.showToggleBtn){
12336 if (this.caret != false) {
12339 cls: 'fa fa-' + this.caret
12346 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12348 Roo.bootstrap.version == 3 ? caret : '',
12351 cls: 'combobox-clear',
12365 combobox.cls += ' roo-select2-container-multi';
12369 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12370 tooltip : 'This field is required'
12372 if (Roo.bootstrap.version == 4) {
12375 style : 'display:none'
12380 if (align ==='left' && this.fieldLabel.length) {
12382 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12389 cls : 'control-label',
12390 html : this.fieldLabel
12402 var labelCfg = cfg.cn[1];
12403 var contentCfg = cfg.cn[2];
12405 if(this.indicatorpos == 'right'){
12410 cls : 'control-label',
12414 html : this.fieldLabel
12428 labelCfg = cfg.cn[0];
12429 contentCfg = cfg.cn[1];
12432 if(this.labelWidth > 12){
12433 labelCfg.style = "width: " + this.labelWidth + 'px';
12436 if(this.labelWidth < 13 && this.labelmd == 0){
12437 this.labelmd = this.labelWidth;
12440 if(this.labellg > 0){
12441 labelCfg.cls += ' col-lg-' + this.labellg;
12442 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12445 if(this.labelmd > 0){
12446 labelCfg.cls += ' col-md-' + this.labelmd;
12447 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12450 if(this.labelsm > 0){
12451 labelCfg.cls += ' col-sm-' + this.labelsm;
12452 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12455 if(this.labelxs > 0){
12456 labelCfg.cls += ' col-xs-' + this.labelxs;
12457 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12460 } else if ( this.fieldLabel.length) {
12461 // Roo.log(" label");
12466 //cls : 'input-group-addon',
12467 html : this.fieldLabel
12475 if(this.indicatorpos == 'right'){
12483 html : this.fieldLabel
12497 // Roo.log(" no label && no align");
12504 ['xs','sm','md','lg'].map(function(size){
12505 if (settings[size]) {
12506 cfg.cls += ' col-' + size + '-' + settings[size];
12517 onResize : function(w, h){
12518 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12519 // if(typeof w == 'number'){
12520 // var x = w - this.trigger.getWidth();
12521 // this.inputEl().setWidth(this.adjustWidth('input', x));
12522 // this.trigger.setStyle('left', x+'px');
12527 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12530 getResizeEl : function(){
12531 return this.inputEl();
12535 getPositionEl : function(){
12536 return this.inputEl();
12540 alignErrorIcon : function(){
12541 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12545 initEvents : function(){
12549 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12550 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12551 if(!this.multiple && this.showToggleBtn){
12552 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12553 if(this.hideTrigger){
12554 this.trigger.setDisplayed(false);
12556 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12560 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12563 if(this.removable && !this.editable && !this.tickable){
12564 var close = this.closeTriggerEl();
12567 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12568 close.on('click', this.removeBtnClick, this, close);
12572 //this.trigger.addClassOnOver('x-form-trigger-over');
12573 //this.trigger.addClassOnClick('x-form-trigger-click');
12576 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12580 closeTriggerEl : function()
12582 var close = this.el.select('.roo-combo-removable-btn', true).first();
12583 return close ? close : false;
12586 removeBtnClick : function(e, h, el)
12588 e.preventDefault();
12590 if(this.fireEvent("remove", this) !== false){
12592 this.fireEvent("afterremove", this)
12596 createList : function()
12598 this.list = Roo.get(document.body).createChild({
12599 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12600 cls: 'typeahead typeahead-long dropdown-menu shadow',
12601 style: 'display:none'
12604 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12609 initTrigger : function(){
12614 onDestroy : function(){
12616 this.trigger.removeAllListeners();
12617 // this.trigger.remove();
12620 // this.wrap.remove();
12622 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12626 onFocus : function(){
12627 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12629 if(!this.mimicing){
12630 this.wrap.addClass('x-trigger-wrap-focus');
12631 this.mimicing = true;
12632 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12633 if(this.monitorTab){
12634 this.el.on("keydown", this.checkTab, this);
12641 checkTab : function(e){
12642 if(e.getKey() == e.TAB){
12643 this.triggerBlur();
12648 onBlur : function(){
12653 mimicBlur : function(e, t){
12655 if(!this.wrap.contains(t) && this.validateBlur()){
12656 this.triggerBlur();
12662 triggerBlur : function(){
12663 this.mimicing = false;
12664 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12665 if(this.monitorTab){
12666 this.el.un("keydown", this.checkTab, this);
12668 //this.wrap.removeClass('x-trigger-wrap-focus');
12669 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12673 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12674 validateBlur : function(e, t){
12679 onDisable : function(){
12680 this.inputEl().dom.disabled = true;
12681 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12683 // this.wrap.addClass('x-item-disabled');
12688 onEnable : function(){
12689 this.inputEl().dom.disabled = false;
12690 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12692 // this.el.removeClass('x-item-disabled');
12697 onShow : function(){
12698 var ae = this.getActionEl();
12701 ae.dom.style.display = '';
12702 ae.dom.style.visibility = 'visible';
12708 onHide : function(){
12709 var ae = this.getActionEl();
12710 ae.dom.style.display = 'none';
12714 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12715 * by an implementing function.
12717 * @param {EventObject} e
12719 onTriggerClick : Roo.emptyFn
12727 * @class Roo.bootstrap.CardUploader
12728 * @extends Roo.bootstrap.Button
12729 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12730 * @cfg {Number} errorTimeout default 3000
12731 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12732 * @cfg {Array} html The button text.
12736 * Create a new CardUploader
12737 * @param {Object} config The config object
12740 Roo.bootstrap.CardUploader = function(config){
12744 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12747 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12755 * When a image is clicked on - and needs to display a slideshow or similar..
12756 * @param {Roo.bootstrap.Card} this
12757 * @param {Object} The image information data
12763 * When a the download link is clicked
12764 * @param {Roo.bootstrap.Card} this
12765 * @param {Object} The image information data contains
12772 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12775 errorTimeout : 3000,
12779 fileCollection : false,
12782 getAutoCreate : function()
12786 cls :'form-group' ,
12791 //cls : 'input-group-addon',
12792 html : this.fieldLabel
12800 value : this.value,
12801 cls : 'd-none form-control'
12806 multiple : 'multiple',
12808 cls : 'd-none roo-card-upload-selector'
12812 cls : 'roo-card-uploader-button-container w-100 mb-2'
12815 cls : 'card-columns roo-card-uploader-container'
12825 getChildContainer : function() /// what children are added to.
12827 return this.containerEl;
12830 getButtonContainer : function() /// what children are added to.
12832 return this.el.select(".roo-card-uploader-button-container").first();
12835 initEvents : function()
12838 Roo.bootstrap.Input.prototype.initEvents.call(this);
12842 xns: Roo.bootstrap,
12845 container_method : 'getButtonContainer' ,
12846 html : this.html, // fix changable?
12849 'click' : function(btn, e) {
12858 this.urlAPI = (window.createObjectURL && window) ||
12859 (window.URL && URL.revokeObjectURL && URL) ||
12860 (window.webkitURL && webkitURL);
12865 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12867 this.selectorEl.on('change', this.onFileSelected, this);
12870 this.images.forEach(function(img) {
12873 this.images = false;
12875 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12881 onClick : function(e)
12883 e.preventDefault();
12885 this.selectorEl.dom.click();
12889 onFileSelected : function(e)
12891 e.preventDefault();
12893 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12897 Roo.each(this.selectorEl.dom.files, function(file){
12898 this.addFile(file);
12907 addFile : function(file)
12910 if(typeof(file) === 'string'){
12911 throw "Add file by name?"; // should not happen
12915 if(!file || !this.urlAPI){
12925 var url = _this.urlAPI.createObjectURL( file);
12928 id : Roo.bootstrap.CardUploader.ID--,
12929 is_uploaded : false,
12933 mimetype : file.type,
12941 * addCard - add an Attachment to the uploader
12942 * @param data - the data about the image to upload
12946 title : "Title of file",
12947 is_uploaded : false,
12948 src : "http://.....",
12949 srcfile : { the File upload object },
12950 mimetype : file.type,
12953 .. any other data...
12959 addCard : function (data)
12961 // hidden input element?
12962 // if the file is not an image...
12963 //then we need to use something other that and header_image
12968 xns : Roo.bootstrap,
12969 xtype : 'CardFooter',
12972 xns : Roo.bootstrap,
12978 xns : Roo.bootstrap,
12980 html : String.format("<small>{0}</small>", data.title),
12981 cls : 'col-10 text-left',
12986 click : function() {
12988 t.fireEvent( "download", t, data );
12994 xns : Roo.bootstrap,
12996 style: 'max-height: 28px; ',
13002 click : function() {
13003 t.removeCard(data.id)
13015 var cn = this.addxtype(
13018 xns : Roo.bootstrap,
13021 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13022 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13023 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13028 initEvents : function() {
13029 Roo.bootstrap.Card.prototype.initEvents.call(this);
13031 this.imgEl = this.el.select('.card-img-top').first();
13033 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13034 this.imgEl.set({ 'pointer' : 'cursor' });
13037 this.getCardFooter().addClass('p-1');
13044 // dont' really need ot update items.
13045 // this.items.push(cn);
13046 this.fileCollection.add(cn);
13048 if (!data.srcfile) {
13049 this.updateInput();
13054 var reader = new FileReader();
13055 reader.addEventListener("load", function() {
13056 data.srcdata = reader.result;
13059 reader.readAsDataURL(data.srcfile);
13064 removeCard : function(id)
13067 var card = this.fileCollection.get(id);
13068 card.data.is_deleted = 1;
13069 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13070 //this.fileCollection.remove(card);
13071 //this.items = this.items.filter(function(e) { return e != card });
13072 // dont' really need ot update items.
13073 card.el.dom.parentNode.removeChild(card.el.dom);
13074 this.updateInput();
13080 this.fileCollection.each(function(card) {
13081 if (card.el.dom && card.el.dom.parentNode) {
13082 card.el.dom.parentNode.removeChild(card.el.dom);
13085 this.fileCollection.clear();
13086 this.updateInput();
13089 updateInput : function()
13092 this.fileCollection.each(function(e) {
13096 this.inputEl().dom.value = JSON.stringify(data);
13106 Roo.bootstrap.CardUploader.ID = -1;/*
13108 * Ext JS Library 1.1.1
13109 * Copyright(c) 2006-2007, Ext JS, LLC.
13111 * Originally Released Under LGPL - original licence link has changed is not relivant.
13114 * <script type="text/javascript">
13119 * @class Roo.data.SortTypes
13121 * Defines the default sorting (casting?) comparison functions used when sorting data.
13123 Roo.data.SortTypes = {
13125 * Default sort that does nothing
13126 * @param {Mixed} s The value being converted
13127 * @return {Mixed} The comparison value
13129 none : function(s){
13134 * The regular expression used to strip tags
13138 stripTagsRE : /<\/?[^>]+>/gi,
13141 * Strips all HTML tags to sort on text only
13142 * @param {Mixed} s The value being converted
13143 * @return {String} The comparison value
13145 asText : function(s){
13146 return String(s).replace(this.stripTagsRE, "");
13150 * Strips all HTML tags to sort on text only - Case insensitive
13151 * @param {Mixed} s The value being converted
13152 * @return {String} The comparison value
13154 asUCText : function(s){
13155 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13159 * Case insensitive string
13160 * @param {Mixed} s The value being converted
13161 * @return {String} The comparison value
13163 asUCString : function(s) {
13164 return String(s).toUpperCase();
13169 * @param {Mixed} s The value being converted
13170 * @return {Number} The comparison value
13172 asDate : function(s) {
13176 if(s instanceof Date){
13177 return s.getTime();
13179 return Date.parse(String(s));
13184 * @param {Mixed} s The value being converted
13185 * @return {Float} The comparison value
13187 asFloat : function(s) {
13188 var val = parseFloat(String(s).replace(/,/g, ""));
13197 * @param {Mixed} s The value being converted
13198 * @return {Number} The comparison value
13200 asInt : function(s) {
13201 var val = parseInt(String(s).replace(/,/g, ""));
13209 * Ext JS Library 1.1.1
13210 * Copyright(c) 2006-2007, Ext JS, LLC.
13212 * Originally Released Under LGPL - original licence link has changed is not relivant.
13215 * <script type="text/javascript">
13219 * @class Roo.data.Record
13220 * Instances of this class encapsulate both record <em>definition</em> information, and record
13221 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13222 * to access Records cached in an {@link Roo.data.Store} object.<br>
13224 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13225 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13228 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13230 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13231 * {@link #create}. The parameters are the same.
13232 * @param {Array} data An associative Array of data values keyed by the field name.
13233 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13234 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13235 * not specified an integer id is generated.
13237 Roo.data.Record = function(data, id){
13238 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13243 * Generate a constructor for a specific record layout.
13244 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13245 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13246 * Each field definition object may contain the following properties: <ul>
13247 * <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,
13248 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13249 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13250 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13251 * is being used, then this is a string containing the javascript expression to reference the data relative to
13252 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13253 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13254 * this may be omitted.</p></li>
13255 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13256 * <ul><li>auto (Default, implies no conversion)</li>
13261 * <li>date</li></ul></p></li>
13262 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13263 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13264 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13265 * by the Reader into an object that will be stored in the Record. It is passed the
13266 * following parameters:<ul>
13267 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13269 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13271 * <br>usage:<br><pre><code>
13272 var TopicRecord = Roo.data.Record.create(
13273 {name: 'title', mapping: 'topic_title'},
13274 {name: 'author', mapping: 'username'},
13275 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13276 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13277 {name: 'lastPoster', mapping: 'user2'},
13278 {name: 'excerpt', mapping: 'post_text'}
13281 var myNewRecord = new TopicRecord({
13282 title: 'Do my job please',
13285 lastPost: new Date(),
13286 lastPoster: 'Animal',
13287 excerpt: 'No way dude!'
13289 myStore.add(myNewRecord);
13294 Roo.data.Record.create = function(o){
13295 var f = function(){
13296 f.superclass.constructor.apply(this, arguments);
13298 Roo.extend(f, Roo.data.Record);
13299 var p = f.prototype;
13300 p.fields = new Roo.util.MixedCollection(false, function(field){
13303 for(var i = 0, len = o.length; i < len; i++){
13304 p.fields.add(new Roo.data.Field(o[i]));
13306 f.getField = function(name){
13307 return p.fields.get(name);
13312 Roo.data.Record.AUTO_ID = 1000;
13313 Roo.data.Record.EDIT = 'edit';
13314 Roo.data.Record.REJECT = 'reject';
13315 Roo.data.Record.COMMIT = 'commit';
13317 Roo.data.Record.prototype = {
13319 * Readonly flag - true if this record has been modified.
13328 join : function(store){
13329 this.store = store;
13333 * Set the named field to the specified value.
13334 * @param {String} name The name of the field to set.
13335 * @param {Object} value The value to set the field to.
13337 set : function(name, value){
13338 if(this.data[name] == value){
13342 if(!this.modified){
13343 this.modified = {};
13345 if(typeof this.modified[name] == 'undefined'){
13346 this.modified[name] = this.data[name];
13348 this.data[name] = value;
13349 if(!this.editing && this.store){
13350 this.store.afterEdit(this);
13355 * Get the value of the named field.
13356 * @param {String} name The name of the field to get the value of.
13357 * @return {Object} The value of the field.
13359 get : function(name){
13360 return this.data[name];
13364 beginEdit : function(){
13365 this.editing = true;
13366 this.modified = {};
13370 cancelEdit : function(){
13371 this.editing = false;
13372 delete this.modified;
13376 endEdit : function(){
13377 this.editing = false;
13378 if(this.dirty && this.store){
13379 this.store.afterEdit(this);
13384 * Usually called by the {@link Roo.data.Store} which owns the Record.
13385 * Rejects all changes made to the Record since either creation, or the last commit operation.
13386 * Modified fields are reverted to their original values.
13388 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13389 * of reject operations.
13391 reject : function(){
13392 var m = this.modified;
13394 if(typeof m[n] != "function"){
13395 this.data[n] = m[n];
13398 this.dirty = false;
13399 delete this.modified;
13400 this.editing = false;
13402 this.store.afterReject(this);
13407 * Usually called by the {@link Roo.data.Store} which owns the Record.
13408 * Commits all changes made to the Record since either creation, or the last commit operation.
13410 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13411 * of commit operations.
13413 commit : function(){
13414 this.dirty = false;
13415 delete this.modified;
13416 this.editing = false;
13418 this.store.afterCommit(this);
13423 hasError : function(){
13424 return this.error != null;
13428 clearError : function(){
13433 * Creates a copy of this record.
13434 * @param {String} id (optional) A new record id if you don't want to use this record's id
13437 copy : function(newId) {
13438 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13442 * Ext JS Library 1.1.1
13443 * Copyright(c) 2006-2007, Ext JS, LLC.
13445 * Originally Released Under LGPL - original licence link has changed is not relivant.
13448 * <script type="text/javascript">
13454 * @class Roo.data.Store
13455 * @extends Roo.util.Observable
13456 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13457 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13459 * 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
13460 * has no knowledge of the format of the data returned by the Proxy.<br>
13462 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13463 * instances from the data object. These records are cached and made available through accessor functions.
13465 * Creates a new Store.
13466 * @param {Object} config A config object containing the objects needed for the Store to access data,
13467 * and read the data into Records.
13469 Roo.data.Store = function(config){
13470 this.data = new Roo.util.MixedCollection(false);
13471 this.data.getKey = function(o){
13474 this.baseParams = {};
13476 this.paramNames = {
13481 "multisort" : "_multisort"
13484 if(config && config.data){
13485 this.inlineData = config.data;
13486 delete config.data;
13489 Roo.apply(this, config);
13491 if(this.reader){ // reader passed
13492 this.reader = Roo.factory(this.reader, Roo.data);
13493 this.reader.xmodule = this.xmodule || false;
13494 if(!this.recordType){
13495 this.recordType = this.reader.recordType;
13497 if(this.reader.onMetaChange){
13498 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13502 if(this.recordType){
13503 this.fields = this.recordType.prototype.fields;
13505 this.modified = [];
13509 * @event datachanged
13510 * Fires when the data cache has changed, and a widget which is using this Store
13511 * as a Record cache should refresh its view.
13512 * @param {Store} this
13514 datachanged : true,
13516 * @event metachange
13517 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13518 * @param {Store} this
13519 * @param {Object} meta The JSON metadata
13524 * Fires when Records have been added to the Store
13525 * @param {Store} this
13526 * @param {Roo.data.Record[]} records The array of Records added
13527 * @param {Number} index The index at which the record(s) were added
13532 * Fires when a Record has been removed from the Store
13533 * @param {Store} this
13534 * @param {Roo.data.Record} record The Record that was removed
13535 * @param {Number} index The index at which the record was removed
13540 * Fires when a Record has been updated
13541 * @param {Store} this
13542 * @param {Roo.data.Record} record The Record that was updated
13543 * @param {String} operation The update operation being performed. Value may be one of:
13545 Roo.data.Record.EDIT
13546 Roo.data.Record.REJECT
13547 Roo.data.Record.COMMIT
13553 * Fires when the data cache has been cleared.
13554 * @param {Store} this
13558 * @event beforeload
13559 * Fires before a request is made for a new data object. If the beforeload handler returns false
13560 * the load action will be canceled.
13561 * @param {Store} this
13562 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13566 * @event beforeloadadd
13567 * Fires after a new set of Records has been loaded.
13568 * @param {Store} this
13569 * @param {Roo.data.Record[]} records The Records that were loaded
13570 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13572 beforeloadadd : true,
13575 * Fires after a new set of Records has been loaded, before they are added to the store.
13576 * @param {Store} this
13577 * @param {Roo.data.Record[]} records The Records that were loaded
13578 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13579 * @params {Object} return from reader
13583 * @event loadexception
13584 * Fires if an exception occurs in the Proxy during loading.
13585 * Called with the signature of the Proxy's "loadexception" event.
13586 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13589 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13590 * @param {Object} load options
13591 * @param {Object} jsonData from your request (normally this contains the Exception)
13593 loadexception : true
13597 this.proxy = Roo.factory(this.proxy, Roo.data);
13598 this.proxy.xmodule = this.xmodule || false;
13599 this.relayEvents(this.proxy, ["loadexception"]);
13601 this.sortToggle = {};
13602 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13604 Roo.data.Store.superclass.constructor.call(this);
13606 if(this.inlineData){
13607 this.loadData(this.inlineData);
13608 delete this.inlineData;
13612 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13614 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13615 * without a remote query - used by combo/forms at present.
13619 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13622 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13625 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13626 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13629 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13630 * on any HTTP request
13633 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13636 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13640 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13641 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13643 remoteSort : false,
13646 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13647 * loaded or when a record is removed. (defaults to false).
13649 pruneModifiedRecords : false,
13652 lastOptions : null,
13655 * Add Records to the Store and fires the add event.
13656 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13658 add : function(records){
13659 records = [].concat(records);
13660 for(var i = 0, len = records.length; i < len; i++){
13661 records[i].join(this);
13663 var index = this.data.length;
13664 this.data.addAll(records);
13665 this.fireEvent("add", this, records, index);
13669 * Remove a Record from the Store and fires the remove event.
13670 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13672 remove : function(record){
13673 var index = this.data.indexOf(record);
13674 this.data.removeAt(index);
13676 if(this.pruneModifiedRecords){
13677 this.modified.remove(record);
13679 this.fireEvent("remove", this, record, index);
13683 * Remove all Records from the Store and fires the clear event.
13685 removeAll : function(){
13687 if(this.pruneModifiedRecords){
13688 this.modified = [];
13690 this.fireEvent("clear", this);
13694 * Inserts Records to the Store at the given index and fires the add event.
13695 * @param {Number} index The start index at which to insert the passed Records.
13696 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13698 insert : function(index, records){
13699 records = [].concat(records);
13700 for(var i = 0, len = records.length; i < len; i++){
13701 this.data.insert(index, records[i]);
13702 records[i].join(this);
13704 this.fireEvent("add", this, records, index);
13708 * Get the index within the cache of the passed Record.
13709 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13710 * @return {Number} The index of the passed Record. Returns -1 if not found.
13712 indexOf : function(record){
13713 return this.data.indexOf(record);
13717 * Get the index within the cache of the Record with the passed id.
13718 * @param {String} id The id of the Record to find.
13719 * @return {Number} The index of the Record. Returns -1 if not found.
13721 indexOfId : function(id){
13722 return this.data.indexOfKey(id);
13726 * Get the Record with the specified id.
13727 * @param {String} id The id of the Record to find.
13728 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13730 getById : function(id){
13731 return this.data.key(id);
13735 * Get the Record at the specified index.
13736 * @param {Number} index The index of the Record to find.
13737 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13739 getAt : function(index){
13740 return this.data.itemAt(index);
13744 * Returns a range of Records between specified indices.
13745 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13746 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13747 * @return {Roo.data.Record[]} An array of Records
13749 getRange : function(start, end){
13750 return this.data.getRange(start, end);
13754 storeOptions : function(o){
13755 o = Roo.apply({}, o);
13758 this.lastOptions = o;
13762 * Loads the Record cache from the configured Proxy using the configured Reader.
13764 * If using remote paging, then the first load call must specify the <em>start</em>
13765 * and <em>limit</em> properties in the options.params property to establish the initial
13766 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13768 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13769 * and this call will return before the new data has been loaded. Perform any post-processing
13770 * in a callback function, or in a "load" event handler.</strong>
13772 * @param {Object} options An object containing properties which control loading options:<ul>
13773 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13774 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13775 * passed the following arguments:<ul>
13776 * <li>r : Roo.data.Record[]</li>
13777 * <li>options: Options object from the load call</li>
13778 * <li>success: Boolean success indicator</li></ul></li>
13779 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13780 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13783 load : function(options){
13784 options = options || {};
13785 if(this.fireEvent("beforeload", this, options) !== false){
13786 this.storeOptions(options);
13787 var p = Roo.apply(options.params || {}, this.baseParams);
13788 // if meta was not loaded from remote source.. try requesting it.
13789 if (!this.reader.metaFromRemote) {
13790 p._requestMeta = 1;
13792 if(this.sortInfo && this.remoteSort){
13793 var pn = this.paramNames;
13794 p[pn["sort"]] = this.sortInfo.field;
13795 p[pn["dir"]] = this.sortInfo.direction;
13797 if (this.multiSort) {
13798 var pn = this.paramNames;
13799 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13802 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13807 * Reloads the Record cache from the configured Proxy using the configured Reader and
13808 * the options from the last load operation performed.
13809 * @param {Object} options (optional) An object containing properties which may override the options
13810 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13811 * the most recently used options are reused).
13813 reload : function(options){
13814 this.load(Roo.applyIf(options||{}, this.lastOptions));
13818 // Called as a callback by the Reader during a load operation.
13819 loadRecords : function(o, options, success){
13820 if(!o || success === false){
13821 if(success !== false){
13822 this.fireEvent("load", this, [], options, o);
13824 if(options.callback){
13825 options.callback.call(options.scope || this, [], options, false);
13829 // if data returned failure - throw an exception.
13830 if (o.success === false) {
13831 // show a message if no listener is registered.
13832 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13833 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13835 // loadmask wil be hooked into this..
13836 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13839 var r = o.records, t = o.totalRecords || r.length;
13841 this.fireEvent("beforeloadadd", this, r, options, o);
13843 if(!options || options.add !== true){
13844 if(this.pruneModifiedRecords){
13845 this.modified = [];
13847 for(var i = 0, len = r.length; i < len; i++){
13851 this.data = this.snapshot;
13852 delete this.snapshot;
13855 this.data.addAll(r);
13856 this.totalLength = t;
13858 this.fireEvent("datachanged", this);
13860 this.totalLength = Math.max(t, this.data.length+r.length);
13864 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13866 var e = new Roo.data.Record({});
13868 e.set(this.parent.displayField, this.parent.emptyTitle);
13869 e.set(this.parent.valueField, '');
13874 this.fireEvent("load", this, r, options, o);
13875 if(options.callback){
13876 options.callback.call(options.scope || this, r, options, true);
13882 * Loads data from a passed data block. A Reader which understands the format of the data
13883 * must have been configured in the constructor.
13884 * @param {Object} data The data block from which to read the Records. The format of the data expected
13885 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13886 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13888 loadData : function(o, append){
13889 var r = this.reader.readRecords(o);
13890 this.loadRecords(r, {add: append}, true);
13894 * using 'cn' the nested child reader read the child array into it's child stores.
13895 * @param {Object} rec The record with a 'children array
13897 loadDataFromChildren : function(rec)
13899 this.loadData(this.reader.toLoadData(rec));
13904 * Gets the number of cached records.
13906 * <em>If using paging, this may not be the total size of the dataset. If the data object
13907 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13908 * the data set size</em>
13910 getCount : function(){
13911 return this.data.length || 0;
13915 * Gets the total number of records in the dataset as returned by the server.
13917 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13918 * the dataset size</em>
13920 getTotalCount : function(){
13921 return this.totalLength || 0;
13925 * Returns the sort state of the Store as an object with two properties:
13927 field {String} The name of the field by which the Records are sorted
13928 direction {String} The sort order, "ASC" or "DESC"
13931 getSortState : function(){
13932 return this.sortInfo;
13936 applySort : function(){
13937 if(this.sortInfo && !this.remoteSort){
13938 var s = this.sortInfo, f = s.field;
13939 var st = this.fields.get(f).sortType;
13940 var fn = function(r1, r2){
13941 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13942 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13944 this.data.sort(s.direction, fn);
13945 if(this.snapshot && this.snapshot != this.data){
13946 this.snapshot.sort(s.direction, fn);
13952 * Sets the default sort column and order to be used by the next load operation.
13953 * @param {String} fieldName The name of the field to sort by.
13954 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13956 setDefaultSort : function(field, dir){
13957 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13961 * Sort the Records.
13962 * If remote sorting is used, the sort is performed on the server, and the cache is
13963 * reloaded. If local sorting is used, the cache is sorted internally.
13964 * @param {String} fieldName The name of the field to sort by.
13965 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13967 sort : function(fieldName, dir){
13968 var f = this.fields.get(fieldName);
13970 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13972 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13973 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13978 this.sortToggle[f.name] = dir;
13979 this.sortInfo = {field: f.name, direction: dir};
13980 if(!this.remoteSort){
13982 this.fireEvent("datachanged", this);
13984 this.load(this.lastOptions);
13989 * Calls the specified function for each of the Records in the cache.
13990 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13991 * Returning <em>false</em> aborts and exits the iteration.
13992 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13994 each : function(fn, scope){
13995 this.data.each(fn, scope);
13999 * Gets all records modified since the last commit. Modified records are persisted across load operations
14000 * (e.g., during paging).
14001 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14003 getModifiedRecords : function(){
14004 return this.modified;
14008 createFilterFn : function(property, value, anyMatch){
14009 if(!value.exec){ // not a regex
14010 value = String(value);
14011 if(value.length == 0){
14014 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14016 return function(r){
14017 return value.test(r.data[property]);
14022 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14023 * @param {String} property A field on your records
14024 * @param {Number} start The record index to start at (defaults to 0)
14025 * @param {Number} end The last record index to include (defaults to length - 1)
14026 * @return {Number} The sum
14028 sum : function(property, start, end){
14029 var rs = this.data.items, v = 0;
14030 start = start || 0;
14031 end = (end || end === 0) ? end : rs.length-1;
14033 for(var i = start; i <= end; i++){
14034 v += (rs[i].data[property] || 0);
14040 * Filter the records by a specified property.
14041 * @param {String} field A field on your records
14042 * @param {String/RegExp} value Either a string that the field
14043 * should start with or a RegExp to test against the field
14044 * @param {Boolean} anyMatch True to match any part not just the beginning
14046 filter : function(property, value, anyMatch){
14047 var fn = this.createFilterFn(property, value, anyMatch);
14048 return fn ? this.filterBy(fn) : this.clearFilter();
14052 * Filter by a function. The specified function will be called with each
14053 * record in this data source. If the function returns true the record is included,
14054 * otherwise it is filtered.
14055 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14056 * @param {Object} scope (optional) The scope of the function (defaults to this)
14058 filterBy : function(fn, scope){
14059 this.snapshot = this.snapshot || this.data;
14060 this.data = this.queryBy(fn, scope||this);
14061 this.fireEvent("datachanged", this);
14065 * Query the records by a specified property.
14066 * @param {String} field A field on your records
14067 * @param {String/RegExp} value Either a string that the field
14068 * should start with or a RegExp to test against the field
14069 * @param {Boolean} anyMatch True to match any part not just the beginning
14070 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14072 query : function(property, value, anyMatch){
14073 var fn = this.createFilterFn(property, value, anyMatch);
14074 return fn ? this.queryBy(fn) : this.data.clone();
14078 * Query by a function. The specified function will be called with each
14079 * record in this data source. If the function returns true the record is included
14081 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14082 * @param {Object} scope (optional) The scope of the function (defaults to this)
14083 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14085 queryBy : function(fn, scope){
14086 var data = this.snapshot || this.data;
14087 return data.filterBy(fn, scope||this);
14091 * Collects unique values for a particular dataIndex from this store.
14092 * @param {String} dataIndex The property to collect
14093 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14094 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14095 * @return {Array} An array of the unique values
14097 collect : function(dataIndex, allowNull, bypassFilter){
14098 var d = (bypassFilter === true && this.snapshot) ?
14099 this.snapshot.items : this.data.items;
14100 var v, sv, r = [], l = {};
14101 for(var i = 0, len = d.length; i < len; i++){
14102 v = d[i].data[dataIndex];
14104 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14113 * Revert to a view of the Record cache with no filtering applied.
14114 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14116 clearFilter : function(suppressEvent){
14117 if(this.snapshot && this.snapshot != this.data){
14118 this.data = this.snapshot;
14119 delete this.snapshot;
14120 if(suppressEvent !== true){
14121 this.fireEvent("datachanged", this);
14127 afterEdit : function(record){
14128 if(this.modified.indexOf(record) == -1){
14129 this.modified.push(record);
14131 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14135 afterReject : function(record){
14136 this.modified.remove(record);
14137 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14141 afterCommit : function(record){
14142 this.modified.remove(record);
14143 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14147 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14148 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14150 commitChanges : function(){
14151 var m = this.modified.slice(0);
14152 this.modified = [];
14153 for(var i = 0, len = m.length; i < len; i++){
14159 * Cancel outstanding changes on all changed records.
14161 rejectChanges : function(){
14162 var m = this.modified.slice(0);
14163 this.modified = [];
14164 for(var i = 0, len = m.length; i < len; i++){
14169 onMetaChange : function(meta, rtype, o){
14170 this.recordType = rtype;
14171 this.fields = rtype.prototype.fields;
14172 delete this.snapshot;
14173 this.sortInfo = meta.sortInfo || this.sortInfo;
14174 this.modified = [];
14175 this.fireEvent('metachange', this, this.reader.meta);
14178 moveIndex : function(data, type)
14180 var index = this.indexOf(data);
14182 var newIndex = index + type;
14186 this.insert(newIndex, data);
14191 * Ext JS Library 1.1.1
14192 * Copyright(c) 2006-2007, Ext JS, LLC.
14194 * Originally Released Under LGPL - original licence link has changed is not relivant.
14197 * <script type="text/javascript">
14201 * @class Roo.data.SimpleStore
14202 * @extends Roo.data.Store
14203 * Small helper class to make creating Stores from Array data easier.
14204 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14205 * @cfg {Array} fields An array of field definition objects, or field name strings.
14206 * @cfg {Object} an existing reader (eg. copied from another store)
14207 * @cfg {Array} data The multi-dimensional array of data
14209 * @param {Object} config
14211 Roo.data.SimpleStore = function(config)
14213 Roo.data.SimpleStore.superclass.constructor.call(this, {
14215 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14218 Roo.data.Record.create(config.fields)
14220 proxy : new Roo.data.MemoryProxy(config.data)
14224 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14226 * Ext JS Library 1.1.1
14227 * Copyright(c) 2006-2007, Ext JS, LLC.
14229 * Originally Released Under LGPL - original licence link has changed is not relivant.
14232 * <script type="text/javascript">
14237 * @extends Roo.data.Store
14238 * @class Roo.data.JsonStore
14239 * Small helper class to make creating Stores for JSON data easier. <br/>
14241 var store = new Roo.data.JsonStore({
14242 url: 'get-images.php',
14244 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14247 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14248 * JsonReader and HttpProxy (unless inline data is provided).</b>
14249 * @cfg {Array} fields An array of field definition objects, or field name strings.
14251 * @param {Object} config
14253 Roo.data.JsonStore = function(c){
14254 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14255 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14256 reader: new Roo.data.JsonReader(c, c.fields)
14259 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14261 * Ext JS Library 1.1.1
14262 * Copyright(c) 2006-2007, Ext JS, LLC.
14264 * Originally Released Under LGPL - original licence link has changed is not relivant.
14267 * <script type="text/javascript">
14271 Roo.data.Field = function(config){
14272 if(typeof config == "string"){
14273 config = {name: config};
14275 Roo.apply(this, config);
14278 this.type = "auto";
14281 var st = Roo.data.SortTypes;
14282 // named sortTypes are supported, here we look them up
14283 if(typeof this.sortType == "string"){
14284 this.sortType = st[this.sortType];
14287 // set default sortType for strings and dates
14288 if(!this.sortType){
14291 this.sortType = st.asUCString;
14294 this.sortType = st.asDate;
14297 this.sortType = st.none;
14302 var stripRe = /[\$,%]/g;
14304 // prebuilt conversion function for this field, instead of
14305 // switching every time we're reading a value
14307 var cv, dateFormat = this.dateFormat;
14312 cv = function(v){ return v; };
14315 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14319 return v !== undefined && v !== null && v !== '' ?
14320 parseInt(String(v).replace(stripRe, ""), 10) : '';
14325 return v !== undefined && v !== null && v !== '' ?
14326 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14331 cv = function(v){ return v === true || v === "true" || v == 1; };
14338 if(v instanceof Date){
14342 if(dateFormat == "timestamp"){
14343 return new Date(v*1000);
14345 return Date.parseDate(v, dateFormat);
14347 var parsed = Date.parse(v);
14348 return parsed ? new Date(parsed) : null;
14357 Roo.data.Field.prototype = {
14365 * Ext JS Library 1.1.1
14366 * Copyright(c) 2006-2007, Ext JS, LLC.
14368 * Originally Released Under LGPL - original licence link has changed is not relivant.
14371 * <script type="text/javascript">
14374 // Base class for reading structured data from a data source. This class is intended to be
14375 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14378 * @class Roo.data.DataReader
14379 * Base class for reading structured data from a data source. This class is intended to be
14380 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14383 Roo.data.DataReader = function(meta, recordType){
14387 this.recordType = recordType instanceof Array ?
14388 Roo.data.Record.create(recordType) : recordType;
14391 Roo.data.DataReader.prototype = {
14394 readerType : 'Data',
14396 * Create an empty record
14397 * @param {Object} data (optional) - overlay some values
14398 * @return {Roo.data.Record} record created.
14400 newRow : function(d) {
14402 this.recordType.prototype.fields.each(function(c) {
14404 case 'int' : da[c.name] = 0; break;
14405 case 'date' : da[c.name] = new Date(); break;
14406 case 'float' : da[c.name] = 0.0; break;
14407 case 'boolean' : da[c.name] = false; break;
14408 default : da[c.name] = ""; break;
14412 return new this.recordType(Roo.apply(da, d));
14418 * Ext JS Library 1.1.1
14419 * Copyright(c) 2006-2007, Ext JS, LLC.
14421 * Originally Released Under LGPL - original licence link has changed is not relivant.
14424 * <script type="text/javascript">
14428 * @class Roo.data.DataProxy
14429 * @extends Roo.data.Observable
14430 * This class is an abstract base class for implementations which provide retrieval of
14431 * unformatted data objects.<br>
14433 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14434 * (of the appropriate type which knows how to parse the data object) to provide a block of
14435 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14437 * Custom implementations must implement the load method as described in
14438 * {@link Roo.data.HttpProxy#load}.
14440 Roo.data.DataProxy = function(){
14443 * @event beforeload
14444 * Fires before a network request is made to retrieve a data object.
14445 * @param {Object} This DataProxy object.
14446 * @param {Object} params The params parameter to the load function.
14451 * Fires before the load method's callback is called.
14452 * @param {Object} This DataProxy object.
14453 * @param {Object} o The data object.
14454 * @param {Object} arg The callback argument object passed to the load function.
14458 * @event loadexception
14459 * Fires if an Exception occurs during data retrieval.
14460 * @param {Object} This DataProxy object.
14461 * @param {Object} o The data object.
14462 * @param {Object} arg The callback argument object passed to the load function.
14463 * @param {Object} e The Exception.
14465 loadexception : true
14467 Roo.data.DataProxy.superclass.constructor.call(this);
14470 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14473 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14477 * Ext JS Library 1.1.1
14478 * Copyright(c) 2006-2007, Ext JS, LLC.
14480 * Originally Released Under LGPL - original licence link has changed is not relivant.
14483 * <script type="text/javascript">
14486 * @class Roo.data.MemoryProxy
14487 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14488 * to the Reader when its load method is called.
14490 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14492 Roo.data.MemoryProxy = function(data){
14496 Roo.data.MemoryProxy.superclass.constructor.call(this);
14500 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14503 * Load data from the requested source (in this case an in-memory
14504 * data object passed to the constructor), read the data object into
14505 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14506 * process that block using the passed callback.
14507 * @param {Object} params This parameter is not used by the MemoryProxy class.
14508 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14509 * object into a block of Roo.data.Records.
14510 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14511 * The function must be passed <ul>
14512 * <li>The Record block object</li>
14513 * <li>The "arg" argument from the load function</li>
14514 * <li>A boolean success indicator</li>
14516 * @param {Object} scope The scope in which to call the callback
14517 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14519 load : function(params, reader, callback, scope, arg){
14520 params = params || {};
14523 result = reader.readRecords(params.data ? params.data :this.data);
14525 this.fireEvent("loadexception", this, arg, null, e);
14526 callback.call(scope, null, arg, false);
14529 callback.call(scope, result, arg, true);
14533 update : function(params, records){
14538 * Ext JS Library 1.1.1
14539 * Copyright(c) 2006-2007, Ext JS, LLC.
14541 * Originally Released Under LGPL - original licence link has changed is not relivant.
14544 * <script type="text/javascript">
14547 * @class Roo.data.HttpProxy
14548 * @extends Roo.data.DataProxy
14549 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14550 * configured to reference a certain URL.<br><br>
14552 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14553 * from which the running page was served.<br><br>
14555 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14557 * Be aware that to enable the browser to parse an XML document, the server must set
14558 * the Content-Type header in the HTTP response to "text/xml".
14560 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14561 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14562 * will be used to make the request.
14564 Roo.data.HttpProxy = function(conn){
14565 Roo.data.HttpProxy.superclass.constructor.call(this);
14566 // is conn a conn config or a real conn?
14568 this.useAjax = !conn || !conn.events;
14572 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14573 // thse are take from connection...
14576 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14579 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14580 * extra parameters to each request made by this object. (defaults to undefined)
14583 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14584 * to each request made by this object. (defaults to undefined)
14587 * @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)
14590 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14593 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14599 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14603 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14604 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14605 * a finer-grained basis than the DataProxy events.
14607 getConnection : function(){
14608 return this.useAjax ? Roo.Ajax : this.conn;
14612 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14613 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14614 * process that block using the passed callback.
14615 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14616 * for the request to the remote server.
14617 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14618 * object into a block of Roo.data.Records.
14619 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14620 * The function must be passed <ul>
14621 * <li>The Record block object</li>
14622 * <li>The "arg" argument from the load function</li>
14623 * <li>A boolean success indicator</li>
14625 * @param {Object} scope The scope in which to call the callback
14626 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14628 load : function(params, reader, callback, scope, arg){
14629 if(this.fireEvent("beforeload", this, params) !== false){
14631 params : params || {},
14633 callback : callback,
14638 callback : this.loadResponse,
14642 Roo.applyIf(o, this.conn);
14643 if(this.activeRequest){
14644 Roo.Ajax.abort(this.activeRequest);
14646 this.activeRequest = Roo.Ajax.request(o);
14648 this.conn.request(o);
14651 callback.call(scope||this, null, arg, false);
14656 loadResponse : function(o, success, response){
14657 delete this.activeRequest;
14659 this.fireEvent("loadexception", this, o, response);
14660 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14665 result = o.reader.read(response);
14667 this.fireEvent("loadexception", this, o, response, e);
14668 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14672 this.fireEvent("load", this, o, o.request.arg);
14673 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14677 update : function(dataSet){
14682 updateResponse : function(dataSet){
14687 * Ext JS Library 1.1.1
14688 * Copyright(c) 2006-2007, Ext JS, LLC.
14690 * Originally Released Under LGPL - original licence link has changed is not relivant.
14693 * <script type="text/javascript">
14697 * @class Roo.data.ScriptTagProxy
14698 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14699 * other than the originating domain of the running page.<br><br>
14701 * <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
14702 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14704 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14705 * source code that is used as the source inside a <script> tag.<br><br>
14707 * In order for the browser to process the returned data, the server must wrap the data object
14708 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14709 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14710 * depending on whether the callback name was passed:
14713 boolean scriptTag = false;
14714 String cb = request.getParameter("callback");
14717 response.setContentType("text/javascript");
14719 response.setContentType("application/x-json");
14721 Writer out = response.getWriter();
14723 out.write(cb + "(");
14725 out.print(dataBlock.toJsonString());
14732 * @param {Object} config A configuration object.
14734 Roo.data.ScriptTagProxy = function(config){
14735 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14736 Roo.apply(this, config);
14737 this.head = document.getElementsByTagName("head")[0];
14740 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14742 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14744 * @cfg {String} url The URL from which to request the data object.
14747 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14751 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14752 * the server the name of the callback function set up by the load call to process the returned data object.
14753 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14754 * javascript output which calls this named function passing the data object as its only parameter.
14756 callbackParam : "callback",
14758 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14759 * name to the request.
14764 * Load data from the configured URL, read the data object into
14765 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14766 * process that block using the passed callback.
14767 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14768 * for the request to the remote server.
14769 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14770 * object into a block of Roo.data.Records.
14771 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14772 * The function must be passed <ul>
14773 * <li>The Record block object</li>
14774 * <li>The "arg" argument from the load function</li>
14775 * <li>A boolean success indicator</li>
14777 * @param {Object} scope The scope in which to call the callback
14778 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14780 load : function(params, reader, callback, scope, arg){
14781 if(this.fireEvent("beforeload", this, params) !== false){
14783 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14785 var url = this.url;
14786 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14788 url += "&_dc=" + (new Date().getTime());
14790 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14793 cb : "stcCallback"+transId,
14794 scriptId : "stcScript"+transId,
14798 callback : callback,
14804 window[trans.cb] = function(o){
14805 conn.handleResponse(o, trans);
14808 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14810 if(this.autoAbort !== false){
14814 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14816 var script = document.createElement("script");
14817 script.setAttribute("src", url);
14818 script.setAttribute("type", "text/javascript");
14819 script.setAttribute("id", trans.scriptId);
14820 this.head.appendChild(script);
14822 this.trans = trans;
14824 callback.call(scope||this, null, arg, false);
14829 isLoading : function(){
14830 return this.trans ? true : false;
14834 * Abort the current server request.
14836 abort : function(){
14837 if(this.isLoading()){
14838 this.destroyTrans(this.trans);
14843 destroyTrans : function(trans, isLoaded){
14844 this.head.removeChild(document.getElementById(trans.scriptId));
14845 clearTimeout(trans.timeoutId);
14847 window[trans.cb] = undefined;
14849 delete window[trans.cb];
14852 // if hasn't been loaded, wait for load to remove it to prevent script error
14853 window[trans.cb] = function(){
14854 window[trans.cb] = undefined;
14856 delete window[trans.cb];
14863 handleResponse : function(o, trans){
14864 this.trans = false;
14865 this.destroyTrans(trans, true);
14868 result = trans.reader.readRecords(o);
14870 this.fireEvent("loadexception", this, o, trans.arg, e);
14871 trans.callback.call(trans.scope||window, null, trans.arg, false);
14874 this.fireEvent("load", this, o, trans.arg);
14875 trans.callback.call(trans.scope||window, result, trans.arg, true);
14879 handleFailure : function(trans){
14880 this.trans = false;
14881 this.destroyTrans(trans, false);
14882 this.fireEvent("loadexception", this, null, trans.arg);
14883 trans.callback.call(trans.scope||window, null, trans.arg, false);
14887 * Ext JS Library 1.1.1
14888 * Copyright(c) 2006-2007, Ext JS, LLC.
14890 * Originally Released Under LGPL - original licence link has changed is not relivant.
14893 * <script type="text/javascript">
14897 * @class Roo.data.JsonReader
14898 * @extends Roo.data.DataReader
14899 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14900 * based on mappings in a provided Roo.data.Record constructor.
14902 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14903 * in the reply previously.
14908 var RecordDef = Roo.data.Record.create([
14909 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14910 {name: 'occupation'} // This field will use "occupation" as the mapping.
14912 var myReader = new Roo.data.JsonReader({
14913 totalProperty: "results", // The property which contains the total dataset size (optional)
14914 root: "rows", // The property which contains an Array of row objects
14915 id: "id" // The property within each row object that provides an ID for the record (optional)
14919 * This would consume a JSON file like this:
14921 { 'results': 2, 'rows': [
14922 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14923 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14926 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14927 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14928 * paged from the remote server.
14929 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14930 * @cfg {String} root name of the property which contains the Array of row objects.
14931 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14932 * @cfg {Array} fields Array of field definition objects
14934 * Create a new JsonReader
14935 * @param {Object} meta Metadata configuration options
14936 * @param {Object} recordType Either an Array of field definition objects,
14937 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14939 Roo.data.JsonReader = function(meta, recordType){
14942 // set some defaults:
14943 Roo.applyIf(meta, {
14944 totalProperty: 'total',
14945 successProperty : 'success',
14950 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14952 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14954 readerType : 'Json',
14957 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14958 * Used by Store query builder to append _requestMeta to params.
14961 metaFromRemote : false,
14963 * This method is only used by a DataProxy which has retrieved data from a remote server.
14964 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14965 * @return {Object} data A data block which is used by an Roo.data.Store object as
14966 * a cache of Roo.data.Records.
14968 read : function(response){
14969 var json = response.responseText;
14971 var o = /* eval:var:o */ eval("("+json+")");
14973 throw {message: "JsonReader.read: Json object not found"};
14979 this.metaFromRemote = true;
14980 this.meta = o.metaData;
14981 this.recordType = Roo.data.Record.create(o.metaData.fields);
14982 this.onMetaChange(this.meta, this.recordType, o);
14984 return this.readRecords(o);
14987 // private function a store will implement
14988 onMetaChange : function(meta, recordType, o){
14995 simpleAccess: function(obj, subsc) {
15002 getJsonAccessor: function(){
15004 return function(expr) {
15006 return(re.test(expr))
15007 ? new Function("obj", "return obj." + expr)
15012 return Roo.emptyFn;
15017 * Create a data block containing Roo.data.Records from an XML document.
15018 * @param {Object} o An object which contains an Array of row objects in the property specified
15019 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15020 * which contains the total size of the dataset.
15021 * @return {Object} data A data block which is used by an Roo.data.Store object as
15022 * a cache of Roo.data.Records.
15024 readRecords : function(o){
15026 * After any data loads, the raw JSON data is available for further custom processing.
15030 var s = this.meta, Record = this.recordType,
15031 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15033 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15035 if(s.totalProperty) {
15036 this.getTotal = this.getJsonAccessor(s.totalProperty);
15038 if(s.successProperty) {
15039 this.getSuccess = this.getJsonAccessor(s.successProperty);
15041 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15043 var g = this.getJsonAccessor(s.id);
15044 this.getId = function(rec) {
15046 return (r === undefined || r === "") ? null : r;
15049 this.getId = function(){return null;};
15052 for(var jj = 0; jj < fl; jj++){
15054 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15055 this.ef[jj] = this.getJsonAccessor(map);
15059 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15060 if(s.totalProperty){
15061 var vt = parseInt(this.getTotal(o), 10);
15066 if(s.successProperty){
15067 var vs = this.getSuccess(o);
15068 if(vs === false || vs === 'false'){
15073 for(var i = 0; i < c; i++){
15076 var id = this.getId(n);
15077 for(var j = 0; j < fl; j++){
15079 var v = this.ef[j](n);
15081 Roo.log('missing convert for ' + f.name);
15085 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15087 var record = new Record(values, id);
15089 records[i] = record;
15095 totalRecords : totalRecords
15098 // used when loading children.. @see loadDataFromChildren
15099 toLoadData: function(rec)
15101 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15102 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15103 return { data : data, total : data.length };
15108 * Ext JS Library 1.1.1
15109 * Copyright(c) 2006-2007, Ext JS, LLC.
15111 * Originally Released Under LGPL - original licence link has changed is not relivant.
15114 * <script type="text/javascript">
15118 * @class Roo.data.ArrayReader
15119 * @extends Roo.data.DataReader
15120 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15121 * Each element of that Array represents a row of data fields. The
15122 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15123 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15127 var RecordDef = Roo.data.Record.create([
15128 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15129 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15131 var myReader = new Roo.data.ArrayReader({
15132 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15136 * This would consume an Array like this:
15138 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15142 * Create a new JsonReader
15143 * @param {Object} meta Metadata configuration options.
15144 * @param {Object|Array} recordType Either an Array of field definition objects
15146 * @cfg {Array} fields Array of field definition objects
15147 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15148 * as specified to {@link Roo.data.Record#create},
15149 * or an {@link Roo.data.Record} object
15152 * created using {@link Roo.data.Record#create}.
15154 Roo.data.ArrayReader = function(meta, recordType)
15156 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15159 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15162 * Create a data block containing Roo.data.Records from an XML document.
15163 * @param {Object} o An Array of row objects which represents the dataset.
15164 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15165 * a cache of Roo.data.Records.
15167 readRecords : function(o)
15169 var sid = this.meta ? this.meta.id : null;
15170 var recordType = this.recordType, fields = recordType.prototype.fields;
15173 for(var i = 0; i < root.length; i++){
15176 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15177 for(var j = 0, jlen = fields.length; j < jlen; j++){
15178 var f = fields.items[j];
15179 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15180 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15182 values[f.name] = v;
15184 var record = new recordType(values, id);
15186 records[records.length] = record;
15190 totalRecords : records.length
15193 // used when loading children.. @see loadDataFromChildren
15194 toLoadData: function(rec)
15196 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15197 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15208 * @class Roo.bootstrap.ComboBox
15209 * @extends Roo.bootstrap.TriggerField
15210 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15211 * @cfg {Boolean} append (true|false) default false
15212 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15213 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15214 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15215 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15216 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15217 * @cfg {Boolean} animate default true
15218 * @cfg {Boolean} emptyResultText only for touch device
15219 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15220 * @cfg {String} emptyTitle default ''
15221 * @cfg {Number} width fixed with? experimental
15223 * Create a new ComboBox.
15224 * @param {Object} config Configuration options
15226 Roo.bootstrap.ComboBox = function(config){
15227 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15231 * Fires when the dropdown list is expanded
15232 * @param {Roo.bootstrap.ComboBox} combo This combo box
15237 * Fires when the dropdown list is collapsed
15238 * @param {Roo.bootstrap.ComboBox} combo This combo box
15242 * @event beforeselect
15243 * Fires before a list item is selected. Return false to cancel the selection.
15244 * @param {Roo.bootstrap.ComboBox} combo This combo box
15245 * @param {Roo.data.Record} record The data record returned from the underlying store
15246 * @param {Number} index The index of the selected item in the dropdown list
15248 'beforeselect' : true,
15251 * Fires when a list item is selected
15252 * @param {Roo.bootstrap.ComboBox} combo This combo box
15253 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15254 * @param {Number} index The index of the selected item in the dropdown list
15258 * @event beforequery
15259 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15260 * The event object passed has these properties:
15261 * @param {Roo.bootstrap.ComboBox} combo This combo box
15262 * @param {String} query The query
15263 * @param {Boolean} forceAll true to force "all" query
15264 * @param {Boolean} cancel true to cancel the query
15265 * @param {Object} e The query event object
15267 'beforequery': true,
15270 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15271 * @param {Roo.bootstrap.ComboBox} combo This combo box
15276 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15277 * @param {Roo.bootstrap.ComboBox} combo This combo box
15278 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15283 * Fires when the remove value from the combobox array
15284 * @param {Roo.bootstrap.ComboBox} combo This combo box
15288 * @event afterremove
15289 * Fires when the remove value from the combobox array
15290 * @param {Roo.bootstrap.ComboBox} combo This combo box
15292 'afterremove' : true,
15294 * @event specialfilter
15295 * Fires when specialfilter
15296 * @param {Roo.bootstrap.ComboBox} combo This combo box
15298 'specialfilter' : true,
15301 * Fires when tick the element
15302 * @param {Roo.bootstrap.ComboBox} combo This combo box
15306 * @event touchviewdisplay
15307 * Fires when touch view require special display (default is using displayField)
15308 * @param {Roo.bootstrap.ComboBox} combo This combo box
15309 * @param {Object} cfg set html .
15311 'touchviewdisplay' : true
15316 this.tickItems = [];
15318 this.selectedIndex = -1;
15319 if(this.mode == 'local'){
15320 if(config.queryDelay === undefined){
15321 this.queryDelay = 10;
15323 if(config.minChars === undefined){
15329 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15332 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15333 * rendering into an Roo.Editor, defaults to false)
15336 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15337 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15340 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15343 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15344 * the dropdown list (defaults to undefined, with no header element)
15348 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15352 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15354 listWidth: undefined,
15356 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15357 * mode = 'remote' or 'text' if mode = 'local')
15359 displayField: undefined,
15362 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15363 * mode = 'remote' or 'value' if mode = 'local').
15364 * Note: use of a valueField requires the user make a selection
15365 * in order for a value to be mapped.
15367 valueField: undefined,
15369 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15374 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15375 * field's data value (defaults to the underlying DOM element's name)
15377 hiddenName: undefined,
15379 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15383 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15385 selectedClass: 'active',
15388 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15392 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15393 * anchor positions (defaults to 'tl-bl')
15395 listAlign: 'tl-bl?',
15397 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15401 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15402 * query specified by the allQuery config option (defaults to 'query')
15404 triggerAction: 'query',
15406 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15407 * (defaults to 4, does not apply if editable = false)
15411 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15412 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15416 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15417 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15421 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15422 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15426 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15427 * when editable = true (defaults to false)
15429 selectOnFocus:false,
15431 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15433 queryParam: 'query',
15435 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15436 * when mode = 'remote' (defaults to 'Loading...')
15438 loadingText: 'Loading...',
15440 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15444 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15448 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15449 * traditional select (defaults to true)
15453 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15457 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15461 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15462 * listWidth has a higher value)
15466 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15467 * allow the user to set arbitrary text into the field (defaults to false)
15469 forceSelection:false,
15471 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15472 * if typeAhead = true (defaults to 250)
15474 typeAheadDelay : 250,
15476 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15477 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15479 valueNotFoundText : undefined,
15481 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15483 blockFocus : false,
15486 * @cfg {Boolean} disableClear Disable showing of clear button.
15488 disableClear : false,
15490 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15492 alwaysQuery : false,
15495 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15500 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15502 invalidClass : "has-warning",
15505 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15507 validClass : "has-success",
15510 * @cfg {Boolean} specialFilter (true|false) special filter default false
15512 specialFilter : false,
15515 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15517 mobileTouchView : true,
15520 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15522 useNativeIOS : false,
15525 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15527 mobile_restrict_height : false,
15529 ios_options : false,
15541 btnPosition : 'right',
15542 triggerList : true,
15543 showToggleBtn : true,
15545 emptyResultText: 'Empty',
15546 triggerText : 'Select',
15550 // element that contains real text value.. (when hidden is used..)
15552 getAutoCreate : function()
15557 * Render classic select for iso
15560 if(Roo.isIOS && this.useNativeIOS){
15561 cfg = this.getAutoCreateNativeIOS();
15569 if(Roo.isTouch && this.mobileTouchView){
15570 cfg = this.getAutoCreateTouchView();
15577 if(!this.tickable){
15578 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15583 * ComboBox with tickable selections
15586 var align = this.labelAlign || this.parentLabelAlign();
15589 cls : 'form-group roo-combobox-tickable' //input-group
15592 var btn_text_select = '';
15593 var btn_text_done = '';
15594 var btn_text_cancel = '';
15596 if (this.btn_text_show) {
15597 btn_text_select = 'Select';
15598 btn_text_done = 'Done';
15599 btn_text_cancel = 'Cancel';
15604 cls : 'tickable-buttons',
15609 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15610 //html : this.triggerText
15611 html: btn_text_select
15617 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15619 html: btn_text_done
15625 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15627 html: btn_text_cancel
15633 buttons.cn.unshift({
15635 cls: 'roo-select2-search-field-input'
15641 Roo.each(buttons.cn, function(c){
15643 c.cls += ' btn-' + _this.size;
15646 if (_this.disabled) {
15653 style : 'display: contents',
15658 cls: 'form-hidden-field'
15662 cls: 'roo-select2-choices',
15666 cls: 'roo-select2-search-field',
15677 cls: 'roo-select2-container input-group roo-select2-container-multi',
15683 // cls: 'typeahead typeahead-long dropdown-menu',
15684 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15689 if(this.hasFeedback && !this.allowBlank){
15693 cls: 'glyphicon form-control-feedback'
15696 combobox.cn.push(feedback);
15703 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15704 tooltip : 'This field is required'
15706 if (Roo.bootstrap.version == 4) {
15709 style : 'display:none'
15712 if (align ==='left' && this.fieldLabel.length) {
15714 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15721 cls : 'control-label col-form-label',
15722 html : this.fieldLabel
15734 var labelCfg = cfg.cn[1];
15735 var contentCfg = cfg.cn[2];
15738 if(this.indicatorpos == 'right'){
15744 cls : 'control-label col-form-label',
15748 html : this.fieldLabel
15764 labelCfg = cfg.cn[0];
15765 contentCfg = cfg.cn[1];
15769 if(this.labelWidth > 12){
15770 labelCfg.style = "width: " + this.labelWidth + 'px';
15772 if(this.width * 1 > 0){
15773 contentCfg.style = "width: " + this.width + 'px';
15775 if(this.labelWidth < 13 && this.labelmd == 0){
15776 this.labelmd = this.labelWidth;
15779 if(this.labellg > 0){
15780 labelCfg.cls += ' col-lg-' + this.labellg;
15781 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15784 if(this.labelmd > 0){
15785 labelCfg.cls += ' col-md-' + this.labelmd;
15786 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15789 if(this.labelsm > 0){
15790 labelCfg.cls += ' col-sm-' + this.labelsm;
15791 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15794 if(this.labelxs > 0){
15795 labelCfg.cls += ' col-xs-' + this.labelxs;
15796 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15800 } else if ( this.fieldLabel.length) {
15801 // Roo.log(" label");
15806 //cls : 'input-group-addon',
15807 html : this.fieldLabel
15812 if(this.indicatorpos == 'right'){
15816 //cls : 'input-group-addon',
15817 html : this.fieldLabel
15827 // Roo.log(" no label && no align");
15834 ['xs','sm','md','lg'].map(function(size){
15835 if (settings[size]) {
15836 cfg.cls += ' col-' + size + '-' + settings[size];
15844 _initEventsCalled : false,
15847 initEvents: function()
15849 if (this._initEventsCalled) { // as we call render... prevent looping...
15852 this._initEventsCalled = true;
15855 throw "can not find store for combo";
15858 this.indicator = this.indicatorEl();
15860 this.store = Roo.factory(this.store, Roo.data);
15861 this.store.parent = this;
15863 // if we are building from html. then this element is so complex, that we can not really
15864 // use the rendered HTML.
15865 // so we have to trash and replace the previous code.
15866 if (Roo.XComponent.build_from_html) {
15867 // remove this element....
15868 var e = this.el.dom, k=0;
15869 while (e ) { e = e.previousSibling; ++k;}
15874 this.rendered = false;
15876 this.render(this.parent().getChildContainer(true), k);
15879 if(Roo.isIOS && this.useNativeIOS){
15880 this.initIOSView();
15888 if(Roo.isTouch && this.mobileTouchView){
15889 this.initTouchView();
15894 this.initTickableEvents();
15898 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15900 if(this.hiddenName){
15902 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15904 this.hiddenField.dom.value =
15905 this.hiddenValue !== undefined ? this.hiddenValue :
15906 this.value !== undefined ? this.value : '';
15908 // prevent input submission
15909 this.el.dom.removeAttribute('name');
15910 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15915 // this.el.dom.setAttribute('autocomplete', 'off');
15918 var cls = 'x-combo-list';
15920 //this.list = new Roo.Layer({
15921 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15927 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15928 _this.list.setWidth(lw);
15931 this.list.on('mouseover', this.onViewOver, this);
15932 this.list.on('mousemove', this.onViewMove, this);
15933 this.list.on('scroll', this.onViewScroll, this);
15936 this.list.swallowEvent('mousewheel');
15937 this.assetHeight = 0;
15940 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15941 this.assetHeight += this.header.getHeight();
15944 this.innerList = this.list.createChild({cls:cls+'-inner'});
15945 this.innerList.on('mouseover', this.onViewOver, this);
15946 this.innerList.on('mousemove', this.onViewMove, this);
15947 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15949 if(this.allowBlank && !this.pageSize && !this.disableClear){
15950 this.footer = this.list.createChild({cls:cls+'-ft'});
15951 this.pageTb = new Roo.Toolbar(this.footer);
15955 this.footer = this.list.createChild({cls:cls+'-ft'});
15956 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15957 {pageSize: this.pageSize});
15961 if (this.pageTb && this.allowBlank && !this.disableClear) {
15963 this.pageTb.add(new Roo.Toolbar.Fill(), {
15964 cls: 'x-btn-icon x-btn-clear',
15966 handler: function()
15969 _this.clearValue();
15970 _this.onSelect(false, -1);
15975 this.assetHeight += this.footer.getHeight();
15980 this.tpl = Roo.bootstrap.version == 4 ?
15981 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15982 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15985 this.view = new Roo.View(this.list, this.tpl, {
15986 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15988 //this.view.wrapEl.setDisplayed(false);
15989 this.view.on('click', this.onViewClick, this);
15992 this.store.on('beforeload', this.onBeforeLoad, this);
15993 this.store.on('load', this.onLoad, this);
15994 this.store.on('loadexception', this.onLoadException, this);
15996 if(this.resizable){
15997 this.resizer = new Roo.Resizable(this.list, {
15998 pinned:true, handles:'se'
16000 this.resizer.on('resize', function(r, w, h){
16001 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16002 this.listWidth = w;
16003 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16004 this.restrictHeight();
16006 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16009 if(!this.editable){
16010 this.editable = true;
16011 this.setEditable(false);
16016 if (typeof(this.events.add.listeners) != 'undefined') {
16018 this.addicon = this.wrap.createChild(
16019 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16021 this.addicon.on('click', function(e) {
16022 this.fireEvent('add', this);
16025 if (typeof(this.events.edit.listeners) != 'undefined') {
16027 this.editicon = this.wrap.createChild(
16028 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16029 if (this.addicon) {
16030 this.editicon.setStyle('margin-left', '40px');
16032 this.editicon.on('click', function(e) {
16034 // we fire even if inothing is selected..
16035 this.fireEvent('edit', this, this.lastData );
16041 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16042 "up" : function(e){
16043 this.inKeyMode = true;
16047 "down" : function(e){
16048 if(!this.isExpanded()){
16049 this.onTriggerClick();
16051 this.inKeyMode = true;
16056 "enter" : function(e){
16057 // this.onViewClick();
16061 if(this.fireEvent("specialkey", this, e)){
16062 this.onViewClick(false);
16068 "esc" : function(e){
16072 "tab" : function(e){
16075 if(this.fireEvent("specialkey", this, e)){
16076 this.onViewClick(false);
16084 doRelay : function(foo, bar, hname){
16085 if(hname == 'down' || this.scope.isExpanded()){
16086 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16095 this.queryDelay = Math.max(this.queryDelay || 10,
16096 this.mode == 'local' ? 10 : 250);
16099 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16101 if(this.typeAhead){
16102 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16104 if(this.editable !== false){
16105 this.inputEl().on("keyup", this.onKeyUp, this);
16107 if(this.forceSelection){
16108 this.inputEl().on('blur', this.doForce, this);
16112 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16113 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16117 initTickableEvents: function()
16121 if(this.hiddenName){
16123 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16125 this.hiddenField.dom.value =
16126 this.hiddenValue !== undefined ? this.hiddenValue :
16127 this.value !== undefined ? this.value : '';
16129 // prevent input submission
16130 this.el.dom.removeAttribute('name');
16131 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16136 // this.list = this.el.select('ul.dropdown-menu',true).first();
16138 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16139 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16140 if(this.triggerList){
16141 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16144 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16145 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16147 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16148 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16150 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16151 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16153 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16154 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16155 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16158 this.cancelBtn.hide();
16163 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16164 _this.list.setWidth(lw);
16167 this.list.on('mouseover', this.onViewOver, this);
16168 this.list.on('mousemove', this.onViewMove, this);
16170 this.list.on('scroll', this.onViewScroll, this);
16173 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16174 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16177 this.view = new Roo.View(this.list, this.tpl, {
16182 selectedClass: this.selectedClass
16185 //this.view.wrapEl.setDisplayed(false);
16186 this.view.on('click', this.onViewClick, this);
16190 this.store.on('beforeload', this.onBeforeLoad, this);
16191 this.store.on('load', this.onLoad, this);
16192 this.store.on('loadexception', this.onLoadException, this);
16195 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16196 "up" : function(e){
16197 this.inKeyMode = true;
16201 "down" : function(e){
16202 this.inKeyMode = true;
16206 "enter" : function(e){
16207 if(this.fireEvent("specialkey", this, e)){
16208 this.onViewClick(false);
16214 "esc" : function(e){
16215 this.onTickableFooterButtonClick(e, false, false);
16218 "tab" : function(e){
16219 this.fireEvent("specialkey", this, e);
16221 this.onTickableFooterButtonClick(e, false, false);
16228 doRelay : function(e, fn, key){
16229 if(this.scope.isExpanded()){
16230 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16239 this.queryDelay = Math.max(this.queryDelay || 10,
16240 this.mode == 'local' ? 10 : 250);
16243 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16245 if(this.typeAhead){
16246 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16249 if(this.editable !== false){
16250 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16253 this.indicator = this.indicatorEl();
16255 if(this.indicator){
16256 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16257 this.indicator.hide();
16262 onDestroy : function(){
16264 this.view.setStore(null);
16265 this.view.el.removeAllListeners();
16266 this.view.el.remove();
16267 this.view.purgeListeners();
16270 this.list.dom.innerHTML = '';
16274 this.store.un('beforeload', this.onBeforeLoad, this);
16275 this.store.un('load', this.onLoad, this);
16276 this.store.un('loadexception', this.onLoadException, this);
16278 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16282 fireKey : function(e){
16283 if(e.isNavKeyPress() && !this.list.isVisible()){
16284 this.fireEvent("specialkey", this, e);
16289 onResize: function(w, h)
16293 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16295 // if(typeof w != 'number'){
16296 // // we do not handle it!?!?
16299 // var tw = this.trigger.getWidth();
16300 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16301 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16303 // this.inputEl().setWidth( this.adjustWidth('input', x));
16305 // //this.trigger.setStyle('left', x+'px');
16307 // if(this.list && this.listWidth === undefined){
16308 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16309 // this.list.setWidth(lw);
16310 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16318 * Allow or prevent the user from directly editing the field text. If false is passed,
16319 * the user will only be able to select from the items defined in the dropdown list. This method
16320 * is the runtime equivalent of setting the 'editable' config option at config time.
16321 * @param {Boolean} value True to allow the user to directly edit the field text
16323 setEditable : function(value){
16324 if(value == this.editable){
16327 this.editable = value;
16329 this.inputEl().dom.setAttribute('readOnly', true);
16330 this.inputEl().on('mousedown', this.onTriggerClick, this);
16331 this.inputEl().addClass('x-combo-noedit');
16333 this.inputEl().dom.setAttribute('readOnly', false);
16334 this.inputEl().un('mousedown', this.onTriggerClick, this);
16335 this.inputEl().removeClass('x-combo-noedit');
16341 onBeforeLoad : function(combo,opts){
16342 if(!this.hasFocus){
16346 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16348 this.restrictHeight();
16349 this.selectedIndex = -1;
16353 onLoad : function(){
16355 this.hasQuery = false;
16357 if(!this.hasFocus){
16361 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16362 this.loading.hide();
16365 if(this.store.getCount() > 0){
16368 this.restrictHeight();
16369 if(this.lastQuery == this.allQuery){
16370 if(this.editable && !this.tickable){
16371 this.inputEl().dom.select();
16375 !this.selectByValue(this.value, true) &&
16378 !this.store.lastOptions ||
16379 typeof(this.store.lastOptions.add) == 'undefined' ||
16380 this.store.lastOptions.add != true
16383 this.select(0, true);
16386 if(this.autoFocus){
16389 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16390 this.taTask.delay(this.typeAheadDelay);
16394 this.onEmptyResults();
16400 onLoadException : function()
16402 this.hasQuery = false;
16404 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16405 this.loading.hide();
16408 if(this.tickable && this.editable){
16413 // only causes errors at present
16414 //Roo.log(this.store.reader.jsonData);
16415 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16417 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16423 onTypeAhead : function(){
16424 if(this.store.getCount() > 0){
16425 var r = this.store.getAt(0);
16426 var newValue = r.data[this.displayField];
16427 var len = newValue.length;
16428 var selStart = this.getRawValue().length;
16430 if(selStart != len){
16431 this.setRawValue(newValue);
16432 this.selectText(selStart, newValue.length);
16438 onSelect : function(record, index){
16440 if(this.fireEvent('beforeselect', this, record, index) !== false){
16442 this.setFromData(index > -1 ? record.data : false);
16445 this.fireEvent('select', this, record, index);
16450 * Returns the currently selected field value or empty string if no value is set.
16451 * @return {String} value The selected value
16453 getValue : function()
16455 if(Roo.isIOS && this.useNativeIOS){
16456 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16460 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16463 if(this.valueField){
16464 return typeof this.value != 'undefined' ? this.value : '';
16466 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16470 getRawValue : function()
16472 if(Roo.isIOS && this.useNativeIOS){
16473 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16476 var v = this.inputEl().getValue();
16482 * Clears any text/value currently set in the field
16484 clearValue : function(){
16486 if(this.hiddenField){
16487 this.hiddenField.dom.value = '';
16490 this.setRawValue('');
16491 this.lastSelectionText = '';
16492 this.lastData = false;
16494 var close = this.closeTriggerEl();
16505 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16506 * will be displayed in the field. If the value does not match the data value of an existing item,
16507 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16508 * Otherwise the field will be blank (although the value will still be set).
16509 * @param {String} value The value to match
16511 setValue : function(v)
16513 if(Roo.isIOS && this.useNativeIOS){
16514 this.setIOSValue(v);
16524 if(this.valueField){
16525 var r = this.findRecord(this.valueField, v);
16527 text = r.data[this.displayField];
16528 }else if(this.valueNotFoundText !== undefined){
16529 text = this.valueNotFoundText;
16532 this.lastSelectionText = text;
16533 if(this.hiddenField){
16534 this.hiddenField.dom.value = v;
16536 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16539 var close = this.closeTriggerEl();
16542 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16548 * @property {Object} the last set data for the element
16553 * Sets the value of the field based on a object which is related to the record format for the store.
16554 * @param {Object} value the value to set as. or false on reset?
16556 setFromData : function(o){
16563 var dv = ''; // display value
16564 var vv = ''; // value value..
16566 if (this.displayField) {
16567 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16569 // this is an error condition!!!
16570 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16573 if(this.valueField){
16574 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16577 var close = this.closeTriggerEl();
16580 if(dv.length || vv * 1 > 0){
16582 this.blockFocus=true;
16588 if(this.hiddenField){
16589 this.hiddenField.dom.value = vv;
16591 this.lastSelectionText = dv;
16592 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16596 // no hidden field.. - we store the value in 'value', but still display
16597 // display field!!!!
16598 this.lastSelectionText = dv;
16599 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16606 reset : function(){
16607 // overridden so that last data is reset..
16614 this.setValue(this.originalValue);
16615 //this.clearInvalid();
16616 this.lastData = false;
16618 this.view.clearSelections();
16624 findRecord : function(prop, value){
16626 if(this.store.getCount() > 0){
16627 this.store.each(function(r){
16628 if(r.data[prop] == value){
16638 getName: function()
16640 // returns hidden if it's set..
16641 if (!this.rendered) {return ''};
16642 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16646 onViewMove : function(e, t){
16647 this.inKeyMode = false;
16651 onViewOver : function(e, t){
16652 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16655 var item = this.view.findItemFromChild(t);
16658 var index = this.view.indexOf(item);
16659 this.select(index, false);
16664 onViewClick : function(view, doFocus, el, e)
16666 var index = this.view.getSelectedIndexes()[0];
16668 var r = this.store.getAt(index);
16672 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16679 Roo.each(this.tickItems, function(v,k){
16681 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16683 _this.tickItems.splice(k, 1);
16685 if(typeof(e) == 'undefined' && view == false){
16686 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16698 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16699 this.tickItems.push(r.data);
16702 if(typeof(e) == 'undefined' && view == false){
16703 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16710 this.onSelect(r, index);
16712 if(doFocus !== false && !this.blockFocus){
16713 this.inputEl().focus();
16718 restrictHeight : function(){
16719 //this.innerList.dom.style.height = '';
16720 //var inner = this.innerList.dom;
16721 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16722 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16723 //this.list.beginUpdate();
16724 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16725 this.list.alignTo(this.inputEl(), this.listAlign);
16726 this.list.alignTo(this.inputEl(), this.listAlign);
16727 //this.list.endUpdate();
16731 onEmptyResults : function(){
16733 if(this.tickable && this.editable){
16734 this.hasFocus = false;
16735 this.restrictHeight();
16743 * Returns true if the dropdown list is expanded, else false.
16745 isExpanded : function(){
16746 return this.list.isVisible();
16750 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16751 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16752 * @param {String} value The data value of the item to select
16753 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16754 * selected item if it is not currently in view (defaults to true)
16755 * @return {Boolean} True if the value matched an item in the list, else false
16757 selectByValue : function(v, scrollIntoView){
16758 if(v !== undefined && v !== null){
16759 var r = this.findRecord(this.valueField || this.displayField, v);
16761 this.select(this.store.indexOf(r), scrollIntoView);
16769 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16770 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16771 * @param {Number} index The zero-based index of the list item to select
16772 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16773 * selected item if it is not currently in view (defaults to true)
16775 select : function(index, scrollIntoView){
16776 this.selectedIndex = index;
16777 this.view.select(index);
16778 if(scrollIntoView !== false){
16779 var el = this.view.getNode(index);
16781 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16784 this.list.scrollChildIntoView(el, false);
16790 selectNext : function(){
16791 var ct = this.store.getCount();
16793 if(this.selectedIndex == -1){
16795 }else if(this.selectedIndex < ct-1){
16796 this.select(this.selectedIndex+1);
16802 selectPrev : function(){
16803 var ct = this.store.getCount();
16805 if(this.selectedIndex == -1){
16807 }else if(this.selectedIndex != 0){
16808 this.select(this.selectedIndex-1);
16814 onKeyUp : function(e){
16815 if(this.editable !== false && !e.isSpecialKey()){
16816 this.lastKey = e.getKey();
16817 this.dqTask.delay(this.queryDelay);
16822 validateBlur : function(){
16823 return !this.list || !this.list.isVisible();
16827 initQuery : function(){
16829 var v = this.getRawValue();
16831 if(this.tickable && this.editable){
16832 v = this.tickableInputEl().getValue();
16839 doForce : function(){
16840 if(this.inputEl().dom.value.length > 0){
16841 this.inputEl().dom.value =
16842 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16848 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16849 * query allowing the query action to be canceled if needed.
16850 * @param {String} query The SQL query to execute
16851 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16852 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16853 * saved in the current store (defaults to false)
16855 doQuery : function(q, forceAll){
16857 if(q === undefined || q === null){
16862 forceAll: forceAll,
16866 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16871 forceAll = qe.forceAll;
16872 if(forceAll === true || (q.length >= this.minChars)){
16874 this.hasQuery = true;
16876 if(this.lastQuery != q || this.alwaysQuery){
16877 this.lastQuery = q;
16878 if(this.mode == 'local'){
16879 this.selectedIndex = -1;
16881 this.store.clearFilter();
16884 if(this.specialFilter){
16885 this.fireEvent('specialfilter', this);
16890 this.store.filter(this.displayField, q);
16893 this.store.fireEvent("datachanged", this.store);
16900 this.store.baseParams[this.queryParam] = q;
16902 var options = {params : this.getParams(q)};
16905 options.add = true;
16906 options.params.start = this.page * this.pageSize;
16909 this.store.load(options);
16912 * this code will make the page width larger, at the beginning, the list not align correctly,
16913 * we should expand the list on onLoad
16914 * so command out it
16919 this.selectedIndex = -1;
16924 this.loadNext = false;
16928 getParams : function(q){
16930 //p[this.queryParam] = q;
16934 p.limit = this.pageSize;
16940 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16942 collapse : function(){
16943 if(!this.isExpanded()){
16949 this.hasFocus = false;
16953 this.cancelBtn.hide();
16954 this.trigger.show();
16957 this.tickableInputEl().dom.value = '';
16958 this.tickableInputEl().blur();
16963 Roo.get(document).un('mousedown', this.collapseIf, this);
16964 Roo.get(document).un('mousewheel', this.collapseIf, this);
16965 if (!this.editable) {
16966 Roo.get(document).un('keydown', this.listKeyPress, this);
16968 this.fireEvent('collapse', this);
16974 collapseIf : function(e){
16975 var in_combo = e.within(this.el);
16976 var in_list = e.within(this.list);
16977 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16979 if (in_combo || in_list || is_list) {
16980 //e.stopPropagation();
16985 this.onTickableFooterButtonClick(e, false, false);
16993 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16995 expand : function(){
16997 if(this.isExpanded() || !this.hasFocus){
17001 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17002 this.list.setWidth(lw);
17008 this.restrictHeight();
17012 this.tickItems = Roo.apply([], this.item);
17015 this.cancelBtn.show();
17016 this.trigger.hide();
17019 this.tickableInputEl().focus();
17024 Roo.get(document).on('mousedown', this.collapseIf, this);
17025 Roo.get(document).on('mousewheel', this.collapseIf, this);
17026 if (!this.editable) {
17027 Roo.get(document).on('keydown', this.listKeyPress, this);
17030 this.fireEvent('expand', this);
17034 // Implements the default empty TriggerField.onTriggerClick function
17035 onTriggerClick : function(e)
17037 Roo.log('trigger click');
17039 if(this.disabled || !this.triggerList){
17044 this.loadNext = false;
17046 if(this.isExpanded()){
17048 if (!this.blockFocus) {
17049 this.inputEl().focus();
17053 this.hasFocus = true;
17054 if(this.triggerAction == 'all') {
17055 this.doQuery(this.allQuery, true);
17057 this.doQuery(this.getRawValue());
17059 if (!this.blockFocus) {
17060 this.inputEl().focus();
17065 onTickableTriggerClick : function(e)
17072 this.loadNext = false;
17073 this.hasFocus = true;
17075 if(this.triggerAction == 'all') {
17076 this.doQuery(this.allQuery, true);
17078 this.doQuery(this.getRawValue());
17082 onSearchFieldClick : function(e)
17084 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17085 this.onTickableFooterButtonClick(e, false, false);
17089 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17094 this.loadNext = false;
17095 this.hasFocus = true;
17097 if(this.triggerAction == 'all') {
17098 this.doQuery(this.allQuery, true);
17100 this.doQuery(this.getRawValue());
17104 listKeyPress : function(e)
17106 //Roo.log('listkeypress');
17107 // scroll to first matching element based on key pres..
17108 if (e.isSpecialKey()) {
17111 var k = String.fromCharCode(e.getKey()).toUpperCase();
17114 var csel = this.view.getSelectedNodes();
17115 var cselitem = false;
17117 var ix = this.view.indexOf(csel[0]);
17118 cselitem = this.store.getAt(ix);
17119 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17125 this.store.each(function(v) {
17127 // start at existing selection.
17128 if (cselitem.id == v.id) {
17134 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17135 match = this.store.indexOf(v);
17141 if (match === false) {
17142 return true; // no more action?
17145 this.view.select(match);
17146 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17147 sn.scrollIntoView(sn.dom.parentNode, false);
17150 onViewScroll : function(e, t){
17152 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){
17156 this.hasQuery = true;
17158 this.loading = this.list.select('.loading', true).first();
17160 if(this.loading === null){
17161 this.list.createChild({
17163 cls: 'loading roo-select2-more-results roo-select2-active',
17164 html: 'Loading more results...'
17167 this.loading = this.list.select('.loading', true).first();
17169 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17171 this.loading.hide();
17174 this.loading.show();
17179 this.loadNext = true;
17181 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17186 addItem : function(o)
17188 var dv = ''; // display value
17190 if (this.displayField) {
17191 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17193 // this is an error condition!!!
17194 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17201 var choice = this.choices.createChild({
17203 cls: 'roo-select2-search-choice',
17212 cls: 'roo-select2-search-choice-close fa fa-times',
17217 }, this.searchField);
17219 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17221 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17229 this.inputEl().dom.value = '';
17234 onRemoveItem : function(e, _self, o)
17236 e.preventDefault();
17238 this.lastItem = Roo.apply([], this.item);
17240 var index = this.item.indexOf(o.data) * 1;
17243 Roo.log('not this item?!');
17247 this.item.splice(index, 1);
17252 this.fireEvent('remove', this, e);
17258 syncValue : function()
17260 if(!this.item.length){
17267 Roo.each(this.item, function(i){
17268 if(_this.valueField){
17269 value.push(i[_this.valueField]);
17276 this.value = value.join(',');
17278 if(this.hiddenField){
17279 this.hiddenField.dom.value = this.value;
17282 this.store.fireEvent("datachanged", this.store);
17287 clearItem : function()
17289 if(!this.multiple){
17295 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17303 if(this.tickable && !Roo.isTouch){
17304 this.view.refresh();
17308 inputEl: function ()
17310 if(Roo.isIOS && this.useNativeIOS){
17311 return this.el.select('select.roo-ios-select', true).first();
17314 if(Roo.isTouch && this.mobileTouchView){
17315 return this.el.select('input.form-control',true).first();
17319 return this.searchField;
17322 return this.el.select('input.form-control',true).first();
17325 onTickableFooterButtonClick : function(e, btn, el)
17327 e.preventDefault();
17329 this.lastItem = Roo.apply([], this.item);
17331 if(btn && btn.name == 'cancel'){
17332 this.tickItems = Roo.apply([], this.item);
17341 Roo.each(this.tickItems, function(o){
17349 validate : function()
17351 if(this.getVisibilityEl().hasClass('hidden')){
17355 var v = this.getRawValue();
17358 v = this.getValue();
17361 if(this.disabled || this.allowBlank || v.length){
17366 this.markInvalid();
17370 tickableInputEl : function()
17372 if(!this.tickable || !this.editable){
17373 return this.inputEl();
17376 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17380 getAutoCreateTouchView : function()
17385 cls: 'form-group' //input-group
17391 type : this.inputType,
17392 cls : 'form-control x-combo-noedit',
17393 autocomplete: 'new-password',
17394 placeholder : this.placeholder || '',
17399 input.name = this.name;
17403 input.cls += ' input-' + this.size;
17406 if (this.disabled) {
17407 input.disabled = true;
17411 cls : 'roo-combobox-wrap',
17418 inputblock.cls += ' input-group';
17420 inputblock.cn.unshift({
17422 cls : 'input-group-addon input-group-prepend input-group-text',
17427 if(this.removable && !this.multiple){
17428 inputblock.cls += ' roo-removable';
17430 inputblock.cn.push({
17433 cls : 'roo-combo-removable-btn close'
17437 if(this.hasFeedback && !this.allowBlank){
17439 inputblock.cls += ' has-feedback';
17441 inputblock.cn.push({
17443 cls: 'glyphicon form-control-feedback'
17450 inputblock.cls += (this.before) ? '' : ' input-group';
17452 inputblock.cn.push({
17454 cls : 'input-group-addon input-group-append input-group-text',
17460 var ibwrap = inputblock;
17465 cls: 'roo-select2-choices',
17469 cls: 'roo-select2-search-field',
17482 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17487 cls: 'form-hidden-field'
17493 if(!this.multiple && this.showToggleBtn){
17499 if (this.caret != false) {
17502 cls: 'fa fa-' + this.caret
17509 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17511 Roo.bootstrap.version == 3 ? caret : '',
17514 cls: 'combobox-clear',
17528 combobox.cls += ' roo-select2-container-multi';
17531 var align = this.labelAlign || this.parentLabelAlign();
17533 if (align ==='left' && this.fieldLabel.length) {
17538 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17539 tooltip : 'This field is required'
17543 cls : 'control-label col-form-label',
17544 html : this.fieldLabel
17548 cls : 'roo-combobox-wrap ',
17555 var labelCfg = cfg.cn[1];
17556 var contentCfg = cfg.cn[2];
17559 if(this.indicatorpos == 'right'){
17564 cls : 'control-label col-form-label',
17568 html : this.fieldLabel
17572 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17573 tooltip : 'This field is required'
17578 cls : "roo-combobox-wrap ",
17586 labelCfg = cfg.cn[0];
17587 contentCfg = cfg.cn[1];
17592 if(this.labelWidth > 12){
17593 labelCfg.style = "width: " + this.labelWidth + 'px';
17596 if(this.labelWidth < 13 && this.labelmd == 0){
17597 this.labelmd = this.labelWidth;
17600 if(this.labellg > 0){
17601 labelCfg.cls += ' col-lg-' + this.labellg;
17602 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17605 if(this.labelmd > 0){
17606 labelCfg.cls += ' col-md-' + this.labelmd;
17607 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17610 if(this.labelsm > 0){
17611 labelCfg.cls += ' col-sm-' + this.labelsm;
17612 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17615 if(this.labelxs > 0){
17616 labelCfg.cls += ' col-xs-' + this.labelxs;
17617 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17621 } else if ( this.fieldLabel.length) {
17625 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17626 tooltip : 'This field is required'
17630 cls : 'control-label',
17631 html : this.fieldLabel
17642 if(this.indicatorpos == 'right'){
17646 cls : 'control-label',
17647 html : this.fieldLabel,
17651 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17652 tooltip : 'This field is required'
17669 var settings = this;
17671 ['xs','sm','md','lg'].map(function(size){
17672 if (settings[size]) {
17673 cfg.cls += ' col-' + size + '-' + settings[size];
17680 initTouchView : function()
17682 this.renderTouchView();
17684 this.touchViewEl.on('scroll', function(){
17685 this.el.dom.scrollTop = 0;
17688 this.originalValue = this.getValue();
17690 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17692 this.inputEl().on("click", this.showTouchView, this);
17693 if (this.triggerEl) {
17694 this.triggerEl.on("click", this.showTouchView, this);
17698 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17699 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17701 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17703 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17704 this.store.on('load', this.onTouchViewLoad, this);
17705 this.store.on('loadexception', this.onTouchViewLoadException, this);
17707 if(this.hiddenName){
17709 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17711 this.hiddenField.dom.value =
17712 this.hiddenValue !== undefined ? this.hiddenValue :
17713 this.value !== undefined ? this.value : '';
17715 this.el.dom.removeAttribute('name');
17716 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17720 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17721 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17724 if(this.removable && !this.multiple){
17725 var close = this.closeTriggerEl();
17727 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17728 close.on('click', this.removeBtnClick, this, close);
17732 * fix the bug in Safari iOS8
17734 this.inputEl().on("focus", function(e){
17735 document.activeElement.blur();
17738 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17745 renderTouchView : function()
17747 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17748 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17750 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17751 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17753 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17754 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17755 this.touchViewBodyEl.setStyle('overflow', 'auto');
17757 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17758 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17760 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17761 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17765 showTouchView : function()
17771 this.touchViewHeaderEl.hide();
17773 if(this.modalTitle.length){
17774 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17775 this.touchViewHeaderEl.show();
17778 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17779 this.touchViewEl.show();
17781 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17783 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17784 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17786 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17788 if(this.modalTitle.length){
17789 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17792 this.touchViewBodyEl.setHeight(bodyHeight);
17796 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17798 this.touchViewEl.addClass(['in','show']);
17801 if(this._touchViewMask){
17802 Roo.get(document.body).addClass("x-body-masked");
17803 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17804 this._touchViewMask.setStyle('z-index', 10000);
17805 this._touchViewMask.addClass('show');
17808 this.doTouchViewQuery();
17812 hideTouchView : function()
17814 this.touchViewEl.removeClass(['in','show']);
17818 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17820 this.touchViewEl.setStyle('display', 'none');
17823 if(this._touchViewMask){
17824 this._touchViewMask.removeClass('show');
17825 Roo.get(document.body).removeClass("x-body-masked");
17829 setTouchViewValue : function()
17836 Roo.each(this.tickItems, function(o){
17841 this.hideTouchView();
17844 doTouchViewQuery : function()
17853 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17857 if(!this.alwaysQuery || this.mode == 'local'){
17858 this.onTouchViewLoad();
17865 onTouchViewBeforeLoad : function(combo,opts)
17871 onTouchViewLoad : function()
17873 if(this.store.getCount() < 1){
17874 this.onTouchViewEmptyResults();
17878 this.clearTouchView();
17880 var rawValue = this.getRawValue();
17882 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17884 this.tickItems = [];
17886 this.store.data.each(function(d, rowIndex){
17887 var row = this.touchViewListGroup.createChild(template);
17889 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17890 row.addClass(d.data.cls);
17893 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17896 html : d.data[this.displayField]
17899 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17900 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17903 row.removeClass('selected');
17904 if(!this.multiple && this.valueField &&
17905 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17908 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17909 row.addClass('selected');
17912 if(this.multiple && this.valueField &&
17913 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17917 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17918 this.tickItems.push(d.data);
17921 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17925 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17927 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17929 if(this.modalTitle.length){
17930 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17933 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17935 if(this.mobile_restrict_height && listHeight < bodyHeight){
17936 this.touchViewBodyEl.setHeight(listHeight);
17941 if(firstChecked && listHeight > bodyHeight){
17942 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17947 onTouchViewLoadException : function()
17949 this.hideTouchView();
17952 onTouchViewEmptyResults : function()
17954 this.clearTouchView();
17956 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17958 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17962 clearTouchView : function()
17964 this.touchViewListGroup.dom.innerHTML = '';
17967 onTouchViewClick : function(e, el, o)
17969 e.preventDefault();
17972 var rowIndex = o.rowIndex;
17974 var r = this.store.getAt(rowIndex);
17976 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17978 if(!this.multiple){
17979 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17980 c.dom.removeAttribute('checked');
17983 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17985 this.setFromData(r.data);
17987 var close = this.closeTriggerEl();
17993 this.hideTouchView();
17995 this.fireEvent('select', this, r, rowIndex);
18000 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18001 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18002 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18006 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18007 this.addItem(r.data);
18008 this.tickItems.push(r.data);
18012 getAutoCreateNativeIOS : function()
18015 cls: 'form-group' //input-group,
18020 cls : 'roo-ios-select'
18024 combobox.name = this.name;
18027 if (this.disabled) {
18028 combobox.disabled = true;
18031 var settings = this;
18033 ['xs','sm','md','lg'].map(function(size){
18034 if (settings[size]) {
18035 cfg.cls += ' col-' + size + '-' + settings[size];
18045 initIOSView : function()
18047 this.store.on('load', this.onIOSViewLoad, this);
18052 onIOSViewLoad : function()
18054 if(this.store.getCount() < 1){
18058 this.clearIOSView();
18060 if(this.allowBlank) {
18062 var default_text = '-- SELECT --';
18064 if(this.placeholder.length){
18065 default_text = this.placeholder;
18068 if(this.emptyTitle.length){
18069 default_text += ' - ' + this.emptyTitle + ' -';
18072 var opt = this.inputEl().createChild({
18075 html : default_text
18079 o[this.valueField] = 0;
18080 o[this.displayField] = default_text;
18082 this.ios_options.push({
18089 this.store.data.each(function(d, rowIndex){
18093 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18094 html = d.data[this.displayField];
18099 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18100 value = d.data[this.valueField];
18109 if(this.value == d.data[this.valueField]){
18110 option['selected'] = true;
18113 var opt = this.inputEl().createChild(option);
18115 this.ios_options.push({
18122 this.inputEl().on('change', function(){
18123 this.fireEvent('select', this);
18128 clearIOSView: function()
18130 this.inputEl().dom.innerHTML = '';
18132 this.ios_options = [];
18135 setIOSValue: function(v)
18139 if(!this.ios_options){
18143 Roo.each(this.ios_options, function(opts){
18145 opts.el.dom.removeAttribute('selected');
18147 if(opts.data[this.valueField] != v){
18151 opts.el.dom.setAttribute('selected', true);
18157 * @cfg {Boolean} grow
18161 * @cfg {Number} growMin
18165 * @cfg {Number} growMax
18174 Roo.apply(Roo.bootstrap.ComboBox, {
18178 cls: 'modal-header',
18200 cls: 'list-group-item',
18204 cls: 'roo-combobox-list-group-item-value'
18208 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18222 listItemCheckbox : {
18224 cls: 'list-group-item',
18228 cls: 'roo-combobox-list-group-item-value'
18232 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18248 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18253 cls: 'modal-footer',
18261 cls: 'col-xs-6 text-left',
18264 cls: 'btn btn-danger roo-touch-view-cancel',
18270 cls: 'col-xs-6 text-right',
18273 cls: 'btn btn-success roo-touch-view-ok',
18284 Roo.apply(Roo.bootstrap.ComboBox, {
18286 touchViewTemplate : {
18288 cls: 'modal fade roo-combobox-touch-view',
18292 cls: 'modal-dialog',
18293 style : 'position:fixed', // we have to fix position....
18297 cls: 'modal-content',
18299 Roo.bootstrap.ComboBox.header,
18300 Roo.bootstrap.ComboBox.body,
18301 Roo.bootstrap.ComboBox.footer
18310 * Ext JS Library 1.1.1
18311 * Copyright(c) 2006-2007, Ext JS, LLC.
18313 * Originally Released Under LGPL - original licence link has changed is not relivant.
18316 * <script type="text/javascript">
18321 * @extends Roo.util.Observable
18322 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18323 * This class also supports single and multi selection modes. <br>
18324 * Create a data model bound view:
18326 var store = new Roo.data.Store(...);
18328 var view = new Roo.View({
18330 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18332 singleSelect: true,
18333 selectedClass: "ydataview-selected",
18337 // listen for node click?
18338 view.on("click", function(vw, index, node, e){
18339 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18343 dataModel.load("foobar.xml");
18345 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18347 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18348 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18350 * Note: old style constructor is still suported (container, template, config)
18353 * Create a new View
18354 * @param {Object} config The config object
18357 Roo.View = function(config, depreciated_tpl, depreciated_config){
18359 this.parent = false;
18361 if (typeof(depreciated_tpl) == 'undefined') {
18362 // new way.. - universal constructor.
18363 Roo.apply(this, config);
18364 this.el = Roo.get(this.el);
18367 this.el = Roo.get(config);
18368 this.tpl = depreciated_tpl;
18369 Roo.apply(this, depreciated_config);
18371 this.wrapEl = this.el.wrap().wrap();
18372 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18375 if(typeof(this.tpl) == "string"){
18376 this.tpl = new Roo.Template(this.tpl);
18378 // support xtype ctors..
18379 this.tpl = new Roo.factory(this.tpl, Roo);
18383 this.tpl.compile();
18388 * @event beforeclick
18389 * Fires before a click is processed. Returns false to cancel the default action.
18390 * @param {Roo.View} this
18391 * @param {Number} index The index of the target node
18392 * @param {HTMLElement} node The target node
18393 * @param {Roo.EventObject} e The raw event object
18395 "beforeclick" : true,
18398 * Fires when a template node is clicked.
18399 * @param {Roo.View} this
18400 * @param {Number} index The index of the target node
18401 * @param {HTMLElement} node The target node
18402 * @param {Roo.EventObject} e The raw event object
18407 * Fires when a template node is double clicked.
18408 * @param {Roo.View} this
18409 * @param {Number} index The index of the target node
18410 * @param {HTMLElement} node The target node
18411 * @param {Roo.EventObject} e The raw event object
18415 * @event contextmenu
18416 * Fires when a template node is right clicked.
18417 * @param {Roo.View} this
18418 * @param {Number} index The index of the target node
18419 * @param {HTMLElement} node The target node
18420 * @param {Roo.EventObject} e The raw event object
18422 "contextmenu" : true,
18424 * @event selectionchange
18425 * Fires when the selected nodes change.
18426 * @param {Roo.View} this
18427 * @param {Array} selections Array of the selected nodes
18429 "selectionchange" : true,
18432 * @event beforeselect
18433 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18434 * @param {Roo.View} this
18435 * @param {HTMLElement} node The node to be selected
18436 * @param {Array} selections Array of currently selected nodes
18438 "beforeselect" : true,
18440 * @event preparedata
18441 * Fires on every row to render, to allow you to change the data.
18442 * @param {Roo.View} this
18443 * @param {Object} data to be rendered (change this)
18445 "preparedata" : true
18453 "click": this.onClick,
18454 "dblclick": this.onDblClick,
18455 "contextmenu": this.onContextMenu,
18459 this.selections = [];
18461 this.cmp = new Roo.CompositeElementLite([]);
18463 this.store = Roo.factory(this.store, Roo.data);
18464 this.setStore(this.store, true);
18467 if ( this.footer && this.footer.xtype) {
18469 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18471 this.footer.dataSource = this.store;
18472 this.footer.container = fctr;
18473 this.footer = Roo.factory(this.footer, Roo);
18474 fctr.insertFirst(this.el);
18476 // this is a bit insane - as the paging toolbar seems to detach the el..
18477 // dom.parentNode.parentNode.parentNode
18478 // they get detached?
18482 Roo.View.superclass.constructor.call(this);
18487 Roo.extend(Roo.View, Roo.util.Observable, {
18490 * @cfg {Roo.data.Store} store Data store to load data from.
18495 * @cfg {String|Roo.Element} el The container element.
18500 * @cfg {String|Roo.Template} tpl The template used by this View
18504 * @cfg {String} dataName the named area of the template to use as the data area
18505 * Works with domtemplates roo-name="name"
18509 * @cfg {String} selectedClass The css class to add to selected nodes
18511 selectedClass : "x-view-selected",
18513 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18518 * @cfg {String} text to display on mask (default Loading)
18522 * @cfg {Boolean} multiSelect Allow multiple selection
18524 multiSelect : false,
18526 * @cfg {Boolean} singleSelect Allow single selection
18528 singleSelect: false,
18531 * @cfg {Boolean} toggleSelect - selecting
18533 toggleSelect : false,
18536 * @cfg {Boolean} tickable - selecting
18541 * Returns the element this view is bound to.
18542 * @return {Roo.Element}
18544 getEl : function(){
18545 return this.wrapEl;
18551 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18553 refresh : function(){
18554 //Roo.log('refresh');
18557 // if we are using something like 'domtemplate', then
18558 // the what gets used is:
18559 // t.applySubtemplate(NAME, data, wrapping data..)
18560 // the outer template then get' applied with
18561 // the store 'extra data'
18562 // and the body get's added to the
18563 // roo-name="data" node?
18564 // <span class='roo-tpl-{name}'></span> ?????
18568 this.clearSelections();
18569 this.el.update("");
18571 var records = this.store.getRange();
18572 if(records.length < 1) {
18574 // is this valid?? = should it render a template??
18576 this.el.update(this.emptyText);
18580 if (this.dataName) {
18581 this.el.update(t.apply(this.store.meta)); //????
18582 el = this.el.child('.roo-tpl-' + this.dataName);
18585 for(var i = 0, len = records.length; i < len; i++){
18586 var data = this.prepareData(records[i].data, i, records[i]);
18587 this.fireEvent("preparedata", this, data, i, records[i]);
18589 var d = Roo.apply({}, data);
18592 Roo.apply(d, {'roo-id' : Roo.id()});
18596 Roo.each(this.parent.item, function(item){
18597 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18600 Roo.apply(d, {'roo-data-checked' : 'checked'});
18604 html[html.length] = Roo.util.Format.trim(
18606 t.applySubtemplate(this.dataName, d, this.store.meta) :
18613 el.update(html.join(""));
18614 this.nodes = el.dom.childNodes;
18615 this.updateIndexes(0);
18620 * Function to override to reformat the data that is sent to
18621 * the template for each node.
18622 * DEPRICATED - use the preparedata event handler.
18623 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18624 * a JSON object for an UpdateManager bound view).
18626 prepareData : function(data, index, record)
18628 this.fireEvent("preparedata", this, data, index, record);
18632 onUpdate : function(ds, record){
18633 // Roo.log('on update');
18634 this.clearSelections();
18635 var index = this.store.indexOf(record);
18636 var n = this.nodes[index];
18637 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18638 n.parentNode.removeChild(n);
18639 this.updateIndexes(index, index);
18645 onAdd : function(ds, records, index)
18647 //Roo.log(['on Add', ds, records, index] );
18648 this.clearSelections();
18649 if(this.nodes.length == 0){
18653 var n = this.nodes[index];
18654 for(var i = 0, len = records.length; i < len; i++){
18655 var d = this.prepareData(records[i].data, i, records[i]);
18657 this.tpl.insertBefore(n, d);
18660 this.tpl.append(this.el, d);
18663 this.updateIndexes(index);
18666 onRemove : function(ds, record, index){
18667 // Roo.log('onRemove');
18668 this.clearSelections();
18669 var el = this.dataName ?
18670 this.el.child('.roo-tpl-' + this.dataName) :
18673 el.dom.removeChild(this.nodes[index]);
18674 this.updateIndexes(index);
18678 * Refresh an individual node.
18679 * @param {Number} index
18681 refreshNode : function(index){
18682 this.onUpdate(this.store, this.store.getAt(index));
18685 updateIndexes : function(startIndex, endIndex){
18686 var ns = this.nodes;
18687 startIndex = startIndex || 0;
18688 endIndex = endIndex || ns.length - 1;
18689 for(var i = startIndex; i <= endIndex; i++){
18690 ns[i].nodeIndex = i;
18695 * Changes the data store this view uses and refresh the view.
18696 * @param {Store} store
18698 setStore : function(store, initial){
18699 if(!initial && this.store){
18700 this.store.un("datachanged", this.refresh);
18701 this.store.un("add", this.onAdd);
18702 this.store.un("remove", this.onRemove);
18703 this.store.un("update", this.onUpdate);
18704 this.store.un("clear", this.refresh);
18705 this.store.un("beforeload", this.onBeforeLoad);
18706 this.store.un("load", this.onLoad);
18707 this.store.un("loadexception", this.onLoad);
18711 store.on("datachanged", this.refresh, this);
18712 store.on("add", this.onAdd, this);
18713 store.on("remove", this.onRemove, this);
18714 store.on("update", this.onUpdate, this);
18715 store.on("clear", this.refresh, this);
18716 store.on("beforeload", this.onBeforeLoad, this);
18717 store.on("load", this.onLoad, this);
18718 store.on("loadexception", this.onLoad, this);
18726 * onbeforeLoad - masks the loading area.
18729 onBeforeLoad : function(store,opts)
18731 //Roo.log('onBeforeLoad');
18733 this.el.update("");
18735 this.el.mask(this.mask ? this.mask : "Loading" );
18737 onLoad : function ()
18744 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18745 * @param {HTMLElement} node
18746 * @return {HTMLElement} The template node
18748 findItemFromChild : function(node){
18749 var el = this.dataName ?
18750 this.el.child('.roo-tpl-' + this.dataName,true) :
18753 if(!node || node.parentNode == el){
18756 var p = node.parentNode;
18757 while(p && p != el){
18758 if(p.parentNode == el){
18767 onClick : function(e){
18768 var item = this.findItemFromChild(e.getTarget());
18770 var index = this.indexOf(item);
18771 if(this.onItemClick(item, index, e) !== false){
18772 this.fireEvent("click", this, index, item, e);
18775 this.clearSelections();
18780 onContextMenu : function(e){
18781 var item = this.findItemFromChild(e.getTarget());
18783 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18788 onDblClick : function(e){
18789 var item = this.findItemFromChild(e.getTarget());
18791 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18795 onItemClick : function(item, index, e)
18797 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18800 if (this.toggleSelect) {
18801 var m = this.isSelected(item) ? 'unselect' : 'select';
18804 _t[m](item, true, false);
18807 if(this.multiSelect || this.singleSelect){
18808 if(this.multiSelect && e.shiftKey && this.lastSelection){
18809 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18811 this.select(item, this.multiSelect && e.ctrlKey);
18812 this.lastSelection = item;
18815 if(!this.tickable){
18816 e.preventDefault();
18824 * Get the number of selected nodes.
18827 getSelectionCount : function(){
18828 return this.selections.length;
18832 * Get the currently selected nodes.
18833 * @return {Array} An array of HTMLElements
18835 getSelectedNodes : function(){
18836 return this.selections;
18840 * Get the indexes of the selected nodes.
18843 getSelectedIndexes : function(){
18844 var indexes = [], s = this.selections;
18845 for(var i = 0, len = s.length; i < len; i++){
18846 indexes.push(s[i].nodeIndex);
18852 * Clear all selections
18853 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18855 clearSelections : function(suppressEvent){
18856 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18857 this.cmp.elements = this.selections;
18858 this.cmp.removeClass(this.selectedClass);
18859 this.selections = [];
18860 if(!suppressEvent){
18861 this.fireEvent("selectionchange", this, this.selections);
18867 * Returns true if the passed node is selected
18868 * @param {HTMLElement/Number} node The node or node index
18869 * @return {Boolean}
18871 isSelected : function(node){
18872 var s = this.selections;
18876 node = this.getNode(node);
18877 return s.indexOf(node) !== -1;
18882 * @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
18883 * @param {Boolean} keepExisting (optional) true to keep existing selections
18884 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18886 select : function(nodeInfo, keepExisting, suppressEvent){
18887 if(nodeInfo instanceof Array){
18889 this.clearSelections(true);
18891 for(var i = 0, len = nodeInfo.length; i < len; i++){
18892 this.select(nodeInfo[i], true, true);
18896 var node = this.getNode(nodeInfo);
18897 if(!node || this.isSelected(node)){
18898 return; // already selected.
18901 this.clearSelections(true);
18904 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18905 Roo.fly(node).addClass(this.selectedClass);
18906 this.selections.push(node);
18907 if(!suppressEvent){
18908 this.fireEvent("selectionchange", this, this.selections);
18916 * @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
18917 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18918 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18920 unselect : function(nodeInfo, keepExisting, suppressEvent)
18922 if(nodeInfo instanceof Array){
18923 Roo.each(this.selections, function(s) {
18924 this.unselect(s, nodeInfo);
18928 var node = this.getNode(nodeInfo);
18929 if(!node || !this.isSelected(node)){
18930 //Roo.log("not selected");
18931 return; // not selected.
18935 Roo.each(this.selections, function(s) {
18937 Roo.fly(node).removeClass(this.selectedClass);
18944 this.selections= ns;
18945 this.fireEvent("selectionchange", this, this.selections);
18949 * Gets a template node.
18950 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18951 * @return {HTMLElement} The node or null if it wasn't found
18953 getNode : function(nodeInfo){
18954 if(typeof nodeInfo == "string"){
18955 return document.getElementById(nodeInfo);
18956 }else if(typeof nodeInfo == "number"){
18957 return this.nodes[nodeInfo];
18963 * Gets a range template nodes.
18964 * @param {Number} startIndex
18965 * @param {Number} endIndex
18966 * @return {Array} An array of nodes
18968 getNodes : function(start, end){
18969 var ns = this.nodes;
18970 start = start || 0;
18971 end = typeof end == "undefined" ? ns.length - 1 : end;
18974 for(var i = start; i <= end; i++){
18978 for(var i = start; i >= end; i--){
18986 * Finds the index of the passed node
18987 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18988 * @return {Number} The index of the node or -1
18990 indexOf : function(node){
18991 node = this.getNode(node);
18992 if(typeof node.nodeIndex == "number"){
18993 return node.nodeIndex;
18995 var ns = this.nodes;
18996 for(var i = 0, len = ns.length; i < len; i++){
19007 * based on jquery fullcalendar
19011 Roo.bootstrap = Roo.bootstrap || {};
19013 * @class Roo.bootstrap.Calendar
19014 * @extends Roo.bootstrap.Component
19015 * Bootstrap Calendar class
19016 * @cfg {Boolean} loadMask (true|false) default false
19017 * @cfg {Object} header generate the user specific header of the calendar, default false
19020 * Create a new Container
19021 * @param {Object} config The config object
19026 Roo.bootstrap.Calendar = function(config){
19027 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19031 * Fires when a date is selected
19032 * @param {DatePicker} this
19033 * @param {Date} date The selected date
19037 * @event monthchange
19038 * Fires when the displayed month changes
19039 * @param {DatePicker} this
19040 * @param {Date} date The selected month
19042 'monthchange': true,
19044 * @event evententer
19045 * Fires when mouse over an event
19046 * @param {Calendar} this
19047 * @param {event} Event
19049 'evententer': true,
19051 * @event eventleave
19052 * Fires when the mouse leaves an
19053 * @param {Calendar} this
19056 'eventleave': true,
19058 * @event eventclick
19059 * Fires when the mouse click an
19060 * @param {Calendar} this
19069 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19072 * @cfg {Number} startDay
19073 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19081 getAutoCreate : function(){
19084 var fc_button = function(name, corner, style, content ) {
19085 return Roo.apply({},{
19087 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19089 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19092 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19103 style : 'width:100%',
19110 cls : 'fc-header-left',
19112 fc_button('prev', 'left', 'arrow', '‹' ),
19113 fc_button('next', 'right', 'arrow', '›' ),
19114 { tag: 'span', cls: 'fc-header-space' },
19115 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19123 cls : 'fc-header-center',
19127 cls: 'fc-header-title',
19130 html : 'month / year'
19138 cls : 'fc-header-right',
19140 /* fc_button('month', 'left', '', 'month' ),
19141 fc_button('week', '', '', 'week' ),
19142 fc_button('day', 'right', '', 'day' )
19154 header = this.header;
19157 var cal_heads = function() {
19159 // fixme - handle this.
19161 for (var i =0; i < Date.dayNames.length; i++) {
19162 var d = Date.dayNames[i];
19165 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19166 html : d.substring(0,3)
19170 ret[0].cls += ' fc-first';
19171 ret[6].cls += ' fc-last';
19174 var cal_cell = function(n) {
19177 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19182 cls: 'fc-day-number',
19186 cls: 'fc-day-content',
19190 style: 'position: relative;' // height: 17px;
19202 var cal_rows = function() {
19205 for (var r = 0; r < 6; r++) {
19212 for (var i =0; i < Date.dayNames.length; i++) {
19213 var d = Date.dayNames[i];
19214 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19217 row.cn[0].cls+=' fc-first';
19218 row.cn[0].cn[0].style = 'min-height:90px';
19219 row.cn[6].cls+=' fc-last';
19223 ret[0].cls += ' fc-first';
19224 ret[4].cls += ' fc-prev-last';
19225 ret[5].cls += ' fc-last';
19232 cls: 'fc-border-separate',
19233 style : 'width:100%',
19241 cls : 'fc-first fc-last',
19259 cls : 'fc-content',
19260 style : "position: relative;",
19263 cls : 'fc-view fc-view-month fc-grid',
19264 style : 'position: relative',
19265 unselectable : 'on',
19268 cls : 'fc-event-container',
19269 style : 'position:absolute;z-index:8;top:0;left:0;'
19287 initEvents : function()
19290 throw "can not find store for calendar";
19296 style: "text-align:center",
19300 style: "background-color:white;width:50%;margin:250 auto",
19304 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19315 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19317 var size = this.el.select('.fc-content', true).first().getSize();
19318 this.maskEl.setSize(size.width, size.height);
19319 this.maskEl.enableDisplayMode("block");
19320 if(!this.loadMask){
19321 this.maskEl.hide();
19324 this.store = Roo.factory(this.store, Roo.data);
19325 this.store.on('load', this.onLoad, this);
19326 this.store.on('beforeload', this.onBeforeLoad, this);
19330 this.cells = this.el.select('.fc-day',true);
19331 //Roo.log(this.cells);
19332 this.textNodes = this.el.query('.fc-day-number');
19333 this.cells.addClassOnOver('fc-state-hover');
19335 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19336 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19337 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19338 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19340 this.on('monthchange', this.onMonthChange, this);
19342 this.update(new Date().clearTime());
19345 resize : function() {
19346 var sz = this.el.getSize();
19348 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19349 this.el.select('.fc-day-content div',true).setHeight(34);
19354 showPrevMonth : function(e){
19355 this.update(this.activeDate.add("mo", -1));
19357 showToday : function(e){
19358 this.update(new Date().clearTime());
19361 showNextMonth : function(e){
19362 this.update(this.activeDate.add("mo", 1));
19366 showPrevYear : function(){
19367 this.update(this.activeDate.add("y", -1));
19371 showNextYear : function(){
19372 this.update(this.activeDate.add("y", 1));
19377 update : function(date)
19379 var vd = this.activeDate;
19380 this.activeDate = date;
19381 // if(vd && this.el){
19382 // var t = date.getTime();
19383 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19384 // Roo.log('using add remove');
19386 // this.fireEvent('monthchange', this, date);
19388 // this.cells.removeClass("fc-state-highlight");
19389 // this.cells.each(function(c){
19390 // if(c.dateValue == t){
19391 // c.addClass("fc-state-highlight");
19392 // setTimeout(function(){
19393 // try{c.dom.firstChild.focus();}catch(e){}
19403 var days = date.getDaysInMonth();
19405 var firstOfMonth = date.getFirstDateOfMonth();
19406 var startingPos = firstOfMonth.getDay()-this.startDay;
19408 if(startingPos < this.startDay){
19412 var pm = date.add(Date.MONTH, -1);
19413 var prevStart = pm.getDaysInMonth()-startingPos;
19415 this.cells = this.el.select('.fc-day',true);
19416 this.textNodes = this.el.query('.fc-day-number');
19417 this.cells.addClassOnOver('fc-state-hover');
19419 var cells = this.cells.elements;
19420 var textEls = this.textNodes;
19422 Roo.each(cells, function(cell){
19423 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19426 days += startingPos;
19428 // convert everything to numbers so it's fast
19429 var day = 86400000;
19430 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19433 //Roo.log(prevStart);
19435 var today = new Date().clearTime().getTime();
19436 var sel = date.clearTime().getTime();
19437 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19438 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19439 var ddMatch = this.disabledDatesRE;
19440 var ddText = this.disabledDatesText;
19441 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19442 var ddaysText = this.disabledDaysText;
19443 var format = this.format;
19445 var setCellClass = function(cal, cell){
19449 //Roo.log('set Cell Class');
19451 var t = d.getTime();
19455 cell.dateValue = t;
19457 cell.className += " fc-today";
19458 cell.className += " fc-state-highlight";
19459 cell.title = cal.todayText;
19462 // disable highlight in other month..
19463 //cell.className += " fc-state-highlight";
19468 cell.className = " fc-state-disabled";
19469 cell.title = cal.minText;
19473 cell.className = " fc-state-disabled";
19474 cell.title = cal.maxText;
19478 if(ddays.indexOf(d.getDay()) != -1){
19479 cell.title = ddaysText;
19480 cell.className = " fc-state-disabled";
19483 if(ddMatch && format){
19484 var fvalue = d.dateFormat(format);
19485 if(ddMatch.test(fvalue)){
19486 cell.title = ddText.replace("%0", fvalue);
19487 cell.className = " fc-state-disabled";
19491 if (!cell.initialClassName) {
19492 cell.initialClassName = cell.dom.className;
19495 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19500 for(; i < startingPos; i++) {
19501 textEls[i].innerHTML = (++prevStart);
19502 d.setDate(d.getDate()+1);
19504 cells[i].className = "fc-past fc-other-month";
19505 setCellClass(this, cells[i]);
19510 for(; i < days; i++){
19511 intDay = i - startingPos + 1;
19512 textEls[i].innerHTML = (intDay);
19513 d.setDate(d.getDate()+1);
19515 cells[i].className = ''; // "x-date-active";
19516 setCellClass(this, cells[i]);
19520 for(; i < 42; i++) {
19521 textEls[i].innerHTML = (++extraDays);
19522 d.setDate(d.getDate()+1);
19524 cells[i].className = "fc-future fc-other-month";
19525 setCellClass(this, cells[i]);
19528 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19530 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19532 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19533 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19535 if(totalRows != 6){
19536 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19537 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19540 this.fireEvent('monthchange', this, date);
19544 if(!this.internalRender){
19545 var main = this.el.dom.firstChild;
19546 var w = main.offsetWidth;
19547 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19548 Roo.fly(main).setWidth(w);
19549 this.internalRender = true;
19550 // opera does not respect the auto grow header center column
19551 // then, after it gets a width opera refuses to recalculate
19552 // without a second pass
19553 if(Roo.isOpera && !this.secondPass){
19554 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19555 this.secondPass = true;
19556 this.update.defer(10, this, [date]);
19563 findCell : function(dt) {
19564 dt = dt.clearTime().getTime();
19566 this.cells.each(function(c){
19567 //Roo.log("check " +c.dateValue + '?=' + dt);
19568 if(c.dateValue == dt){
19578 findCells : function(ev) {
19579 var s = ev.start.clone().clearTime().getTime();
19581 var e= ev.end.clone().clearTime().getTime();
19584 this.cells.each(function(c){
19585 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19587 if(c.dateValue > e){
19590 if(c.dateValue < s){
19599 // findBestRow: function(cells)
19603 // for (var i =0 ; i < cells.length;i++) {
19604 // ret = Math.max(cells[i].rows || 0,ret);
19611 addItem : function(ev)
19613 // look for vertical location slot in
19614 var cells = this.findCells(ev);
19616 // ev.row = this.findBestRow(cells);
19618 // work out the location.
19622 for(var i =0; i < cells.length; i++) {
19624 cells[i].row = cells[0].row;
19627 cells[i].row = cells[i].row + 1;
19637 if (crow.start.getY() == cells[i].getY()) {
19639 crow.end = cells[i];
19656 cells[0].events.push(ev);
19658 this.calevents.push(ev);
19661 clearEvents: function() {
19663 if(!this.calevents){
19667 Roo.each(this.cells.elements, function(c){
19673 Roo.each(this.calevents, function(e) {
19674 Roo.each(e.els, function(el) {
19675 el.un('mouseenter' ,this.onEventEnter, this);
19676 el.un('mouseleave' ,this.onEventLeave, this);
19681 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19687 renderEvents: function()
19691 this.cells.each(function(c) {
19700 if(c.row != c.events.length){
19701 r = 4 - (4 - (c.row - c.events.length));
19704 c.events = ev.slice(0, r);
19705 c.more = ev.slice(r);
19707 if(c.more.length && c.more.length == 1){
19708 c.events.push(c.more.pop());
19711 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19715 this.cells.each(function(c) {
19717 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19720 for (var e = 0; e < c.events.length; e++){
19721 var ev = c.events[e];
19722 var rows = ev.rows;
19724 for(var i = 0; i < rows.length; i++) {
19726 // how many rows should it span..
19729 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19730 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19732 unselectable : "on",
19735 cls: 'fc-event-inner',
19739 // cls: 'fc-event-time',
19740 // html : cells.length > 1 ? '' : ev.time
19744 cls: 'fc-event-title',
19745 html : String.format('{0}', ev.title)
19752 cls: 'ui-resizable-handle ui-resizable-e',
19753 html : '  '
19760 cfg.cls += ' fc-event-start';
19762 if ((i+1) == rows.length) {
19763 cfg.cls += ' fc-event-end';
19766 var ctr = _this.el.select('.fc-event-container',true).first();
19767 var cg = ctr.createChild(cfg);
19769 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19770 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19772 var r = (c.more.length) ? 1 : 0;
19773 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19774 cg.setWidth(ebox.right - sbox.x -2);
19776 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19777 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19778 cg.on('click', _this.onEventClick, _this, ev);
19789 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19790 style : 'position: absolute',
19791 unselectable : "on",
19794 cls: 'fc-event-inner',
19798 cls: 'fc-event-title',
19806 cls: 'ui-resizable-handle ui-resizable-e',
19807 html : '  '
19813 var ctr = _this.el.select('.fc-event-container',true).first();
19814 var cg = ctr.createChild(cfg);
19816 var sbox = c.select('.fc-day-content',true).first().getBox();
19817 var ebox = c.select('.fc-day-content',true).first().getBox();
19819 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19820 cg.setWidth(ebox.right - sbox.x -2);
19822 cg.on('click', _this.onMoreEventClick, _this, c.more);
19832 onEventEnter: function (e, el,event,d) {
19833 this.fireEvent('evententer', this, el, event);
19836 onEventLeave: function (e, el,event,d) {
19837 this.fireEvent('eventleave', this, el, event);
19840 onEventClick: function (e, el,event,d) {
19841 this.fireEvent('eventclick', this, el, event);
19844 onMonthChange: function () {
19848 onMoreEventClick: function(e, el, more)
19852 this.calpopover.placement = 'right';
19853 this.calpopover.setTitle('More');
19855 this.calpopover.setContent('');
19857 var ctr = this.calpopover.el.select('.popover-content', true).first();
19859 Roo.each(more, function(m){
19861 cls : 'fc-event-hori fc-event-draggable',
19864 var cg = ctr.createChild(cfg);
19866 cg.on('click', _this.onEventClick, _this, m);
19869 this.calpopover.show(el);
19874 onLoad: function ()
19876 this.calevents = [];
19879 if(this.store.getCount() > 0){
19880 this.store.data.each(function(d){
19883 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19884 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19885 time : d.data.start_time,
19886 title : d.data.title,
19887 description : d.data.description,
19888 venue : d.data.venue
19893 this.renderEvents();
19895 if(this.calevents.length && this.loadMask){
19896 this.maskEl.hide();
19900 onBeforeLoad: function()
19902 this.clearEvents();
19904 this.maskEl.show();
19918 * @class Roo.bootstrap.Popover
19919 * @extends Roo.bootstrap.Component
19920 * Bootstrap Popover class
19921 * @cfg {String} html contents of the popover (or false to use children..)
19922 * @cfg {String} title of popover (or false to hide)
19923 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19924 * @cfg {String} trigger click || hover (or false to trigger manually)
19925 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19926 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19927 * - if false and it has a 'parent' then it will be automatically added to that element
19928 * - if string - Roo.get will be called
19929 * @cfg {Number} delay - delay before showing
19932 * Create a new Popover
19933 * @param {Object} config The config object
19936 Roo.bootstrap.Popover = function(config){
19937 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19943 * After the popover show
19945 * @param {Roo.bootstrap.Popover} this
19950 * After the popover hide
19952 * @param {Roo.bootstrap.Popover} this
19958 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19963 placement : 'right',
19964 trigger : 'hover', // hover
19970 can_build_overlaid : false,
19972 maskEl : false, // the mask element
19975 alignEl : false, // when show is called with an element - this get's stored.
19977 getChildContainer : function()
19979 return this.contentEl;
19982 getPopoverHeader : function()
19984 this.title = true; // flag not to hide it..
19985 this.headerEl.addClass('p-0');
19986 return this.headerEl
19990 getAutoCreate : function(){
19993 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19994 style: 'display:block',
20000 cls : 'popover-inner ',
20004 cls: 'popover-title popover-header',
20005 html : this.title === false ? '' : this.title
20008 cls : 'popover-content popover-body ' + (this.cls || ''),
20009 html : this.html || ''
20020 * @param {string} the title
20022 setTitle: function(str)
20026 this.headerEl.dom.innerHTML = str;
20031 * @param {string} the body content
20033 setContent: function(str)
20036 if (this.contentEl) {
20037 this.contentEl.dom.innerHTML = str;
20041 // as it get's added to the bottom of the page.
20042 onRender : function(ct, position)
20044 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20049 var cfg = Roo.apply({}, this.getAutoCreate());
20053 cfg.cls += ' ' + this.cls;
20056 cfg.style = this.style;
20058 //Roo.log("adding to ");
20059 this.el = Roo.get(document.body).createChild(cfg, position);
20060 // Roo.log(this.el);
20063 this.contentEl = this.el.select('.popover-content',true).first();
20064 this.headerEl = this.el.select('.popover-title',true).first();
20067 if(typeof(this.items) != 'undefined'){
20068 var items = this.items;
20071 for(var i =0;i < items.length;i++) {
20072 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20076 this.items = nitems;
20078 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20079 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20086 resizeMask : function()
20088 this.maskEl.setSize(
20089 Roo.lib.Dom.getViewWidth(true),
20090 Roo.lib.Dom.getViewHeight(true)
20094 initEvents : function()
20098 Roo.bootstrap.Popover.register(this);
20101 this.arrowEl = this.el.select('.arrow',true).first();
20102 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20103 this.el.enableDisplayMode('block');
20107 if (this.over === false && !this.parent()) {
20110 if (this.triggers === false) {
20115 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20116 var triggers = this.trigger ? this.trigger.split(' ') : [];
20117 Roo.each(triggers, function(trigger) {
20119 if (trigger == 'click') {
20120 on_el.on('click', this.toggle, this);
20121 } else if (trigger != 'manual') {
20122 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20123 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20125 on_el.on(eventIn ,this.enter, this);
20126 on_el.on(eventOut, this.leave, this);
20136 toggle : function () {
20137 this.hoverState == 'in' ? this.leave() : this.enter();
20140 enter : function () {
20142 clearTimeout(this.timeout);
20144 this.hoverState = 'in';
20146 if (!this.delay || !this.delay.show) {
20151 this.timeout = setTimeout(function () {
20152 if (_t.hoverState == 'in') {
20155 }, this.delay.show)
20158 leave : function() {
20159 clearTimeout(this.timeout);
20161 this.hoverState = 'out';
20163 if (!this.delay || !this.delay.hide) {
20168 this.timeout = setTimeout(function () {
20169 if (_t.hoverState == 'out') {
20172 }, this.delay.hide)
20176 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20177 * @param {string} (left|right|top|bottom) position
20179 show : function (on_el, placement)
20181 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20182 on_el = on_el || false; // default to false
20185 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20186 on_el = this.parent().el;
20187 } else if (this.over) {
20188 Roo.get(this.over);
20193 this.alignEl = Roo.get( on_el );
20196 this.render(document.body);
20202 if (this.title === false) {
20203 this.headerEl.hide();
20208 this.el.dom.style.display = 'block';
20211 if (this.alignEl) {
20212 this.updatePosition(this.placement, true);
20215 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20216 var es = this.el.getSize();
20217 var x = Roo.lib.Dom.getViewWidth()/2;
20218 var y = Roo.lib.Dom.getViewHeight()/2;
20219 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20224 //var arrow = this.el.select('.arrow',true).first();
20225 //arrow.set(align[2],
20227 this.el.addClass('in');
20231 this.hoverState = 'in';
20234 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20235 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20236 this.maskEl.dom.style.display = 'block';
20237 this.maskEl.addClass('show');
20239 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20241 this.fireEvent('show', this);
20245 * fire this manually after loading a grid in the table for example
20246 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20247 * @param {Boolean} try and move it if we cant get right position.
20249 updatePosition : function(placement, try_move)
20251 // allow for calling with no parameters
20252 placement = placement ? placement : this.placement;
20253 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20255 this.el.removeClass([
20256 'fade','top','bottom', 'left', 'right','in',
20257 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20259 this.el.addClass(placement + ' bs-popover-' + placement);
20261 if (!this.alignEl ) {
20265 switch (placement) {
20267 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20268 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20269 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20270 //normal display... or moved up/down.
20271 this.el.setXY(offset);
20272 var xy = this.alignEl.getAnchorXY('tr', false);
20274 this.arrowEl.setXY(xy);
20277 // continue through...
20278 return this.updatePosition('left', false);
20282 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20283 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20284 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20285 //normal display... or moved up/down.
20286 this.el.setXY(offset);
20287 var xy = this.alignEl.getAnchorXY('tl', false);
20288 xy[0]-=10;xy[1]+=5; // << fix me
20289 this.arrowEl.setXY(xy);
20293 return this.updatePosition('right', false);
20296 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20297 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20298 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20299 //normal display... or moved up/down.
20300 this.el.setXY(offset);
20301 var xy = this.alignEl.getAnchorXY('t', false);
20302 xy[1]-=10; // << fix me
20303 this.arrowEl.setXY(xy);
20307 return this.updatePosition('bottom', false);
20310 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20311 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20312 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20313 //normal display... or moved up/down.
20314 this.el.setXY(offset);
20315 var xy = this.alignEl.getAnchorXY('b', false);
20316 xy[1]+=2; // << fix me
20317 this.arrowEl.setXY(xy);
20321 return this.updatePosition('top', false);
20332 this.el.setXY([0,0]);
20333 this.el.removeClass('in');
20335 this.hoverState = null;
20336 this.maskEl.hide(); // always..
20337 this.fireEvent('hide', this);
20343 Roo.apply(Roo.bootstrap.Popover, {
20346 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20347 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20348 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20349 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20354 clickHander : false,
20357 onMouseDown : function(e)
20359 if (!e.getTarget(".roo-popover")) {
20367 register : function(popup)
20369 if (!Roo.bootstrap.Popover.clickHandler) {
20370 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20372 // hide other popups.
20374 this.popups.push(popup);
20376 hideAll : function()
20378 this.popups.forEach(function(p) {
20386 * Card header - holder for the card header elements.
20391 * @class Roo.bootstrap.PopoverNav
20392 * @extends Roo.bootstrap.NavGroup
20393 * Bootstrap Popover header navigation class
20395 * Create a new Popover Header Navigation
20396 * @param {Object} config The config object
20399 Roo.bootstrap.PopoverNav = function(config){
20400 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20403 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20406 container_method : 'getPopoverHeader'
20424 * @class Roo.bootstrap.Progress
20425 * @extends Roo.bootstrap.Component
20426 * Bootstrap Progress class
20427 * @cfg {Boolean} striped striped of the progress bar
20428 * @cfg {Boolean} active animated of the progress bar
20432 * Create a new Progress
20433 * @param {Object} config The config object
20436 Roo.bootstrap.Progress = function(config){
20437 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20440 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20445 getAutoCreate : function(){
20453 cfg.cls += ' progress-striped';
20457 cfg.cls += ' active';
20476 * @class Roo.bootstrap.ProgressBar
20477 * @extends Roo.bootstrap.Component
20478 * Bootstrap ProgressBar class
20479 * @cfg {Number} aria_valuenow aria-value now
20480 * @cfg {Number} aria_valuemin aria-value min
20481 * @cfg {Number} aria_valuemax aria-value max
20482 * @cfg {String} label label for the progress bar
20483 * @cfg {String} panel (success | info | warning | danger )
20484 * @cfg {String} role role of the progress bar
20485 * @cfg {String} sr_only text
20489 * Create a new ProgressBar
20490 * @param {Object} config The config object
20493 Roo.bootstrap.ProgressBar = function(config){
20494 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20497 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20501 aria_valuemax : 100,
20507 getAutoCreate : function()
20512 cls: 'progress-bar',
20513 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20525 cfg.role = this.role;
20528 if(this.aria_valuenow){
20529 cfg['aria-valuenow'] = this.aria_valuenow;
20532 if(this.aria_valuemin){
20533 cfg['aria-valuemin'] = this.aria_valuemin;
20536 if(this.aria_valuemax){
20537 cfg['aria-valuemax'] = this.aria_valuemax;
20540 if(this.label && !this.sr_only){
20541 cfg.html = this.label;
20545 cfg.cls += ' progress-bar-' + this.panel;
20551 update : function(aria_valuenow)
20553 this.aria_valuenow = aria_valuenow;
20555 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20570 * @class Roo.bootstrap.TabGroup
20571 * @extends Roo.bootstrap.Column
20572 * Bootstrap Column class
20573 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20574 * @cfg {Boolean} carousel true to make the group behave like a carousel
20575 * @cfg {Boolean} bullets show bullets for the panels
20576 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20577 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20578 * @cfg {Boolean} showarrow (true|false) show arrow default true
20581 * Create a new TabGroup
20582 * @param {Object} config The config object
20585 Roo.bootstrap.TabGroup = function(config){
20586 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20588 this.navId = Roo.id();
20591 Roo.bootstrap.TabGroup.register(this);
20595 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20598 transition : false,
20603 slideOnTouch : false,
20606 getAutoCreate : function()
20608 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20610 cfg.cls += ' tab-content';
20612 if (this.carousel) {
20613 cfg.cls += ' carousel slide';
20616 cls : 'carousel-inner',
20620 if(this.bullets && !Roo.isTouch){
20623 cls : 'carousel-bullets',
20627 if(this.bullets_cls){
20628 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20635 cfg.cn[0].cn.push(bullets);
20638 if(this.showarrow){
20639 cfg.cn[0].cn.push({
20641 class : 'carousel-arrow',
20645 class : 'carousel-prev',
20649 class : 'fa fa-chevron-left'
20655 class : 'carousel-next',
20659 class : 'fa fa-chevron-right'
20672 initEvents: function()
20674 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20675 // this.el.on("touchstart", this.onTouchStart, this);
20678 if(this.autoslide){
20681 this.slideFn = window.setInterval(function() {
20682 _this.showPanelNext();
20686 if(this.showarrow){
20687 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20688 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20694 // onTouchStart : function(e, el, o)
20696 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20700 // this.showPanelNext();
20704 getChildContainer : function()
20706 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20710 * register a Navigation item
20711 * @param {Roo.bootstrap.NavItem} the navitem to add
20713 register : function(item)
20715 this.tabs.push( item);
20716 item.navId = this.navId; // not really needed..
20721 getActivePanel : function()
20724 Roo.each(this.tabs, function(t) {
20734 getPanelByName : function(n)
20737 Roo.each(this.tabs, function(t) {
20738 if (t.tabId == n) {
20746 indexOfPanel : function(p)
20749 Roo.each(this.tabs, function(t,i) {
20750 if (t.tabId == p.tabId) {
20759 * show a specific panel
20760 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20761 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20763 showPanel : function (pan)
20765 if(this.transition || typeof(pan) == 'undefined'){
20766 Roo.log("waiting for the transitionend");
20770 if (typeof(pan) == 'number') {
20771 pan = this.tabs[pan];
20774 if (typeof(pan) == 'string') {
20775 pan = this.getPanelByName(pan);
20778 var cur = this.getActivePanel();
20781 Roo.log('pan or acitve pan is undefined');
20785 if (pan.tabId == this.getActivePanel().tabId) {
20789 if (false === cur.fireEvent('beforedeactivate')) {
20793 if(this.bullets > 0 && !Roo.isTouch){
20794 this.setActiveBullet(this.indexOfPanel(pan));
20797 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20799 //class="carousel-item carousel-item-next carousel-item-left"
20801 this.transition = true;
20802 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20803 var lr = dir == 'next' ? 'left' : 'right';
20804 pan.el.addClass(dir); // or prev
20805 pan.el.addClass('carousel-item-' + dir); // or prev
20806 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20807 cur.el.addClass(lr); // or right
20808 pan.el.addClass(lr);
20809 cur.el.addClass('carousel-item-' +lr); // or right
20810 pan.el.addClass('carousel-item-' +lr);
20814 cur.el.on('transitionend', function() {
20815 Roo.log("trans end?");
20817 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20818 pan.setActive(true);
20820 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20821 cur.setActive(false);
20823 _this.transition = false;
20825 }, this, { single: true } );
20830 cur.setActive(false);
20831 pan.setActive(true);
20836 showPanelNext : function()
20838 var i = this.indexOfPanel(this.getActivePanel());
20840 if (i >= this.tabs.length - 1 && !this.autoslide) {
20844 if (i >= this.tabs.length - 1 && this.autoslide) {
20848 this.showPanel(this.tabs[i+1]);
20851 showPanelPrev : function()
20853 var i = this.indexOfPanel(this.getActivePanel());
20855 if (i < 1 && !this.autoslide) {
20859 if (i < 1 && this.autoslide) {
20860 i = this.tabs.length;
20863 this.showPanel(this.tabs[i-1]);
20867 addBullet: function()
20869 if(!this.bullets || Roo.isTouch){
20872 var ctr = this.el.select('.carousel-bullets',true).first();
20873 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20874 var bullet = ctr.createChild({
20875 cls : 'bullet bullet-' + i
20876 },ctr.dom.lastChild);
20881 bullet.on('click', (function(e, el, o, ii, t){
20883 e.preventDefault();
20885 this.showPanel(ii);
20887 if(this.autoslide && this.slideFn){
20888 clearInterval(this.slideFn);
20889 this.slideFn = window.setInterval(function() {
20890 _this.showPanelNext();
20894 }).createDelegate(this, [i, bullet], true));
20899 setActiveBullet : function(i)
20905 Roo.each(this.el.select('.bullet', true).elements, function(el){
20906 el.removeClass('selected');
20909 var bullet = this.el.select('.bullet-' + i, true).first();
20915 bullet.addClass('selected');
20926 Roo.apply(Roo.bootstrap.TabGroup, {
20930 * register a Navigation Group
20931 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20933 register : function(navgrp)
20935 this.groups[navgrp.navId] = navgrp;
20939 * fetch a Navigation Group based on the navigation ID
20940 * if one does not exist , it will get created.
20941 * @param {string} the navgroup to add
20942 * @returns {Roo.bootstrap.NavGroup} the navgroup
20944 get: function(navId) {
20945 if (typeof(this.groups[navId]) == 'undefined') {
20946 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20948 return this.groups[navId] ;
20963 * @class Roo.bootstrap.TabPanel
20964 * @extends Roo.bootstrap.Component
20965 * Bootstrap TabPanel class
20966 * @cfg {Boolean} active panel active
20967 * @cfg {String} html panel content
20968 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20969 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20970 * @cfg {String} href click to link..
20971 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20975 * Create a new TabPanel
20976 * @param {Object} config The config object
20979 Roo.bootstrap.TabPanel = function(config){
20980 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20984 * Fires when the active status changes
20985 * @param {Roo.bootstrap.TabPanel} this
20986 * @param {Boolean} state the new state
20991 * @event beforedeactivate
20992 * Fires before a tab is de-activated - can be used to do validation on a form.
20993 * @param {Roo.bootstrap.TabPanel} this
20994 * @return {Boolean} false if there is an error
20997 'beforedeactivate': true
21000 this.tabId = this.tabId || Roo.id();
21004 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21011 touchSlide : false,
21012 getAutoCreate : function(){
21017 // item is needed for carousel - not sure if it has any effect otherwise
21018 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21019 html: this.html || ''
21023 cfg.cls += ' active';
21027 cfg.tabId = this.tabId;
21035 initEvents: function()
21037 var p = this.parent();
21039 this.navId = this.navId || p.navId;
21041 if (typeof(this.navId) != 'undefined') {
21042 // not really needed.. but just in case.. parent should be a NavGroup.
21043 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21047 var i = tg.tabs.length - 1;
21049 if(this.active && tg.bullets > 0 && i < tg.bullets){
21050 tg.setActiveBullet(i);
21054 this.el.on('click', this.onClick, this);
21056 if(Roo.isTouch && this.touchSlide){
21057 this.el.on("touchstart", this.onTouchStart, this);
21058 this.el.on("touchmove", this.onTouchMove, this);
21059 this.el.on("touchend", this.onTouchEnd, this);
21064 onRender : function(ct, position)
21066 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21069 setActive : function(state)
21071 Roo.log("panel - set active " + this.tabId + "=" + state);
21073 this.active = state;
21075 this.el.removeClass('active');
21077 } else if (!this.el.hasClass('active')) {
21078 this.el.addClass('active');
21081 this.fireEvent('changed', this, state);
21084 onClick : function(e)
21086 e.preventDefault();
21088 if(!this.href.length){
21092 window.location.href = this.href;
21101 onTouchStart : function(e)
21103 this.swiping = false;
21105 this.startX = e.browserEvent.touches[0].clientX;
21106 this.startY = e.browserEvent.touches[0].clientY;
21109 onTouchMove : function(e)
21111 this.swiping = true;
21113 this.endX = e.browserEvent.touches[0].clientX;
21114 this.endY = e.browserEvent.touches[0].clientY;
21117 onTouchEnd : function(e)
21124 var tabGroup = this.parent();
21126 if(this.endX > this.startX){ // swiping right
21127 tabGroup.showPanelPrev();
21131 if(this.startX > this.endX){ // swiping left
21132 tabGroup.showPanelNext();
21151 * @class Roo.bootstrap.DateField
21152 * @extends Roo.bootstrap.Input
21153 * Bootstrap DateField class
21154 * @cfg {Number} weekStart default 0
21155 * @cfg {String} viewMode default empty, (months|years)
21156 * @cfg {String} minViewMode default empty, (months|years)
21157 * @cfg {Number} startDate default -Infinity
21158 * @cfg {Number} endDate default Infinity
21159 * @cfg {Boolean} todayHighlight default false
21160 * @cfg {Boolean} todayBtn default false
21161 * @cfg {Boolean} calendarWeeks default false
21162 * @cfg {Object} daysOfWeekDisabled default empty
21163 * @cfg {Boolean} singleMode default false (true | false)
21165 * @cfg {Boolean} keyboardNavigation default true
21166 * @cfg {String} language default en
21169 * Create a new DateField
21170 * @param {Object} config The config object
21173 Roo.bootstrap.DateField = function(config){
21174 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21178 * Fires when this field show.
21179 * @param {Roo.bootstrap.DateField} this
21180 * @param {Mixed} date The date value
21185 * Fires when this field hide.
21186 * @param {Roo.bootstrap.DateField} this
21187 * @param {Mixed} date The date value
21192 * Fires when select a date.
21193 * @param {Roo.bootstrap.DateField} this
21194 * @param {Mixed} date The date value
21198 * @event beforeselect
21199 * Fires when before select a date.
21200 * @param {Roo.bootstrap.DateField} this
21201 * @param {Mixed} date The date value
21203 beforeselect : true
21207 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21210 * @cfg {String} format
21211 * The default date format string which can be overriden for localization support. The format must be
21212 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21216 * @cfg {String} altFormats
21217 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21218 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21220 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21228 todayHighlight : false,
21234 keyboardNavigation: true,
21236 calendarWeeks: false,
21238 startDate: -Infinity,
21242 daysOfWeekDisabled: [],
21246 singleMode : false,
21248 UTCDate: function()
21250 return new Date(Date.UTC.apply(Date, arguments));
21253 UTCToday: function()
21255 var today = new Date();
21256 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21259 getDate: function() {
21260 var d = this.getUTCDate();
21261 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21264 getUTCDate: function() {
21268 setDate: function(d) {
21269 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21272 setUTCDate: function(d) {
21274 this.setValue(this.formatDate(this.date));
21277 onRender: function(ct, position)
21280 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21282 this.language = this.language || 'en';
21283 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21284 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21286 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21287 this.format = this.format || 'm/d/y';
21288 this.isInline = false;
21289 this.isInput = true;
21290 this.component = this.el.select('.add-on', true).first() || false;
21291 this.component = (this.component && this.component.length === 0) ? false : this.component;
21292 this.hasInput = this.component && this.inputEl().length;
21294 if (typeof(this.minViewMode === 'string')) {
21295 switch (this.minViewMode) {
21297 this.minViewMode = 1;
21300 this.minViewMode = 2;
21303 this.minViewMode = 0;
21308 if (typeof(this.viewMode === 'string')) {
21309 switch (this.viewMode) {
21322 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21324 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21326 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21328 this.picker().on('mousedown', this.onMousedown, this);
21329 this.picker().on('click', this.onClick, this);
21331 this.picker().addClass('datepicker-dropdown');
21333 this.startViewMode = this.viewMode;
21335 if(this.singleMode){
21336 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21337 v.setVisibilityMode(Roo.Element.DISPLAY);
21341 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21342 v.setStyle('width', '189px');
21346 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21347 if(!this.calendarWeeks){
21352 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21353 v.attr('colspan', function(i, val){
21354 return parseInt(val) + 1;
21359 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21361 this.setStartDate(this.startDate);
21362 this.setEndDate(this.endDate);
21364 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21371 if(this.isInline) {
21376 picker : function()
21378 return this.pickerEl;
21379 // return this.el.select('.datepicker', true).first();
21382 fillDow: function()
21384 var dowCnt = this.weekStart;
21393 if(this.calendarWeeks){
21401 while (dowCnt < this.weekStart + 7) {
21405 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21409 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21412 fillMonths: function()
21415 var months = this.picker().select('>.datepicker-months td', true).first();
21417 months.dom.innerHTML = '';
21423 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21426 months.createChild(month);
21433 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;
21435 if (this.date < this.startDate) {
21436 this.viewDate = new Date(this.startDate);
21437 } else if (this.date > this.endDate) {
21438 this.viewDate = new Date(this.endDate);
21440 this.viewDate = new Date(this.date);
21448 var d = new Date(this.viewDate),
21449 year = d.getUTCFullYear(),
21450 month = d.getUTCMonth(),
21451 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21452 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21453 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21454 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21455 currentDate = this.date && this.date.valueOf(),
21456 today = this.UTCToday();
21458 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21460 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21462 // this.picker.select('>tfoot th.today').
21463 // .text(dates[this.language].today)
21464 // .toggle(this.todayBtn !== false);
21466 this.updateNavArrows();
21469 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21471 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21473 prevMonth.setUTCDate(day);
21475 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21477 var nextMonth = new Date(prevMonth);
21479 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21481 nextMonth = nextMonth.valueOf();
21483 var fillMonths = false;
21485 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21487 while(prevMonth.valueOf() <= nextMonth) {
21490 if (prevMonth.getUTCDay() === this.weekStart) {
21492 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21500 if(this.calendarWeeks){
21501 // ISO 8601: First week contains first thursday.
21502 // ISO also states week starts on Monday, but we can be more abstract here.
21504 // Start of current week: based on weekstart/current date
21505 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21506 // Thursday of this week
21507 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21508 // First Thursday of year, year from thursday
21509 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21510 // Calendar week: ms between thursdays, div ms per day, div 7 days
21511 calWeek = (th - yth) / 864e5 / 7 + 1;
21513 fillMonths.cn.push({
21521 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21523 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21526 if (this.todayHighlight &&
21527 prevMonth.getUTCFullYear() == today.getFullYear() &&
21528 prevMonth.getUTCMonth() == today.getMonth() &&
21529 prevMonth.getUTCDate() == today.getDate()) {
21530 clsName += ' today';
21533 if (currentDate && prevMonth.valueOf() === currentDate) {
21534 clsName += ' active';
21537 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21538 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21539 clsName += ' disabled';
21542 fillMonths.cn.push({
21544 cls: 'day ' + clsName,
21545 html: prevMonth.getDate()
21548 prevMonth.setDate(prevMonth.getDate()+1);
21551 var currentYear = this.date && this.date.getUTCFullYear();
21552 var currentMonth = this.date && this.date.getUTCMonth();
21554 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21556 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21557 v.removeClass('active');
21559 if(currentYear === year && k === currentMonth){
21560 v.addClass('active');
21563 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21564 v.addClass('disabled');
21570 year = parseInt(year/10, 10) * 10;
21572 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21574 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21577 for (var i = -1; i < 11; i++) {
21578 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21580 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21588 showMode: function(dir)
21591 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21594 Roo.each(this.picker().select('>div',true).elements, function(v){
21595 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21598 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21603 if(this.isInline) {
21607 this.picker().removeClass(['bottom', 'top']);
21609 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21611 * place to the top of element!
21615 this.picker().addClass('top');
21616 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21621 this.picker().addClass('bottom');
21623 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21626 parseDate : function(value)
21628 if(!value || value instanceof Date){
21631 var v = Date.parseDate(value, this.format);
21632 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21633 v = Date.parseDate(value, 'Y-m-d');
21635 if(!v && this.altFormats){
21636 if(!this.altFormatsArray){
21637 this.altFormatsArray = this.altFormats.split("|");
21639 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21640 v = Date.parseDate(value, this.altFormatsArray[i]);
21646 formatDate : function(date, fmt)
21648 return (!date || !(date instanceof Date)) ?
21649 date : date.dateFormat(fmt || this.format);
21652 onFocus : function()
21654 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21658 onBlur : function()
21660 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21662 var d = this.inputEl().getValue();
21669 showPopup : function()
21671 this.picker().show();
21675 this.fireEvent('showpopup', this, this.date);
21678 hidePopup : function()
21680 if(this.isInline) {
21683 this.picker().hide();
21684 this.viewMode = this.startViewMode;
21687 this.fireEvent('hidepopup', this, this.date);
21691 onMousedown: function(e)
21693 e.stopPropagation();
21694 e.preventDefault();
21699 Roo.bootstrap.DateField.superclass.keyup.call(this);
21703 setValue: function(v)
21705 if(this.fireEvent('beforeselect', this, v) !== false){
21706 var d = new Date(this.parseDate(v) ).clearTime();
21708 if(isNaN(d.getTime())){
21709 this.date = this.viewDate = '';
21710 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21714 v = this.formatDate(d);
21716 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21718 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21722 this.fireEvent('select', this, this.date);
21726 getValue: function()
21728 return this.formatDate(this.date);
21731 fireKey: function(e)
21733 if (!this.picker().isVisible()){
21734 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21740 var dateChanged = false,
21742 newDate, newViewDate;
21747 e.preventDefault();
21751 if (!this.keyboardNavigation) {
21754 dir = e.keyCode == 37 ? -1 : 1;
21757 newDate = this.moveYear(this.date, dir);
21758 newViewDate = this.moveYear(this.viewDate, dir);
21759 } else if (e.shiftKey){
21760 newDate = this.moveMonth(this.date, dir);
21761 newViewDate = this.moveMonth(this.viewDate, dir);
21763 newDate = new Date(this.date);
21764 newDate.setUTCDate(this.date.getUTCDate() + dir);
21765 newViewDate = new Date(this.viewDate);
21766 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21768 if (this.dateWithinRange(newDate)){
21769 this.date = newDate;
21770 this.viewDate = newViewDate;
21771 this.setValue(this.formatDate(this.date));
21773 e.preventDefault();
21774 dateChanged = true;
21779 if (!this.keyboardNavigation) {
21782 dir = e.keyCode == 38 ? -1 : 1;
21784 newDate = this.moveYear(this.date, dir);
21785 newViewDate = this.moveYear(this.viewDate, dir);
21786 } else if (e.shiftKey){
21787 newDate = this.moveMonth(this.date, dir);
21788 newViewDate = this.moveMonth(this.viewDate, dir);
21790 newDate = new Date(this.date);
21791 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21792 newViewDate = new Date(this.viewDate);
21793 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21795 if (this.dateWithinRange(newDate)){
21796 this.date = newDate;
21797 this.viewDate = newViewDate;
21798 this.setValue(this.formatDate(this.date));
21800 e.preventDefault();
21801 dateChanged = true;
21805 this.setValue(this.formatDate(this.date));
21807 e.preventDefault();
21810 this.setValue(this.formatDate(this.date));
21824 onClick: function(e)
21826 e.stopPropagation();
21827 e.preventDefault();
21829 var target = e.getTarget();
21831 if(target.nodeName.toLowerCase() === 'i'){
21832 target = Roo.get(target).dom.parentNode;
21835 var nodeName = target.nodeName;
21836 var className = target.className;
21837 var html = target.innerHTML;
21838 //Roo.log(nodeName);
21840 switch(nodeName.toLowerCase()) {
21842 switch(className) {
21848 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21849 switch(this.viewMode){
21851 this.viewDate = this.moveMonth(this.viewDate, dir);
21855 this.viewDate = this.moveYear(this.viewDate, dir);
21861 var date = new Date();
21862 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21864 this.setValue(this.formatDate(this.date));
21871 if (className.indexOf('disabled') < 0) {
21872 this.viewDate.setUTCDate(1);
21873 if (className.indexOf('month') > -1) {
21874 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21876 var year = parseInt(html, 10) || 0;
21877 this.viewDate.setUTCFullYear(year);
21881 if(this.singleMode){
21882 this.setValue(this.formatDate(this.viewDate));
21893 //Roo.log(className);
21894 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21895 var day = parseInt(html, 10) || 1;
21896 var year = (this.viewDate || new Date()).getUTCFullYear(),
21897 month = (this.viewDate || new Date()).getUTCMonth();
21899 if (className.indexOf('old') > -1) {
21906 } else if (className.indexOf('new') > -1) {
21914 //Roo.log([year,month,day]);
21915 this.date = this.UTCDate(year, month, day,0,0,0,0);
21916 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21918 //Roo.log(this.formatDate(this.date));
21919 this.setValue(this.formatDate(this.date));
21926 setStartDate: function(startDate)
21928 this.startDate = startDate || -Infinity;
21929 if (this.startDate !== -Infinity) {
21930 this.startDate = this.parseDate(this.startDate);
21933 this.updateNavArrows();
21936 setEndDate: function(endDate)
21938 this.endDate = endDate || Infinity;
21939 if (this.endDate !== Infinity) {
21940 this.endDate = this.parseDate(this.endDate);
21943 this.updateNavArrows();
21946 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21948 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21949 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21950 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21952 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21953 return parseInt(d, 10);
21956 this.updateNavArrows();
21959 updateNavArrows: function()
21961 if(this.singleMode){
21965 var d = new Date(this.viewDate),
21966 year = d.getUTCFullYear(),
21967 month = d.getUTCMonth();
21969 Roo.each(this.picker().select('.prev', true).elements, function(v){
21971 switch (this.viewMode) {
21974 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21980 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21987 Roo.each(this.picker().select('.next', true).elements, function(v){
21989 switch (this.viewMode) {
21992 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21998 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22006 moveMonth: function(date, dir)
22011 var new_date = new Date(date.valueOf()),
22012 day = new_date.getUTCDate(),
22013 month = new_date.getUTCMonth(),
22014 mag = Math.abs(dir),
22016 dir = dir > 0 ? 1 : -1;
22019 // If going back one month, make sure month is not current month
22020 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22022 return new_date.getUTCMonth() == month;
22024 // If going forward one month, make sure month is as expected
22025 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22027 return new_date.getUTCMonth() != new_month;
22029 new_month = month + dir;
22030 new_date.setUTCMonth(new_month);
22031 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22032 if (new_month < 0 || new_month > 11) {
22033 new_month = (new_month + 12) % 12;
22036 // For magnitudes >1, move one month at a time...
22037 for (var i=0; i<mag; i++) {
22038 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22039 new_date = this.moveMonth(new_date, dir);
22041 // ...then reset the day, keeping it in the new month
22042 new_month = new_date.getUTCMonth();
22043 new_date.setUTCDate(day);
22045 return new_month != new_date.getUTCMonth();
22048 // Common date-resetting loop -- if date is beyond end of month, make it
22051 new_date.setUTCDate(--day);
22052 new_date.setUTCMonth(new_month);
22057 moveYear: function(date, dir)
22059 return this.moveMonth(date, dir*12);
22062 dateWithinRange: function(date)
22064 return date >= this.startDate && date <= this.endDate;
22070 this.picker().remove();
22073 validateValue : function(value)
22075 if(this.getVisibilityEl().hasClass('hidden')){
22079 if(value.length < 1) {
22080 if(this.allowBlank){
22086 if(value.length < this.minLength){
22089 if(value.length > this.maxLength){
22093 var vt = Roo.form.VTypes;
22094 if(!vt[this.vtype](value, this)){
22098 if(typeof this.validator == "function"){
22099 var msg = this.validator(value);
22105 if(this.regex && !this.regex.test(value)){
22109 if(typeof(this.parseDate(value)) == 'undefined'){
22113 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22117 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22127 this.date = this.viewDate = '';
22129 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22134 Roo.apply(Roo.bootstrap.DateField, {
22145 html: '<i class="fa fa-arrow-left"/>'
22155 html: '<i class="fa fa-arrow-right"/>'
22197 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22198 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22199 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22200 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22201 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22214 navFnc: 'FullYear',
22219 navFnc: 'FullYear',
22224 Roo.apply(Roo.bootstrap.DateField, {
22228 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22232 cls: 'datepicker-days',
22236 cls: 'table-condensed',
22238 Roo.bootstrap.DateField.head,
22242 Roo.bootstrap.DateField.footer
22249 cls: 'datepicker-months',
22253 cls: 'table-condensed',
22255 Roo.bootstrap.DateField.head,
22256 Roo.bootstrap.DateField.content,
22257 Roo.bootstrap.DateField.footer
22264 cls: 'datepicker-years',
22268 cls: 'table-condensed',
22270 Roo.bootstrap.DateField.head,
22271 Roo.bootstrap.DateField.content,
22272 Roo.bootstrap.DateField.footer
22291 * @class Roo.bootstrap.TimeField
22292 * @extends Roo.bootstrap.Input
22293 * Bootstrap DateField class
22297 * Create a new TimeField
22298 * @param {Object} config The config object
22301 Roo.bootstrap.TimeField = function(config){
22302 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22306 * Fires when this field show.
22307 * @param {Roo.bootstrap.DateField} thisthis
22308 * @param {Mixed} date The date value
22313 * Fires when this field hide.
22314 * @param {Roo.bootstrap.DateField} this
22315 * @param {Mixed} date The date value
22320 * Fires when select a date.
22321 * @param {Roo.bootstrap.DateField} this
22322 * @param {Mixed} date The date value
22328 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22331 * @cfg {String} format
22332 * The default time format string which can be overriden for localization support. The format must be
22333 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22337 getAutoCreate : function()
22339 this.after = '<i class="fa far fa-clock"></i>';
22340 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22344 onRender: function(ct, position)
22347 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22349 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22351 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22353 this.pop = this.picker().select('>.datepicker-time',true).first();
22354 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22356 this.picker().on('mousedown', this.onMousedown, this);
22357 this.picker().on('click', this.onClick, this);
22359 this.picker().addClass('datepicker-dropdown');
22364 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22365 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22366 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22367 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22368 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22369 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22373 fireKey: function(e){
22374 if (!this.picker().isVisible()){
22375 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22381 e.preventDefault();
22389 this.onTogglePeriod();
22392 this.onIncrementMinutes();
22395 this.onDecrementMinutes();
22404 onClick: function(e) {
22405 e.stopPropagation();
22406 e.preventDefault();
22409 picker : function()
22411 return this.pickerEl;
22414 fillTime: function()
22416 var time = this.pop.select('tbody', true).first();
22418 time.dom.innerHTML = '';
22433 cls: 'hours-up fa fas fa-chevron-up'
22453 cls: 'minutes-up fa fas fa-chevron-up'
22474 cls: 'timepicker-hour',
22489 cls: 'timepicker-minute',
22504 cls: 'btn btn-primary period',
22526 cls: 'hours-down fa fas fa-chevron-down'
22546 cls: 'minutes-down fa fas fa-chevron-down'
22564 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22571 var hours = this.time.getHours();
22572 var minutes = this.time.getMinutes();
22585 hours = hours - 12;
22589 hours = '0' + hours;
22593 minutes = '0' + minutes;
22596 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22597 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22598 this.pop.select('button', true).first().dom.innerHTML = period;
22604 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22606 var cls = ['bottom'];
22608 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22615 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22619 //this.picker().setXY(20000,20000);
22620 this.picker().addClass(cls.join('-'));
22624 Roo.each(cls, function(c){
22629 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22630 //_this.picker().setTop(_this.inputEl().getHeight());
22634 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22636 //_this.picker().setTop(0 - _this.picker().getHeight());
22641 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22645 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22653 onFocus : function()
22655 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22659 onBlur : function()
22661 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22667 this.picker().show();
22672 this.fireEvent('show', this, this.date);
22677 this.picker().hide();
22680 this.fireEvent('hide', this, this.date);
22683 setTime : function()
22686 this.setValue(this.time.format(this.format));
22688 this.fireEvent('select', this, this.date);
22693 onMousedown: function(e){
22694 e.stopPropagation();
22695 e.preventDefault();
22698 onIncrementHours: function()
22700 Roo.log('onIncrementHours');
22701 this.time = this.time.add(Date.HOUR, 1);
22706 onDecrementHours: function()
22708 Roo.log('onDecrementHours');
22709 this.time = this.time.add(Date.HOUR, -1);
22713 onIncrementMinutes: function()
22715 Roo.log('onIncrementMinutes');
22716 this.time = this.time.add(Date.MINUTE, 1);
22720 onDecrementMinutes: function()
22722 Roo.log('onDecrementMinutes');
22723 this.time = this.time.add(Date.MINUTE, -1);
22727 onTogglePeriod: function()
22729 Roo.log('onTogglePeriod');
22730 this.time = this.time.add(Date.HOUR, 12);
22738 Roo.apply(Roo.bootstrap.TimeField, {
22742 cls: 'datepicker dropdown-menu',
22746 cls: 'datepicker-time',
22750 cls: 'table-condensed',
22779 cls: 'btn btn-info ok',
22807 * @class Roo.bootstrap.MonthField
22808 * @extends Roo.bootstrap.Input
22809 * Bootstrap MonthField class
22811 * @cfg {String} language default en
22814 * Create a new MonthField
22815 * @param {Object} config The config object
22818 Roo.bootstrap.MonthField = function(config){
22819 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22824 * Fires when this field show.
22825 * @param {Roo.bootstrap.MonthField} this
22826 * @param {Mixed} date The date value
22831 * Fires when this field hide.
22832 * @param {Roo.bootstrap.MonthField} this
22833 * @param {Mixed} date The date value
22838 * Fires when select a date.
22839 * @param {Roo.bootstrap.MonthField} this
22840 * @param {String} oldvalue The old value
22841 * @param {String} newvalue The new value
22847 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22849 onRender: function(ct, position)
22852 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22854 this.language = this.language || 'en';
22855 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22856 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22858 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22859 this.isInline = false;
22860 this.isInput = true;
22861 this.component = this.el.select('.add-on', true).first() || false;
22862 this.component = (this.component && this.component.length === 0) ? false : this.component;
22863 this.hasInput = this.component && this.inputEL().length;
22865 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22867 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22869 this.picker().on('mousedown', this.onMousedown, this);
22870 this.picker().on('click', this.onClick, this);
22872 this.picker().addClass('datepicker-dropdown');
22874 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22875 v.setStyle('width', '189px');
22882 if(this.isInline) {
22888 setValue: function(v, suppressEvent)
22890 var o = this.getValue();
22892 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22896 if(suppressEvent !== true){
22897 this.fireEvent('select', this, o, v);
22902 getValue: function()
22907 onClick: function(e)
22909 e.stopPropagation();
22910 e.preventDefault();
22912 var target = e.getTarget();
22914 if(target.nodeName.toLowerCase() === 'i'){
22915 target = Roo.get(target).dom.parentNode;
22918 var nodeName = target.nodeName;
22919 var className = target.className;
22920 var html = target.innerHTML;
22922 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22926 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22928 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22934 picker : function()
22936 return this.pickerEl;
22939 fillMonths: function()
22942 var months = this.picker().select('>.datepicker-months td', true).first();
22944 months.dom.innerHTML = '';
22950 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22953 months.createChild(month);
22962 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22963 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22966 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22967 e.removeClass('active');
22969 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22970 e.addClass('active');
22977 if(this.isInline) {
22981 this.picker().removeClass(['bottom', 'top']);
22983 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22985 * place to the top of element!
22989 this.picker().addClass('top');
22990 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22995 this.picker().addClass('bottom');
22997 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23000 onFocus : function()
23002 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23006 onBlur : function()
23008 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23010 var d = this.inputEl().getValue();
23019 this.picker().show();
23020 this.picker().select('>.datepicker-months', true).first().show();
23024 this.fireEvent('show', this, this.date);
23029 if(this.isInline) {
23032 this.picker().hide();
23033 this.fireEvent('hide', this, this.date);
23037 onMousedown: function(e)
23039 e.stopPropagation();
23040 e.preventDefault();
23045 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23049 fireKey: function(e)
23051 if (!this.picker().isVisible()){
23052 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23063 e.preventDefault();
23067 dir = e.keyCode == 37 ? -1 : 1;
23069 this.vIndex = this.vIndex + dir;
23071 if(this.vIndex < 0){
23075 if(this.vIndex > 11){
23079 if(isNaN(this.vIndex)){
23083 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23089 dir = e.keyCode == 38 ? -1 : 1;
23091 this.vIndex = this.vIndex + dir * 4;
23093 if(this.vIndex < 0){
23097 if(this.vIndex > 11){
23101 if(isNaN(this.vIndex)){
23105 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23110 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23111 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23115 e.preventDefault();
23118 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23119 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23135 this.picker().remove();
23140 Roo.apply(Roo.bootstrap.MonthField, {
23159 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23160 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23165 Roo.apply(Roo.bootstrap.MonthField, {
23169 cls: 'datepicker dropdown-menu roo-dynamic',
23173 cls: 'datepicker-months',
23177 cls: 'table-condensed',
23179 Roo.bootstrap.DateField.content
23199 * @class Roo.bootstrap.CheckBox
23200 * @extends Roo.bootstrap.Input
23201 * Bootstrap CheckBox class
23203 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23204 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23205 * @cfg {String} boxLabel The text that appears beside the checkbox
23206 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23207 * @cfg {Boolean} checked initnal the element
23208 * @cfg {Boolean} inline inline the element (default false)
23209 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23210 * @cfg {String} tooltip label tooltip
23213 * Create a new CheckBox
23214 * @param {Object} config The config object
23217 Roo.bootstrap.CheckBox = function(config){
23218 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23223 * Fires when the element is checked or unchecked.
23224 * @param {Roo.bootstrap.CheckBox} this This input
23225 * @param {Boolean} checked The new checked value
23230 * Fires when the element is click.
23231 * @param {Roo.bootstrap.CheckBox} this This input
23238 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23240 inputType: 'checkbox',
23249 // checkbox success does not make any sense really..
23254 getAutoCreate : function()
23256 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23262 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23265 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23271 type : this.inputType,
23272 value : this.inputValue,
23273 cls : 'roo-' + this.inputType, //'form-box',
23274 placeholder : this.placeholder || ''
23278 if(this.inputType != 'radio'){
23282 cls : 'roo-hidden-value',
23283 value : this.checked ? this.inputValue : this.valueOff
23288 if (this.weight) { // Validity check?
23289 cfg.cls += " " + this.inputType + "-" + this.weight;
23292 if (this.disabled) {
23293 input.disabled=true;
23297 input.checked = this.checked;
23302 input.name = this.name;
23304 if(this.inputType != 'radio'){
23305 hidden.name = this.name;
23306 input.name = '_hidden_' + this.name;
23311 input.cls += ' input-' + this.size;
23316 ['xs','sm','md','lg'].map(function(size){
23317 if (settings[size]) {
23318 cfg.cls += ' col-' + size + '-' + settings[size];
23322 var inputblock = input;
23324 if (this.before || this.after) {
23327 cls : 'input-group',
23332 inputblock.cn.push({
23334 cls : 'input-group-addon',
23339 inputblock.cn.push(input);
23341 if(this.inputType != 'radio'){
23342 inputblock.cn.push(hidden);
23346 inputblock.cn.push({
23348 cls : 'input-group-addon',
23354 var boxLabelCfg = false;
23360 //'for': id, // box label is handled by onclick - so no for...
23362 html: this.boxLabel
23365 boxLabelCfg.tooltip = this.tooltip;
23371 if (align ==='left' && this.fieldLabel.length) {
23372 // Roo.log("left and has label");
23377 cls : 'control-label',
23378 html : this.fieldLabel
23389 cfg.cn[1].cn.push(boxLabelCfg);
23392 if(this.labelWidth > 12){
23393 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23396 if(this.labelWidth < 13 && this.labelmd == 0){
23397 this.labelmd = this.labelWidth;
23400 if(this.labellg > 0){
23401 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23402 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23405 if(this.labelmd > 0){
23406 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23407 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23410 if(this.labelsm > 0){
23411 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23412 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23415 if(this.labelxs > 0){
23416 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23417 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23420 } else if ( this.fieldLabel.length) {
23421 // Roo.log(" label");
23425 tag: this.boxLabel ? 'span' : 'label',
23427 cls: 'control-label box-input-label',
23428 //cls : 'input-group-addon',
23429 html : this.fieldLabel
23436 cfg.cn.push(boxLabelCfg);
23441 // Roo.log(" no label && no align");
23442 cfg.cn = [ inputblock ] ;
23444 cfg.cn.push(boxLabelCfg);
23452 if(this.inputType != 'radio'){
23453 cfg.cn.push(hidden);
23461 * return the real input element.
23463 inputEl: function ()
23465 return this.el.select('input.roo-' + this.inputType,true).first();
23467 hiddenEl: function ()
23469 return this.el.select('input.roo-hidden-value',true).first();
23472 labelEl: function()
23474 return this.el.select('label.control-label',true).first();
23476 /* depricated... */
23480 return this.labelEl();
23483 boxLabelEl: function()
23485 return this.el.select('label.box-label',true).first();
23488 initEvents : function()
23490 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23492 this.inputEl().on('click', this.onClick, this);
23494 if (this.boxLabel) {
23495 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23498 this.startValue = this.getValue();
23501 Roo.bootstrap.CheckBox.register(this);
23505 onClick : function(e)
23507 if(this.fireEvent('click', this, e) !== false){
23508 this.setChecked(!this.checked);
23513 setChecked : function(state,suppressEvent)
23515 this.startValue = this.getValue();
23517 if(this.inputType == 'radio'){
23519 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23520 e.dom.checked = false;
23523 this.inputEl().dom.checked = true;
23525 this.inputEl().dom.value = this.inputValue;
23527 if(suppressEvent !== true){
23528 this.fireEvent('check', this, true);
23536 this.checked = state;
23538 this.inputEl().dom.checked = state;
23541 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23543 if(suppressEvent !== true){
23544 this.fireEvent('check', this, state);
23550 getValue : function()
23552 if(this.inputType == 'radio'){
23553 return this.getGroupValue();
23556 return this.hiddenEl().dom.value;
23560 getGroupValue : function()
23562 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23566 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23569 setValue : function(v,suppressEvent)
23571 if(this.inputType == 'radio'){
23572 this.setGroupValue(v, suppressEvent);
23576 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23581 setGroupValue : function(v, suppressEvent)
23583 this.startValue = this.getValue();
23585 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23586 e.dom.checked = false;
23588 if(e.dom.value == v){
23589 e.dom.checked = true;
23593 if(suppressEvent !== true){
23594 this.fireEvent('check', this, true);
23602 validate : function()
23604 if(this.getVisibilityEl().hasClass('hidden')){
23610 (this.inputType == 'radio' && this.validateRadio()) ||
23611 (this.inputType == 'checkbox' && this.validateCheckbox())
23617 this.markInvalid();
23621 validateRadio : function()
23623 if(this.getVisibilityEl().hasClass('hidden')){
23627 if(this.allowBlank){
23633 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23634 if(!e.dom.checked){
23646 validateCheckbox : function()
23649 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23650 //return (this.getValue() == this.inputValue) ? true : false;
23653 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23661 for(var i in group){
23662 if(group[i].el.isVisible(true)){
23670 for(var i in group){
23675 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23682 * Mark this field as valid
23684 markValid : function()
23688 this.fireEvent('valid', this);
23690 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23693 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23700 if(this.inputType == 'radio'){
23701 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23702 var fg = e.findParent('.form-group', false, true);
23703 if (Roo.bootstrap.version == 3) {
23704 fg.removeClass([_this.invalidClass, _this.validClass]);
23705 fg.addClass(_this.validClass);
23707 fg.removeClass(['is-valid', 'is-invalid']);
23708 fg.addClass('is-valid');
23716 var fg = this.el.findParent('.form-group', false, true);
23717 if (Roo.bootstrap.version == 3) {
23718 fg.removeClass([this.invalidClass, this.validClass]);
23719 fg.addClass(this.validClass);
23721 fg.removeClass(['is-valid', 'is-invalid']);
23722 fg.addClass('is-valid');
23727 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23733 for(var i in group){
23734 var fg = group[i].el.findParent('.form-group', false, true);
23735 if (Roo.bootstrap.version == 3) {
23736 fg.removeClass([this.invalidClass, this.validClass]);
23737 fg.addClass(this.validClass);
23739 fg.removeClass(['is-valid', 'is-invalid']);
23740 fg.addClass('is-valid');
23746 * Mark this field as invalid
23747 * @param {String} msg The validation message
23749 markInvalid : function(msg)
23751 if(this.allowBlank){
23757 this.fireEvent('invalid', this, msg);
23759 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23762 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23766 label.markInvalid();
23769 if(this.inputType == 'radio'){
23771 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23772 var fg = e.findParent('.form-group', false, true);
23773 if (Roo.bootstrap.version == 3) {
23774 fg.removeClass([_this.invalidClass, _this.validClass]);
23775 fg.addClass(_this.invalidClass);
23777 fg.removeClass(['is-invalid', 'is-valid']);
23778 fg.addClass('is-invalid');
23786 var fg = this.el.findParent('.form-group', false, true);
23787 if (Roo.bootstrap.version == 3) {
23788 fg.removeClass([_this.invalidClass, _this.validClass]);
23789 fg.addClass(_this.invalidClass);
23791 fg.removeClass(['is-invalid', 'is-valid']);
23792 fg.addClass('is-invalid');
23797 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23803 for(var i in group){
23804 var fg = group[i].el.findParent('.form-group', false, true);
23805 if (Roo.bootstrap.version == 3) {
23806 fg.removeClass([_this.invalidClass, _this.validClass]);
23807 fg.addClass(_this.invalidClass);
23809 fg.removeClass(['is-invalid', 'is-valid']);
23810 fg.addClass('is-invalid');
23816 clearInvalid : function()
23818 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23820 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23822 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23824 if (label && label.iconEl) {
23825 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23826 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23830 disable : function()
23832 if(this.inputType != 'radio'){
23833 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23840 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23841 _this.getActionEl().addClass(this.disabledClass);
23842 e.dom.disabled = true;
23846 this.disabled = true;
23847 this.fireEvent("disable", this);
23851 enable : function()
23853 if(this.inputType != 'radio'){
23854 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23861 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23862 _this.getActionEl().removeClass(this.disabledClass);
23863 e.dom.disabled = false;
23867 this.disabled = false;
23868 this.fireEvent("enable", this);
23872 setBoxLabel : function(v)
23877 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23883 Roo.apply(Roo.bootstrap.CheckBox, {
23888 * register a CheckBox Group
23889 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23891 register : function(checkbox)
23893 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23894 this.groups[checkbox.groupId] = {};
23897 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23901 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23905 * fetch a CheckBox Group based on the group ID
23906 * @param {string} the group ID
23907 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23909 get: function(groupId) {
23910 if (typeof(this.groups[groupId]) == 'undefined') {
23914 return this.groups[groupId] ;
23927 * @class Roo.bootstrap.Radio
23928 * @extends Roo.bootstrap.Component
23929 * Bootstrap Radio class
23930 * @cfg {String} boxLabel - the label associated
23931 * @cfg {String} value - the value of radio
23934 * Create a new Radio
23935 * @param {Object} config The config object
23937 Roo.bootstrap.Radio = function(config){
23938 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23942 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23948 getAutoCreate : function()
23952 cls : 'form-group radio',
23957 html : this.boxLabel
23965 initEvents : function()
23967 this.parent().register(this);
23969 this.el.on('click', this.onClick, this);
23973 onClick : function(e)
23975 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23976 this.setChecked(true);
23980 setChecked : function(state, suppressEvent)
23982 this.parent().setValue(this.value, suppressEvent);
23986 setBoxLabel : function(v)
23991 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24006 * @class Roo.bootstrap.SecurePass
24007 * @extends Roo.bootstrap.Input
24008 * Bootstrap SecurePass class
24012 * Create a new SecurePass
24013 * @param {Object} config The config object
24016 Roo.bootstrap.SecurePass = function (config) {
24017 // these go here, so the translation tool can replace them..
24019 PwdEmpty: "Please type a password, and then retype it to confirm.",
24020 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24021 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24022 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24023 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24024 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24025 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24026 TooWeak: "Your password is Too Weak."
24028 this.meterLabel = "Password strength:";
24029 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24030 this.meterClass = [
24031 "roo-password-meter-tooweak",
24032 "roo-password-meter-weak",
24033 "roo-password-meter-medium",
24034 "roo-password-meter-strong",
24035 "roo-password-meter-grey"
24040 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24043 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24045 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24047 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24048 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24049 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24050 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24051 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24052 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24053 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24063 * @cfg {String/Object} Label for the strength meter (defaults to
24064 * 'Password strength:')
24069 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24070 * ['Weak', 'Medium', 'Strong'])
24073 pwdStrengths: false,
24086 initEvents: function ()
24088 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24090 if (this.el.is('input[type=password]') && Roo.isSafari) {
24091 this.el.on('keydown', this.SafariOnKeyDown, this);
24094 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24097 onRender: function (ct, position)
24099 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24100 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24101 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24103 this.trigger.createChild({
24108 cls: 'roo-password-meter-grey col-xs-12',
24111 //width: this.meterWidth + 'px'
24115 cls: 'roo-password-meter-text'
24121 if (this.hideTrigger) {
24122 this.trigger.setDisplayed(false);
24124 this.setSize(this.width || '', this.height || '');
24127 onDestroy: function ()
24129 if (this.trigger) {
24130 this.trigger.removeAllListeners();
24131 this.trigger.remove();
24134 this.wrap.remove();
24136 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24139 checkStrength: function ()
24141 var pwd = this.inputEl().getValue();
24142 if (pwd == this._lastPwd) {
24147 if (this.ClientSideStrongPassword(pwd)) {
24149 } else if (this.ClientSideMediumPassword(pwd)) {
24151 } else if (this.ClientSideWeakPassword(pwd)) {
24157 Roo.log('strength1: ' + strength);
24159 //var pm = this.trigger.child('div/div/div').dom;
24160 var pm = this.trigger.child('div/div');
24161 pm.removeClass(this.meterClass);
24162 pm.addClass(this.meterClass[strength]);
24165 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24167 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24169 this._lastPwd = pwd;
24173 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24175 this._lastPwd = '';
24177 var pm = this.trigger.child('div/div');
24178 pm.removeClass(this.meterClass);
24179 pm.addClass('roo-password-meter-grey');
24182 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24185 this.inputEl().dom.type='password';
24188 validateValue: function (value)
24190 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24193 if (value.length == 0) {
24194 if (this.allowBlank) {
24195 this.clearInvalid();
24199 this.markInvalid(this.errors.PwdEmpty);
24200 this.errorMsg = this.errors.PwdEmpty;
24208 if (!value.match(/[\x21-\x7e]+/)) {
24209 this.markInvalid(this.errors.PwdBadChar);
24210 this.errorMsg = this.errors.PwdBadChar;
24213 if (value.length < 6) {
24214 this.markInvalid(this.errors.PwdShort);
24215 this.errorMsg = this.errors.PwdShort;
24218 if (value.length > 16) {
24219 this.markInvalid(this.errors.PwdLong);
24220 this.errorMsg = this.errors.PwdLong;
24224 if (this.ClientSideStrongPassword(value)) {
24226 } else if (this.ClientSideMediumPassword(value)) {
24228 } else if (this.ClientSideWeakPassword(value)) {
24235 if (strength < 2) {
24236 //this.markInvalid(this.errors.TooWeak);
24237 this.errorMsg = this.errors.TooWeak;
24242 console.log('strength2: ' + strength);
24244 //var pm = this.trigger.child('div/div/div').dom;
24246 var pm = this.trigger.child('div/div');
24247 pm.removeClass(this.meterClass);
24248 pm.addClass(this.meterClass[strength]);
24250 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24252 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24254 this.errorMsg = '';
24258 CharacterSetChecks: function (type)
24261 this.fResult = false;
24264 isctype: function (character, type)
24267 case this.kCapitalLetter:
24268 if (character >= 'A' && character <= 'Z') {
24273 case this.kSmallLetter:
24274 if (character >= 'a' && character <= 'z') {
24280 if (character >= '0' && character <= '9') {
24285 case this.kPunctuation:
24286 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24297 IsLongEnough: function (pwd, size)
24299 return !(pwd == null || isNaN(size) || pwd.length < size);
24302 SpansEnoughCharacterSets: function (word, nb)
24304 if (!this.IsLongEnough(word, nb))
24309 var characterSetChecks = new Array(
24310 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24311 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24314 for (var index = 0; index < word.length; ++index) {
24315 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24316 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24317 characterSetChecks[nCharSet].fResult = true;
24324 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24325 if (characterSetChecks[nCharSet].fResult) {
24330 if (nCharSets < nb) {
24336 ClientSideStrongPassword: function (pwd)
24338 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24341 ClientSideMediumPassword: function (pwd)
24343 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24346 ClientSideWeakPassword: function (pwd)
24348 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24351 })//<script type="text/javascript">
24354 * Based Ext JS Library 1.1.1
24355 * Copyright(c) 2006-2007, Ext JS, LLC.
24361 * @class Roo.HtmlEditorCore
24362 * @extends Roo.Component
24363 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24365 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24368 Roo.HtmlEditorCore = function(config){
24371 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24376 * @event initialize
24377 * Fires when the editor is fully initialized (including the iframe)
24378 * @param {Roo.HtmlEditorCore} this
24383 * Fires when the editor is first receives the focus. Any insertion must wait
24384 * until after this event.
24385 * @param {Roo.HtmlEditorCore} this
24389 * @event beforesync
24390 * Fires before the textarea is updated with content from the editor iframe. Return false
24391 * to cancel the sync.
24392 * @param {Roo.HtmlEditorCore} this
24393 * @param {String} html
24397 * @event beforepush
24398 * Fires before the iframe editor is updated with content from the textarea. Return false
24399 * to cancel the push.
24400 * @param {Roo.HtmlEditorCore} this
24401 * @param {String} html
24406 * Fires when the textarea is updated with content from the editor iframe.
24407 * @param {Roo.HtmlEditorCore} this
24408 * @param {String} html
24413 * Fires when the iframe editor is updated with content from the textarea.
24414 * @param {Roo.HtmlEditorCore} this
24415 * @param {String} html
24420 * @event editorevent
24421 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24422 * @param {Roo.HtmlEditorCore} this
24428 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24430 // defaults : white / black...
24431 this.applyBlacklists();
24438 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24442 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24448 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24453 * @cfg {Number} height (in pixels)
24457 * @cfg {Number} width (in pixels)
24462 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24465 stylesheets: false,
24470 // private properties
24471 validationEvent : false,
24473 initialized : false,
24475 sourceEditMode : false,
24476 onFocus : Roo.emptyFn,
24478 hideMode:'offsets',
24482 // blacklist + whitelisted elements..
24489 * Protected method that will not generally be called directly. It
24490 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24491 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24493 getDocMarkup : function(){
24497 // inherit styels from page...??
24498 if (this.stylesheets === false) {
24500 Roo.get(document.head).select('style').each(function(node) {
24501 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24504 Roo.get(document.head).select('link').each(function(node) {
24505 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24508 } else if (!this.stylesheets.length) {
24510 st = '<style type="text/css">' +
24511 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24514 for (var i in this.stylesheets) {
24515 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24520 st += '<style type="text/css">' +
24521 'IMG { cursor: pointer } ' +
24524 var cls = 'roo-htmleditor-body';
24526 if(this.bodyCls.length){
24527 cls += ' ' + this.bodyCls;
24530 return '<html><head>' + st +
24531 //<style type="text/css">' +
24532 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24534 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24538 onRender : function(ct, position)
24541 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24542 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24545 this.el.dom.style.border = '0 none';
24546 this.el.dom.setAttribute('tabIndex', -1);
24547 this.el.addClass('x-hidden hide');
24551 if(Roo.isIE){ // fix IE 1px bogus margin
24552 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24556 this.frameId = Roo.id();
24560 var iframe = this.owner.wrap.createChild({
24562 cls: 'form-control', // bootstrap..
24564 name: this.frameId,
24565 frameBorder : 'no',
24566 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24571 this.iframe = iframe.dom;
24573 this.assignDocWin();
24575 this.doc.designMode = 'on';
24578 this.doc.write(this.getDocMarkup());
24582 var task = { // must defer to wait for browser to be ready
24584 //console.log("run task?" + this.doc.readyState);
24585 this.assignDocWin();
24586 if(this.doc.body || this.doc.readyState == 'complete'){
24588 this.doc.designMode="on";
24592 Roo.TaskMgr.stop(task);
24593 this.initEditor.defer(10, this);
24600 Roo.TaskMgr.start(task);
24605 onResize : function(w, h)
24607 Roo.log('resize: ' +w + ',' + h );
24608 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24612 if(typeof w == 'number'){
24614 this.iframe.style.width = w + 'px';
24616 if(typeof h == 'number'){
24618 this.iframe.style.height = h + 'px';
24620 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24627 * Toggles the editor between standard and source edit mode.
24628 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24630 toggleSourceEdit : function(sourceEditMode){
24632 this.sourceEditMode = sourceEditMode === true;
24634 if(this.sourceEditMode){
24636 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24639 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24640 //this.iframe.className = '';
24643 //this.setSize(this.owner.wrap.getSize());
24644 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24651 * Protected method that will not generally be called directly. If you need/want
24652 * custom HTML cleanup, this is the method you should override.
24653 * @param {String} html The HTML to be cleaned
24654 * return {String} The cleaned HTML
24656 cleanHtml : function(html){
24657 html = String(html);
24658 if(html.length > 5){
24659 if(Roo.isSafari){ // strip safari nonsense
24660 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24663 if(html == ' '){
24670 * HTML Editor -> Textarea
24671 * Protected method that will not generally be called directly. Syncs the contents
24672 * of the editor iframe with the textarea.
24674 syncValue : function(){
24675 if(this.initialized){
24676 var bd = (this.doc.body || this.doc.documentElement);
24677 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24678 var html = bd.innerHTML;
24680 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24681 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24683 html = '<div style="'+m[0]+'">' + html + '</div>';
24686 html = this.cleanHtml(html);
24687 // fix up the special chars.. normaly like back quotes in word...
24688 // however we do not want to do this with chinese..
24689 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24691 var cc = match.charCodeAt();
24693 // Get the character value, handling surrogate pairs
24694 if (match.length == 2) {
24695 // It's a surrogate pair, calculate the Unicode code point
24696 var high = match.charCodeAt(0) - 0xD800;
24697 var low = match.charCodeAt(1) - 0xDC00;
24698 cc = (high * 0x400) + low + 0x10000;
24700 (cc >= 0x4E00 && cc < 0xA000 ) ||
24701 (cc >= 0x3400 && cc < 0x4E00 ) ||
24702 (cc >= 0xf900 && cc < 0xfb00 )
24707 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24708 return "&#" + cc + ";";
24715 if(this.owner.fireEvent('beforesync', this, html) !== false){
24716 this.el.dom.value = html;
24717 this.owner.fireEvent('sync', this, html);
24723 * Protected method that will not generally be called directly. Pushes the value of the textarea
24724 * into the iframe editor.
24726 pushValue : function(){
24727 if(this.initialized){
24728 var v = this.el.dom.value.trim();
24730 // if(v.length < 1){
24734 if(this.owner.fireEvent('beforepush', this, v) !== false){
24735 var d = (this.doc.body || this.doc.documentElement);
24737 this.cleanUpPaste();
24738 this.el.dom.value = d.innerHTML;
24739 this.owner.fireEvent('push', this, v);
24745 deferFocus : function(){
24746 this.focus.defer(10, this);
24750 focus : function(){
24751 if(this.win && !this.sourceEditMode){
24758 assignDocWin: function()
24760 var iframe = this.iframe;
24763 this.doc = iframe.contentWindow.document;
24764 this.win = iframe.contentWindow;
24766 // if (!Roo.get(this.frameId)) {
24769 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24770 // this.win = Roo.get(this.frameId).dom.contentWindow;
24772 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24776 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24777 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24782 initEditor : function(){
24783 //console.log("INIT EDITOR");
24784 this.assignDocWin();
24788 this.doc.designMode="on";
24790 this.doc.write(this.getDocMarkup());
24793 var dbody = (this.doc.body || this.doc.documentElement);
24794 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24795 // this copies styles from the containing element into thsi one..
24796 // not sure why we need all of this..
24797 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24799 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24800 //ss['background-attachment'] = 'fixed'; // w3c
24801 dbody.bgProperties = 'fixed'; // ie
24802 //Roo.DomHelper.applyStyles(dbody, ss);
24803 Roo.EventManager.on(this.doc, {
24804 //'mousedown': this.onEditorEvent,
24805 'mouseup': this.onEditorEvent,
24806 'dblclick': this.onEditorEvent,
24807 'click': this.onEditorEvent,
24808 'keyup': this.onEditorEvent,
24813 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24815 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24816 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24818 this.initialized = true;
24820 this.owner.fireEvent('initialize', this);
24825 onDestroy : function(){
24831 //for (var i =0; i < this.toolbars.length;i++) {
24832 // // fixme - ask toolbars for heights?
24833 // this.toolbars[i].onDestroy();
24836 //this.wrap.dom.innerHTML = '';
24837 //this.wrap.remove();
24842 onFirstFocus : function(){
24844 this.assignDocWin();
24847 this.activated = true;
24850 if(Roo.isGecko){ // prevent silly gecko errors
24852 var s = this.win.getSelection();
24853 if(!s.focusNode || s.focusNode.nodeType != 3){
24854 var r = s.getRangeAt(0);
24855 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24860 this.execCmd('useCSS', true);
24861 this.execCmd('styleWithCSS', false);
24864 this.owner.fireEvent('activate', this);
24868 adjustFont: function(btn){
24869 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24870 //if(Roo.isSafari){ // safari
24873 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24874 if(Roo.isSafari){ // safari
24875 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24876 v = (v < 10) ? 10 : v;
24877 v = (v > 48) ? 48 : v;
24878 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24883 v = Math.max(1, v+adjust);
24885 this.execCmd('FontSize', v );
24888 onEditorEvent : function(e)
24890 this.owner.fireEvent('editorevent', this, e);
24891 // this.updateToolbar();
24892 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24895 insertTag : function(tg)
24897 // could be a bit smarter... -> wrap the current selected tRoo..
24898 if (tg.toLowerCase() == 'span' ||
24899 tg.toLowerCase() == 'code' ||
24900 tg.toLowerCase() == 'sup' ||
24901 tg.toLowerCase() == 'sub'
24904 range = this.createRange(this.getSelection());
24905 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24906 wrappingNode.appendChild(range.extractContents());
24907 range.insertNode(wrappingNode);
24914 this.execCmd("formatblock", tg);
24918 insertText : function(txt)
24922 var range = this.createRange();
24923 range.deleteContents();
24924 //alert(Sender.getAttribute('label'));
24926 range.insertNode(this.doc.createTextNode(txt));
24932 * Executes a Midas editor command on the editor document and performs necessary focus and
24933 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24934 * @param {String} cmd The Midas command
24935 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24937 relayCmd : function(cmd, value){
24939 this.execCmd(cmd, value);
24940 this.owner.fireEvent('editorevent', this);
24941 //this.updateToolbar();
24942 this.owner.deferFocus();
24946 * Executes a Midas editor command directly on the editor document.
24947 * For visual commands, you should use {@link #relayCmd} instead.
24948 * <b>This should only be called after the editor is initialized.</b>
24949 * @param {String} cmd The Midas command
24950 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24952 execCmd : function(cmd, value){
24953 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24960 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24962 * @param {String} text | dom node..
24964 insertAtCursor : function(text)
24967 if(!this.activated){
24973 var r = this.doc.selection.createRange();
24984 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24988 // from jquery ui (MIT licenced)
24990 var win = this.win;
24992 if (win.getSelection && win.getSelection().getRangeAt) {
24993 range = win.getSelection().getRangeAt(0);
24994 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24995 range.insertNode(node);
24996 } else if (win.document.selection && win.document.selection.createRange) {
24997 // no firefox support
24998 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24999 win.document.selection.createRange().pasteHTML(txt);
25001 // no firefox support
25002 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25003 this.execCmd('InsertHTML', txt);
25012 mozKeyPress : function(e){
25014 var c = e.getCharCode(), cmd;
25017 c = String.fromCharCode(c).toLowerCase();
25031 this.cleanUpPaste.defer(100, this);
25039 e.preventDefault();
25047 fixKeys : function(){ // load time branching for fastest keydown performance
25049 return function(e){
25050 var k = e.getKey(), r;
25053 r = this.doc.selection.createRange();
25056 r.pasteHTML('    ');
25063 r = this.doc.selection.createRange();
25065 var target = r.parentElement();
25066 if(!target || target.tagName.toLowerCase() != 'li'){
25068 r.pasteHTML('<br />');
25074 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25075 this.cleanUpPaste.defer(100, this);
25081 }else if(Roo.isOpera){
25082 return function(e){
25083 var k = e.getKey();
25087 this.execCmd('InsertHTML','    ');
25090 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25091 this.cleanUpPaste.defer(100, this);
25096 }else if(Roo.isSafari){
25097 return function(e){
25098 var k = e.getKey();
25102 this.execCmd('InsertText','\t');
25106 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25107 this.cleanUpPaste.defer(100, this);
25115 getAllAncestors: function()
25117 var p = this.getSelectedNode();
25120 a.push(p); // push blank onto stack..
25121 p = this.getParentElement();
25125 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25129 a.push(this.doc.body);
25133 lastSelNode : false,
25136 getSelection : function()
25138 this.assignDocWin();
25139 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25142 getSelectedNode: function()
25144 // this may only work on Gecko!!!
25146 // should we cache this!!!!
25151 var range = this.createRange(this.getSelection()).cloneRange();
25154 var parent = range.parentElement();
25156 var testRange = range.duplicate();
25157 testRange.moveToElementText(parent);
25158 if (testRange.inRange(range)) {
25161 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25164 parent = parent.parentElement;
25169 // is ancestor a text element.
25170 var ac = range.commonAncestorContainer;
25171 if (ac.nodeType == 3) {
25172 ac = ac.parentNode;
25175 var ar = ac.childNodes;
25178 var other_nodes = [];
25179 var has_other_nodes = false;
25180 for (var i=0;i<ar.length;i++) {
25181 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25184 // fullly contained node.
25186 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25191 // probably selected..
25192 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25193 other_nodes.push(ar[i]);
25197 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25202 has_other_nodes = true;
25204 if (!nodes.length && other_nodes.length) {
25205 nodes= other_nodes;
25207 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25213 createRange: function(sel)
25215 // this has strange effects when using with
25216 // top toolbar - not sure if it's a great idea.
25217 //this.editor.contentWindow.focus();
25218 if (typeof sel != "undefined") {
25220 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25222 return this.doc.createRange();
25225 return this.doc.createRange();
25228 getParentElement: function()
25231 this.assignDocWin();
25232 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25234 var range = this.createRange(sel);
25237 var p = range.commonAncestorContainer;
25238 while (p.nodeType == 3) { // text node
25249 * Range intersection.. the hard stuff...
25253 * [ -- selected range --- ]
25257 * if end is before start or hits it. fail.
25258 * if start is after end or hits it fail.
25260 * if either hits (but other is outside. - then it's not
25266 // @see http://www.thismuchiknow.co.uk/?p=64.
25267 rangeIntersectsNode : function(range, node)
25269 var nodeRange = node.ownerDocument.createRange();
25271 nodeRange.selectNode(node);
25273 nodeRange.selectNodeContents(node);
25276 var rangeStartRange = range.cloneRange();
25277 rangeStartRange.collapse(true);
25279 var rangeEndRange = range.cloneRange();
25280 rangeEndRange.collapse(false);
25282 var nodeStartRange = nodeRange.cloneRange();
25283 nodeStartRange.collapse(true);
25285 var nodeEndRange = nodeRange.cloneRange();
25286 nodeEndRange.collapse(false);
25288 return rangeStartRange.compareBoundaryPoints(
25289 Range.START_TO_START, nodeEndRange) == -1 &&
25290 rangeEndRange.compareBoundaryPoints(
25291 Range.START_TO_START, nodeStartRange) == 1;
25295 rangeCompareNode : function(range, node)
25297 var nodeRange = node.ownerDocument.createRange();
25299 nodeRange.selectNode(node);
25301 nodeRange.selectNodeContents(node);
25305 range.collapse(true);
25307 nodeRange.collapse(true);
25309 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25310 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25312 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25314 var nodeIsBefore = ss == 1;
25315 var nodeIsAfter = ee == -1;
25317 if (nodeIsBefore && nodeIsAfter) {
25320 if (!nodeIsBefore && nodeIsAfter) {
25321 return 1; //right trailed.
25324 if (nodeIsBefore && !nodeIsAfter) {
25325 return 2; // left trailed.
25331 // private? - in a new class?
25332 cleanUpPaste : function()
25334 // cleans up the whole document..
25335 Roo.log('cleanuppaste');
25337 this.cleanUpChildren(this.doc.body);
25338 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25339 if (clean != this.doc.body.innerHTML) {
25340 this.doc.body.innerHTML = clean;
25345 cleanWordChars : function(input) {// change the chars to hex code
25346 var he = Roo.HtmlEditorCore;
25348 var output = input;
25349 Roo.each(he.swapCodes, function(sw) {
25350 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25352 output = output.replace(swapper, sw[1]);
25359 cleanUpChildren : function (n)
25361 if (!n.childNodes.length) {
25364 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25365 this.cleanUpChild(n.childNodes[i]);
25372 cleanUpChild : function (node)
25375 //console.log(node);
25376 if (node.nodeName == "#text") {
25377 // clean up silly Windows -- stuff?
25380 if (node.nodeName == "#comment") {
25381 node.parentNode.removeChild(node);
25382 // clean up silly Windows -- stuff?
25385 var lcname = node.tagName.toLowerCase();
25386 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25387 // whitelist of tags..
25389 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25391 node.parentNode.removeChild(node);
25396 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25398 // spans with no attributes - just remove them..
25399 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25400 remove_keep_children = true;
25403 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25404 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25406 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25407 // remove_keep_children = true;
25410 if (remove_keep_children) {
25411 this.cleanUpChildren(node);
25412 // inserts everything just before this node...
25413 while (node.childNodes.length) {
25414 var cn = node.childNodes[0];
25415 node.removeChild(cn);
25416 node.parentNode.insertBefore(cn, node);
25418 node.parentNode.removeChild(node);
25422 if (!node.attributes || !node.attributes.length) {
25427 this.cleanUpChildren(node);
25431 function cleanAttr(n,v)
25434 if (v.match(/^\./) || v.match(/^\//)) {
25437 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25440 if (v.match(/^#/)) {
25443 if (v.match(/^\{/)) { // allow template editing.
25446 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25447 node.removeAttribute(n);
25451 var cwhite = this.cwhite;
25452 var cblack = this.cblack;
25454 function cleanStyle(n,v)
25456 if (v.match(/expression/)) { //XSS?? should we even bother..
25457 node.removeAttribute(n);
25461 var parts = v.split(/;/);
25464 Roo.each(parts, function(p) {
25465 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25469 var l = p.split(':').shift().replace(/\s+/g,'');
25470 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25472 if ( cwhite.length && cblack.indexOf(l) > -1) {
25473 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25474 //node.removeAttribute(n);
25478 // only allow 'c whitelisted system attributes'
25479 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25480 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25481 //node.removeAttribute(n);
25491 if (clean.length) {
25492 node.setAttribute(n, clean.join(';'));
25494 node.removeAttribute(n);
25500 for (var i = node.attributes.length-1; i > -1 ; i--) {
25501 var a = node.attributes[i];
25504 if (a.name.toLowerCase().substr(0,2)=='on') {
25505 node.removeAttribute(a.name);
25508 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25509 node.removeAttribute(a.name);
25512 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25513 cleanAttr(a.name,a.value); // fixme..
25516 if (a.name == 'style') {
25517 cleanStyle(a.name,a.value);
25520 /// clean up MS crap..
25521 // tecnically this should be a list of valid class'es..
25524 if (a.name == 'class') {
25525 if (a.value.match(/^Mso/)) {
25526 node.removeAttribute('class');
25529 if (a.value.match(/^body$/)) {
25530 node.removeAttribute('class');
25541 this.cleanUpChildren(node);
25547 * Clean up MS wordisms...
25549 cleanWord : function(node)
25552 this.cleanWord(this.doc.body);
25557 node.nodeName == 'SPAN' &&
25558 !node.hasAttributes() &&
25559 node.childNodes.length == 1 &&
25560 node.firstChild.nodeName == "#text"
25562 var textNode = node.firstChild;
25563 node.removeChild(textNode);
25564 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25565 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25567 node.parentNode.insertBefore(textNode, node);
25568 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25569 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25571 node.parentNode.removeChild(node);
25574 if (node.nodeName == "#text") {
25575 // clean up silly Windows -- stuff?
25578 if (node.nodeName == "#comment") {
25579 node.parentNode.removeChild(node);
25580 // clean up silly Windows -- stuff?
25584 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25585 node.parentNode.removeChild(node);
25588 //Roo.log(node.tagName);
25589 // remove - but keep children..
25590 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25591 //Roo.log('-- removed');
25592 while (node.childNodes.length) {
25593 var cn = node.childNodes[0];
25594 node.removeChild(cn);
25595 node.parentNode.insertBefore(cn, node);
25596 // move node to parent - and clean it..
25597 this.cleanWord(cn);
25599 node.parentNode.removeChild(node);
25600 /// no need to iterate chidlren = it's got none..
25601 //this.iterateChildren(node, this.cleanWord);
25605 if (node.className.length) {
25607 var cn = node.className.split(/\W+/);
25609 Roo.each(cn, function(cls) {
25610 if (cls.match(/Mso[a-zA-Z]+/)) {
25615 node.className = cna.length ? cna.join(' ') : '';
25617 node.removeAttribute("class");
25621 if (node.hasAttribute("lang")) {
25622 node.removeAttribute("lang");
25625 if (node.hasAttribute("style")) {
25627 var styles = node.getAttribute("style").split(";");
25629 Roo.each(styles, function(s) {
25630 if (!s.match(/:/)) {
25633 var kv = s.split(":");
25634 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25637 // what ever is left... we allow.
25640 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25641 if (!nstyle.length) {
25642 node.removeAttribute('style');
25645 this.iterateChildren(node, this.cleanWord);
25651 * iterateChildren of a Node, calling fn each time, using this as the scole..
25652 * @param {DomNode} node node to iterate children of.
25653 * @param {Function} fn method of this class to call on each item.
25655 iterateChildren : function(node, fn)
25657 if (!node.childNodes.length) {
25660 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25661 fn.call(this, node.childNodes[i])
25667 * cleanTableWidths.
25669 * Quite often pasting from word etc.. results in tables with column and widths.
25670 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25673 cleanTableWidths : function(node)
25678 this.cleanTableWidths(this.doc.body);
25683 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25686 Roo.log(node.tagName);
25687 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25688 this.iterateChildren(node, this.cleanTableWidths);
25691 if (node.hasAttribute('width')) {
25692 node.removeAttribute('width');
25696 if (node.hasAttribute("style")) {
25699 var styles = node.getAttribute("style").split(";");
25701 Roo.each(styles, function(s) {
25702 if (!s.match(/:/)) {
25705 var kv = s.split(":");
25706 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25709 // what ever is left... we allow.
25712 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25713 if (!nstyle.length) {
25714 node.removeAttribute('style');
25718 this.iterateChildren(node, this.cleanTableWidths);
25726 domToHTML : function(currentElement, depth, nopadtext) {
25728 depth = depth || 0;
25729 nopadtext = nopadtext || false;
25731 if (!currentElement) {
25732 return this.domToHTML(this.doc.body);
25735 //Roo.log(currentElement);
25737 var allText = false;
25738 var nodeName = currentElement.nodeName;
25739 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25741 if (nodeName == '#text') {
25743 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25748 if (nodeName != 'BODY') {
25751 // Prints the node tagName, such as <A>, <IMG>, etc
25754 for(i = 0; i < currentElement.attributes.length;i++) {
25756 var aname = currentElement.attributes.item(i).name;
25757 if (!currentElement.attributes.item(i).value.length) {
25760 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25763 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25772 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25775 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25780 // Traverse the tree
25782 var currentElementChild = currentElement.childNodes.item(i);
25783 var allText = true;
25784 var innerHTML = '';
25786 while (currentElementChild) {
25787 // Formatting code (indent the tree so it looks nice on the screen)
25788 var nopad = nopadtext;
25789 if (lastnode == 'SPAN') {
25793 if (currentElementChild.nodeName == '#text') {
25794 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25795 toadd = nopadtext ? toadd : toadd.trim();
25796 if (!nopad && toadd.length > 80) {
25797 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25799 innerHTML += toadd;
25802 currentElementChild = currentElement.childNodes.item(i);
25808 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25810 // Recursively traverse the tree structure of the child node
25811 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25812 lastnode = currentElementChild.nodeName;
25814 currentElementChild=currentElement.childNodes.item(i);
25820 // The remaining code is mostly for formatting the tree
25821 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25826 ret+= "</"+tagName+">";
25832 applyBlacklists : function()
25834 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25835 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25839 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25840 if (b.indexOf(tag) > -1) {
25843 this.white.push(tag);
25847 Roo.each(w, function(tag) {
25848 if (b.indexOf(tag) > -1) {
25851 if (this.white.indexOf(tag) > -1) {
25854 this.white.push(tag);
25859 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25860 if (w.indexOf(tag) > -1) {
25863 this.black.push(tag);
25867 Roo.each(b, function(tag) {
25868 if (w.indexOf(tag) > -1) {
25871 if (this.black.indexOf(tag) > -1) {
25874 this.black.push(tag);
25879 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25880 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25884 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25885 if (b.indexOf(tag) > -1) {
25888 this.cwhite.push(tag);
25892 Roo.each(w, function(tag) {
25893 if (b.indexOf(tag) > -1) {
25896 if (this.cwhite.indexOf(tag) > -1) {
25899 this.cwhite.push(tag);
25904 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25905 if (w.indexOf(tag) > -1) {
25908 this.cblack.push(tag);
25912 Roo.each(b, function(tag) {
25913 if (w.indexOf(tag) > -1) {
25916 if (this.cblack.indexOf(tag) > -1) {
25919 this.cblack.push(tag);
25924 setStylesheets : function(stylesheets)
25926 if(typeof(stylesheets) == 'string'){
25927 Roo.get(this.iframe.contentDocument.head).createChild({
25929 rel : 'stylesheet',
25938 Roo.each(stylesheets, function(s) {
25943 Roo.get(_this.iframe.contentDocument.head).createChild({
25945 rel : 'stylesheet',
25954 removeStylesheets : function()
25958 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25963 setStyle : function(style)
25965 Roo.get(this.iframe.contentDocument.head).createChild({
25974 // hide stuff that is not compatible
25988 * @event specialkey
25992 * @cfg {String} fieldClass @hide
25995 * @cfg {String} focusClass @hide
25998 * @cfg {String} autoCreate @hide
26001 * @cfg {String} inputType @hide
26004 * @cfg {String} invalidClass @hide
26007 * @cfg {String} invalidText @hide
26010 * @cfg {String} msgFx @hide
26013 * @cfg {String} validateOnBlur @hide
26017 Roo.HtmlEditorCore.white = [
26018 'area', 'br', 'img', 'input', 'hr', 'wbr',
26020 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26021 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26022 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26023 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26024 'table', 'ul', 'xmp',
26026 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26029 'dir', 'menu', 'ol', 'ul', 'dl',
26035 Roo.HtmlEditorCore.black = [
26036 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26038 'base', 'basefont', 'bgsound', 'blink', 'body',
26039 'frame', 'frameset', 'head', 'html', 'ilayer',
26040 'iframe', 'layer', 'link', 'meta', 'object',
26041 'script', 'style' ,'title', 'xml' // clean later..
26043 Roo.HtmlEditorCore.clean = [
26044 'script', 'style', 'title', 'xml'
26046 Roo.HtmlEditorCore.remove = [
26051 Roo.HtmlEditorCore.ablack = [
26055 Roo.HtmlEditorCore.aclean = [
26056 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26060 Roo.HtmlEditorCore.pwhite= [
26061 'http', 'https', 'mailto'
26064 // white listed style attributes.
26065 Roo.HtmlEditorCore.cwhite= [
26066 // 'text-align', /// default is to allow most things..
26072 // black listed style attributes.
26073 Roo.HtmlEditorCore.cblack= [
26074 // 'font-size' -- this can be set by the project
26078 Roo.HtmlEditorCore.swapCodes =[
26079 [ 8211, "–" ],
26080 [ 8212, "—" ],
26097 * @class Roo.bootstrap.HtmlEditor
26098 * @extends Roo.bootstrap.TextArea
26099 * Bootstrap HtmlEditor class
26102 * Create a new HtmlEditor
26103 * @param {Object} config The config object
26106 Roo.bootstrap.HtmlEditor = function(config){
26107 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26108 if (!this.toolbars) {
26109 this.toolbars = [];
26112 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26115 * @event initialize
26116 * Fires when the editor is fully initialized (including the iframe)
26117 * @param {HtmlEditor} this
26122 * Fires when the editor is first receives the focus. Any insertion must wait
26123 * until after this event.
26124 * @param {HtmlEditor} this
26128 * @event beforesync
26129 * Fires before the textarea is updated with content from the editor iframe. Return false
26130 * to cancel the sync.
26131 * @param {HtmlEditor} this
26132 * @param {String} html
26136 * @event beforepush
26137 * Fires before the iframe editor is updated with content from the textarea. Return false
26138 * to cancel the push.
26139 * @param {HtmlEditor} this
26140 * @param {String} html
26145 * Fires when the textarea is updated with content from the editor iframe.
26146 * @param {HtmlEditor} this
26147 * @param {String} html
26152 * Fires when the iframe editor is updated with content from the textarea.
26153 * @param {HtmlEditor} this
26154 * @param {String} html
26158 * @event editmodechange
26159 * Fires when the editor switches edit modes
26160 * @param {HtmlEditor} this
26161 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26163 editmodechange: true,
26165 * @event editorevent
26166 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26167 * @param {HtmlEditor} this
26171 * @event firstfocus
26172 * Fires when on first focus - needed by toolbars..
26173 * @param {HtmlEditor} this
26178 * Auto save the htmlEditor value as a file into Events
26179 * @param {HtmlEditor} this
26183 * @event savedpreview
26184 * preview the saved version of htmlEditor
26185 * @param {HtmlEditor} this
26192 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26196 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26201 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26206 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26211 * @cfg {Number} height (in pixels)
26215 * @cfg {Number} width (in pixels)
26220 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26223 stylesheets: false,
26228 // private properties
26229 validationEvent : false,
26231 initialized : false,
26234 onFocus : Roo.emptyFn,
26236 hideMode:'offsets',
26238 tbContainer : false,
26242 toolbarContainer :function() {
26243 return this.wrap.select('.x-html-editor-tb',true).first();
26247 * Protected method that will not generally be called directly. It
26248 * is called when the editor creates its toolbar. Override this method if you need to
26249 * add custom toolbar buttons.
26250 * @param {HtmlEditor} editor
26252 createToolbar : function(){
26253 Roo.log('renewing');
26254 Roo.log("create toolbars");
26256 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26257 this.toolbars[0].render(this.toolbarContainer());
26261 // if (!editor.toolbars || !editor.toolbars.length) {
26262 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26265 // for (var i =0 ; i < editor.toolbars.length;i++) {
26266 // editor.toolbars[i] = Roo.factory(
26267 // typeof(editor.toolbars[i]) == 'string' ?
26268 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26269 // Roo.bootstrap.HtmlEditor);
26270 // editor.toolbars[i].init(editor);
26276 onRender : function(ct, position)
26278 // Roo.log("Call onRender: " + this.xtype);
26280 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26282 this.wrap = this.inputEl().wrap({
26283 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26286 this.editorcore.onRender(ct, position);
26288 if (this.resizable) {
26289 this.resizeEl = new Roo.Resizable(this.wrap, {
26293 minHeight : this.height,
26294 height: this.height,
26295 handles : this.resizable,
26298 resize : function(r, w, h) {
26299 _t.onResize(w,h); // -something
26305 this.createToolbar(this);
26308 if(!this.width && this.resizable){
26309 this.setSize(this.wrap.getSize());
26311 if (this.resizeEl) {
26312 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26313 // should trigger onReize..
26319 onResize : function(w, h)
26321 Roo.log('resize: ' +w + ',' + h );
26322 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26326 if(this.inputEl() ){
26327 if(typeof w == 'number'){
26328 var aw = w - this.wrap.getFrameWidth('lr');
26329 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26332 if(typeof h == 'number'){
26333 var tbh = -11; // fixme it needs to tool bar size!
26334 for (var i =0; i < this.toolbars.length;i++) {
26335 // fixme - ask toolbars for heights?
26336 tbh += this.toolbars[i].el.getHeight();
26337 //if (this.toolbars[i].footer) {
26338 // tbh += this.toolbars[i].footer.el.getHeight();
26346 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26347 ah -= 5; // knock a few pixes off for look..
26348 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26352 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26353 this.editorcore.onResize(ew,eh);
26358 * Toggles the editor between standard and source edit mode.
26359 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26361 toggleSourceEdit : function(sourceEditMode)
26363 this.editorcore.toggleSourceEdit(sourceEditMode);
26365 if(this.editorcore.sourceEditMode){
26366 Roo.log('editor - showing textarea');
26369 // Roo.log(this.syncValue());
26371 this.inputEl().removeClass(['hide', 'x-hidden']);
26372 this.inputEl().dom.removeAttribute('tabIndex');
26373 this.inputEl().focus();
26375 Roo.log('editor - hiding textarea');
26377 // Roo.log(this.pushValue());
26380 this.inputEl().addClass(['hide', 'x-hidden']);
26381 this.inputEl().dom.setAttribute('tabIndex', -1);
26382 //this.deferFocus();
26385 if(this.resizable){
26386 this.setSize(this.wrap.getSize());
26389 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26392 // private (for BoxComponent)
26393 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26395 // private (for BoxComponent)
26396 getResizeEl : function(){
26400 // private (for BoxComponent)
26401 getPositionEl : function(){
26406 initEvents : function(){
26407 this.originalValue = this.getValue();
26411 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26414 // markInvalid : Roo.emptyFn,
26416 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26419 // clearInvalid : Roo.emptyFn,
26421 setValue : function(v){
26422 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26423 this.editorcore.pushValue();
26428 deferFocus : function(){
26429 this.focus.defer(10, this);
26433 focus : function(){
26434 this.editorcore.focus();
26440 onDestroy : function(){
26446 for (var i =0; i < this.toolbars.length;i++) {
26447 // fixme - ask toolbars for heights?
26448 this.toolbars[i].onDestroy();
26451 this.wrap.dom.innerHTML = '';
26452 this.wrap.remove();
26457 onFirstFocus : function(){
26458 //Roo.log("onFirstFocus");
26459 this.editorcore.onFirstFocus();
26460 for (var i =0; i < this.toolbars.length;i++) {
26461 this.toolbars[i].onFirstFocus();
26467 syncValue : function()
26469 this.editorcore.syncValue();
26472 pushValue : function()
26474 this.editorcore.pushValue();
26478 // hide stuff that is not compatible
26492 * @event specialkey
26496 * @cfg {String} fieldClass @hide
26499 * @cfg {String} focusClass @hide
26502 * @cfg {String} autoCreate @hide
26505 * @cfg {String} inputType @hide
26509 * @cfg {String} invalidText @hide
26512 * @cfg {String} msgFx @hide
26515 * @cfg {String} validateOnBlur @hide
26524 Roo.namespace('Roo.bootstrap.htmleditor');
26526 * @class Roo.bootstrap.HtmlEditorToolbar1
26532 new Roo.bootstrap.HtmlEditor({
26535 new Roo.bootstrap.HtmlEditorToolbar1({
26536 disable : { fonts: 1 , format: 1, ..., ... , ...],
26542 * @cfg {Object} disable List of elements to disable..
26543 * @cfg {Array} btns List of additional buttons.
26547 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26550 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26553 Roo.apply(this, config);
26555 // default disabled, based on 'good practice'..
26556 this.disable = this.disable || {};
26557 Roo.applyIf(this.disable, {
26560 specialElements : true
26562 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26564 this.editor = config.editor;
26565 this.editorcore = config.editor.editorcore;
26567 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26569 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26570 // dont call parent... till later.
26572 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26577 editorcore : false,
26582 "h1","h2","h3","h4","h5","h6",
26584 "abbr", "acronym", "address", "cite", "samp", "var",
26588 onRender : function(ct, position)
26590 // Roo.log("Call onRender: " + this.xtype);
26592 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26594 this.el.dom.style.marginBottom = '0';
26596 var editorcore = this.editorcore;
26597 var editor= this.editor;
26600 var btn = function(id,cmd , toggle, handler, html){
26602 var event = toggle ? 'toggle' : 'click';
26607 xns: Roo.bootstrap,
26611 enableToggle:toggle !== false,
26613 pressed : toggle ? false : null,
26616 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26617 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26623 // var cb_box = function...
26628 xns: Roo.bootstrap,
26633 xns: Roo.bootstrap,
26637 Roo.each(this.formats, function(f) {
26638 style.menu.items.push({
26640 xns: Roo.bootstrap,
26641 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26646 editorcore.insertTag(this.tagname);
26653 children.push(style);
26655 btn('bold',false,true);
26656 btn('italic',false,true);
26657 btn('align-left', 'justifyleft',true);
26658 btn('align-center', 'justifycenter',true);
26659 btn('align-right' , 'justifyright',true);
26660 btn('link', false, false, function(btn) {
26661 //Roo.log("create link?");
26662 var url = prompt(this.createLinkText, this.defaultLinkValue);
26663 if(url && url != 'http:/'+'/'){
26664 this.editorcore.relayCmd('createlink', url);
26667 btn('list','insertunorderedlist',true);
26668 btn('pencil', false,true, function(btn){
26670 this.toggleSourceEdit(btn.pressed);
26673 if (this.editor.btns.length > 0) {
26674 for (var i = 0; i<this.editor.btns.length; i++) {
26675 children.push(this.editor.btns[i]);
26683 xns: Roo.bootstrap,
26688 xns: Roo.bootstrap,
26693 cog.menu.items.push({
26695 xns: Roo.bootstrap,
26696 html : Clean styles,
26701 editorcore.insertTag(this.tagname);
26710 this.xtype = 'NavSimplebar';
26712 for(var i=0;i< children.length;i++) {
26714 this.buttons.add(this.addxtypeChild(children[i]));
26718 editor.on('editorevent', this.updateToolbar, this);
26720 onBtnClick : function(id)
26722 this.editorcore.relayCmd(id);
26723 this.editorcore.focus();
26727 * Protected method that will not generally be called directly. It triggers
26728 * a toolbar update by reading the markup state of the current selection in the editor.
26730 updateToolbar: function(){
26732 if(!this.editorcore.activated){
26733 this.editor.onFirstFocus(); // is this neeed?
26737 var btns = this.buttons;
26738 var doc = this.editorcore.doc;
26739 btns.get('bold').setActive(doc.queryCommandState('bold'));
26740 btns.get('italic').setActive(doc.queryCommandState('italic'));
26741 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26743 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26744 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26745 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26747 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26748 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26751 var ans = this.editorcore.getAllAncestors();
26752 if (this.formatCombo) {
26755 var store = this.formatCombo.store;
26756 this.formatCombo.setValue("");
26757 for (var i =0; i < ans.length;i++) {
26758 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26760 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26768 // hides menus... - so this cant be on a menu...
26769 Roo.bootstrap.MenuMgr.hideAll();
26771 Roo.bootstrap.MenuMgr.hideAll();
26772 //this.editorsyncValue();
26774 onFirstFocus: function() {
26775 this.buttons.each(function(item){
26779 toggleSourceEdit : function(sourceEditMode){
26782 if(sourceEditMode){
26783 Roo.log("disabling buttons");
26784 this.buttons.each( function(item){
26785 if(item.cmd != 'pencil'){
26791 Roo.log("enabling buttons");
26792 if(this.editorcore.initialized){
26793 this.buttons.each( function(item){
26799 Roo.log("calling toggole on editor");
26800 // tell the editor that it's been pressed..
26801 this.editor.toggleSourceEdit(sourceEditMode);
26815 * @class Roo.bootstrap.Markdown
26816 * @extends Roo.bootstrap.TextArea
26817 * Bootstrap Showdown editable area
26818 * @cfg {string} content
26821 * Create a new Showdown
26824 Roo.bootstrap.Markdown = function(config){
26825 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26829 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26833 initEvents : function()
26836 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26837 this.markdownEl = this.el.createChild({
26838 cls : 'roo-markdown-area'
26840 this.inputEl().addClass('d-none');
26841 if (this.getValue() == '') {
26842 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26845 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26847 this.markdownEl.on('click', this.toggleTextEdit, this);
26848 this.on('blur', this.toggleTextEdit, this);
26849 this.on('specialkey', this.resizeTextArea, this);
26852 toggleTextEdit : function()
26854 var sh = this.markdownEl.getHeight();
26855 this.inputEl().addClass('d-none');
26856 this.markdownEl.addClass('d-none');
26857 if (!this.editing) {
26859 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26860 this.inputEl().removeClass('d-none');
26861 this.inputEl().focus();
26862 this.editing = true;
26865 // show showdown...
26866 this.updateMarkdown();
26867 this.markdownEl.removeClass('d-none');
26868 this.editing = false;
26871 updateMarkdown : function()
26873 if (this.getValue() == '') {
26874 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26878 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26881 resizeTextArea: function () {
26884 Roo.log([sh, this.getValue().split("\n").length * 30]);
26885 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26887 setValue : function(val)
26889 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26890 if (!this.editing) {
26891 this.updateMarkdown();
26897 if (!this.editing) {
26898 this.toggleTextEdit();
26906 * @class Roo.bootstrap.Table.AbstractSelectionModel
26907 * @extends Roo.util.Observable
26908 * Abstract base class for grid SelectionModels. It provides the interface that should be
26909 * implemented by descendant classes. This class should not be directly instantiated.
26912 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26913 this.locked = false;
26914 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26918 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26919 /** @ignore Called by the grid automatically. Do not call directly. */
26920 init : function(grid){
26926 * Locks the selections.
26929 this.locked = true;
26933 * Unlocks the selections.
26935 unlock : function(){
26936 this.locked = false;
26940 * Returns true if the selections are locked.
26941 * @return {Boolean}
26943 isLocked : function(){
26944 return this.locked;
26948 initEvents : function ()
26954 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26955 * @class Roo.bootstrap.Table.RowSelectionModel
26956 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26957 * It supports multiple selections and keyboard selection/navigation.
26959 * @param {Object} config
26962 Roo.bootstrap.Table.RowSelectionModel = function(config){
26963 Roo.apply(this, config);
26964 this.selections = new Roo.util.MixedCollection(false, function(o){
26969 this.lastActive = false;
26973 * @event selectionchange
26974 * Fires when the selection changes
26975 * @param {SelectionModel} this
26977 "selectionchange" : true,
26979 * @event afterselectionchange
26980 * Fires after the selection changes (eg. by key press or clicking)
26981 * @param {SelectionModel} this
26983 "afterselectionchange" : true,
26985 * @event beforerowselect
26986 * Fires when a row is selected being selected, return false to cancel.
26987 * @param {SelectionModel} this
26988 * @param {Number} rowIndex The selected index
26989 * @param {Boolean} keepExisting False if other selections will be cleared
26991 "beforerowselect" : true,
26994 * Fires when a row is selected.
26995 * @param {SelectionModel} this
26996 * @param {Number} rowIndex The selected index
26997 * @param {Roo.data.Record} r The record
26999 "rowselect" : true,
27001 * @event rowdeselect
27002 * Fires when a row is deselected.
27003 * @param {SelectionModel} this
27004 * @param {Number} rowIndex The selected index
27006 "rowdeselect" : true
27008 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27009 this.locked = false;
27012 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27014 * @cfg {Boolean} singleSelect
27015 * True to allow selection of only one row at a time (defaults to false)
27017 singleSelect : false,
27020 initEvents : function()
27023 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27024 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27025 //}else{ // allow click to work like normal
27026 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27028 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27029 this.grid.on("rowclick", this.handleMouseDown, this);
27031 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27032 "up" : function(e){
27034 this.selectPrevious(e.shiftKey);
27035 }else if(this.last !== false && this.lastActive !== false){
27036 var last = this.last;
27037 this.selectRange(this.last, this.lastActive-1);
27038 this.grid.getView().focusRow(this.lastActive);
27039 if(last !== false){
27043 this.selectFirstRow();
27045 this.fireEvent("afterselectionchange", this);
27047 "down" : function(e){
27049 this.selectNext(e.shiftKey);
27050 }else if(this.last !== false && this.lastActive !== false){
27051 var last = this.last;
27052 this.selectRange(this.last, this.lastActive+1);
27053 this.grid.getView().focusRow(this.lastActive);
27054 if(last !== false){
27058 this.selectFirstRow();
27060 this.fireEvent("afterselectionchange", this);
27064 this.grid.store.on('load', function(){
27065 this.selections.clear();
27068 var view = this.grid.view;
27069 view.on("refresh", this.onRefresh, this);
27070 view.on("rowupdated", this.onRowUpdated, this);
27071 view.on("rowremoved", this.onRemove, this);
27076 onRefresh : function()
27078 var ds = this.grid.store, i, v = this.grid.view;
27079 var s = this.selections;
27080 s.each(function(r){
27081 if((i = ds.indexOfId(r.id)) != -1){
27090 onRemove : function(v, index, r){
27091 this.selections.remove(r);
27095 onRowUpdated : function(v, index, r){
27096 if(this.isSelected(r)){
27097 v.onRowSelect(index);
27103 * @param {Array} records The records to select
27104 * @param {Boolean} keepExisting (optional) True to keep existing selections
27106 selectRecords : function(records, keepExisting)
27109 this.clearSelections();
27111 var ds = this.grid.store;
27112 for(var i = 0, len = records.length; i < len; i++){
27113 this.selectRow(ds.indexOf(records[i]), true);
27118 * Gets the number of selected rows.
27121 getCount : function(){
27122 return this.selections.length;
27126 * Selects the first row in the grid.
27128 selectFirstRow : function(){
27133 * Select the last row.
27134 * @param {Boolean} keepExisting (optional) True to keep existing selections
27136 selectLastRow : function(keepExisting){
27137 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27138 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27142 * Selects the row immediately following the last selected row.
27143 * @param {Boolean} keepExisting (optional) True to keep existing selections
27145 selectNext : function(keepExisting)
27147 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27148 this.selectRow(this.last+1, keepExisting);
27149 this.grid.getView().focusRow(this.last);
27154 * Selects the row that precedes the last selected row.
27155 * @param {Boolean} keepExisting (optional) True to keep existing selections
27157 selectPrevious : function(keepExisting){
27159 this.selectRow(this.last-1, keepExisting);
27160 this.grid.getView().focusRow(this.last);
27165 * Returns the selected records
27166 * @return {Array} Array of selected records
27168 getSelections : function(){
27169 return [].concat(this.selections.items);
27173 * Returns the first selected record.
27176 getSelected : function(){
27177 return this.selections.itemAt(0);
27182 * Clears all selections.
27184 clearSelections : function(fast)
27190 var ds = this.grid.store;
27191 var s = this.selections;
27192 s.each(function(r){
27193 this.deselectRow(ds.indexOfId(r.id));
27197 this.selections.clear();
27204 * Selects all rows.
27206 selectAll : function(){
27210 this.selections.clear();
27211 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27212 this.selectRow(i, true);
27217 * Returns True if there is a selection.
27218 * @return {Boolean}
27220 hasSelection : function(){
27221 return this.selections.length > 0;
27225 * Returns True if the specified row is selected.
27226 * @param {Number/Record} record The record or index of the record to check
27227 * @return {Boolean}
27229 isSelected : function(index){
27230 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27231 return (r && this.selections.key(r.id) ? true : false);
27235 * Returns True if the specified record id is selected.
27236 * @param {String} id The id of record to check
27237 * @return {Boolean}
27239 isIdSelected : function(id){
27240 return (this.selections.key(id) ? true : false);
27245 handleMouseDBClick : function(e, t){
27249 handleMouseDown : function(e, t)
27251 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27252 if(this.isLocked() || rowIndex < 0 ){
27255 if(e.shiftKey && this.last !== false){
27256 var last = this.last;
27257 this.selectRange(last, rowIndex, e.ctrlKey);
27258 this.last = last; // reset the last
27262 var isSelected = this.isSelected(rowIndex);
27263 //Roo.log("select row:" + rowIndex);
27265 this.deselectRow(rowIndex);
27267 this.selectRow(rowIndex, true);
27271 if(e.button !== 0 && isSelected){
27272 alert('rowIndex 2: ' + rowIndex);
27273 view.focusRow(rowIndex);
27274 }else if(e.ctrlKey && isSelected){
27275 this.deselectRow(rowIndex);
27276 }else if(!isSelected){
27277 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27278 view.focusRow(rowIndex);
27282 this.fireEvent("afterselectionchange", this);
27285 handleDragableRowClick : function(grid, rowIndex, e)
27287 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27288 this.selectRow(rowIndex, false);
27289 grid.view.focusRow(rowIndex);
27290 this.fireEvent("afterselectionchange", this);
27295 * Selects multiple rows.
27296 * @param {Array} rows Array of the indexes of the row to select
27297 * @param {Boolean} keepExisting (optional) True to keep existing selections
27299 selectRows : function(rows, keepExisting){
27301 this.clearSelections();
27303 for(var i = 0, len = rows.length; i < len; i++){
27304 this.selectRow(rows[i], true);
27309 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27310 * @param {Number} startRow The index of the first row in the range
27311 * @param {Number} endRow The index of the last row in the range
27312 * @param {Boolean} keepExisting (optional) True to retain existing selections
27314 selectRange : function(startRow, endRow, keepExisting){
27319 this.clearSelections();
27321 if(startRow <= endRow){
27322 for(var i = startRow; i <= endRow; i++){
27323 this.selectRow(i, true);
27326 for(var i = startRow; i >= endRow; i--){
27327 this.selectRow(i, true);
27333 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27334 * @param {Number} startRow The index of the first row in the range
27335 * @param {Number} endRow The index of the last row in the range
27337 deselectRange : function(startRow, endRow, preventViewNotify){
27341 for(var i = startRow; i <= endRow; i++){
27342 this.deselectRow(i, preventViewNotify);
27348 * @param {Number} row The index of the row to select
27349 * @param {Boolean} keepExisting (optional) True to keep existing selections
27351 selectRow : function(index, keepExisting, preventViewNotify)
27353 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27356 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27357 if(!keepExisting || this.singleSelect){
27358 this.clearSelections();
27361 var r = this.grid.store.getAt(index);
27362 //console.log('selectRow - record id :' + r.id);
27364 this.selections.add(r);
27365 this.last = this.lastActive = index;
27366 if(!preventViewNotify){
27367 var proxy = new Roo.Element(
27368 this.grid.getRowDom(index)
27370 proxy.addClass('bg-info info');
27372 this.fireEvent("rowselect", this, index, r);
27373 this.fireEvent("selectionchange", this);
27379 * @param {Number} row The index of the row to deselect
27381 deselectRow : function(index, preventViewNotify)
27386 if(this.last == index){
27389 if(this.lastActive == index){
27390 this.lastActive = false;
27393 var r = this.grid.store.getAt(index);
27398 this.selections.remove(r);
27399 //.console.log('deselectRow - record id :' + r.id);
27400 if(!preventViewNotify){
27402 var proxy = new Roo.Element(
27403 this.grid.getRowDom(index)
27405 proxy.removeClass('bg-info info');
27407 this.fireEvent("rowdeselect", this, index);
27408 this.fireEvent("selectionchange", this);
27412 restoreLast : function(){
27414 this.last = this._last;
27419 acceptsNav : function(row, col, cm){
27420 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27424 onEditorKey : function(field, e){
27425 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27430 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27432 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27434 }else if(k == e.ENTER && !e.ctrlKey){
27438 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27440 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27442 }else if(k == e.ESC){
27446 g.startEditing(newCell[0], newCell[1]);
27452 * Ext JS Library 1.1.1
27453 * Copyright(c) 2006-2007, Ext JS, LLC.
27455 * Originally Released Under LGPL - original licence link has changed is not relivant.
27458 * <script type="text/javascript">
27462 * @class Roo.bootstrap.PagingToolbar
27463 * @extends Roo.bootstrap.NavSimplebar
27464 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27466 * Create a new PagingToolbar
27467 * @param {Object} config The config object
27468 * @param {Roo.data.Store} store
27470 Roo.bootstrap.PagingToolbar = function(config)
27472 // old args format still supported... - xtype is prefered..
27473 // created from xtype...
27475 this.ds = config.dataSource;
27477 if (config.store && !this.ds) {
27478 this.store= Roo.factory(config.store, Roo.data);
27479 this.ds = this.store;
27480 this.ds.xmodule = this.xmodule || false;
27483 this.toolbarItems = [];
27484 if (config.items) {
27485 this.toolbarItems = config.items;
27488 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27493 this.bind(this.ds);
27496 if (Roo.bootstrap.version == 4) {
27497 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27499 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27504 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27506 * @cfg {Roo.data.Store} dataSource
27507 * The underlying data store providing the paged data
27510 * @cfg {String/HTMLElement/Element} container
27511 * container The id or element that will contain the toolbar
27514 * @cfg {Boolean} displayInfo
27515 * True to display the displayMsg (defaults to false)
27518 * @cfg {Number} pageSize
27519 * The number of records to display per page (defaults to 20)
27523 * @cfg {String} displayMsg
27524 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27526 displayMsg : 'Displaying {0} - {1} of {2}',
27528 * @cfg {String} emptyMsg
27529 * The message to display when no records are found (defaults to "No data to display")
27531 emptyMsg : 'No data to display',
27533 * Customizable piece of the default paging text (defaults to "Page")
27536 beforePageText : "Page",
27538 * Customizable piece of the default paging text (defaults to "of %0")
27541 afterPageText : "of {0}",
27543 * Customizable piece of the default paging text (defaults to "First Page")
27546 firstText : "First Page",
27548 * Customizable piece of the default paging text (defaults to "Previous Page")
27551 prevText : "Previous Page",
27553 * Customizable piece of the default paging text (defaults to "Next Page")
27556 nextText : "Next Page",
27558 * Customizable piece of the default paging text (defaults to "Last Page")
27561 lastText : "Last Page",
27563 * Customizable piece of the default paging text (defaults to "Refresh")
27566 refreshText : "Refresh",
27570 onRender : function(ct, position)
27572 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27573 this.navgroup.parentId = this.id;
27574 this.navgroup.onRender(this.el, null);
27575 // add the buttons to the navgroup
27577 if(this.displayInfo){
27578 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27579 this.displayEl = this.el.select('.x-paging-info', true).first();
27580 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27581 // this.displayEl = navel.el.select('span',true).first();
27587 Roo.each(_this.buttons, function(e){ // this might need to use render????
27588 Roo.factory(e).render(_this.el);
27592 Roo.each(_this.toolbarItems, function(e) {
27593 _this.navgroup.addItem(e);
27597 this.first = this.navgroup.addItem({
27598 tooltip: this.firstText,
27599 cls: "prev btn-outline-secondary",
27600 html : ' <i class="fa fa-step-backward"></i>',
27602 preventDefault: true,
27603 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27606 this.prev = this.navgroup.addItem({
27607 tooltip: this.prevText,
27608 cls: "prev btn-outline-secondary",
27609 html : ' <i class="fa fa-backward"></i>',
27611 preventDefault: true,
27612 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27614 //this.addSeparator();
27617 var field = this.navgroup.addItem( {
27619 cls : 'x-paging-position btn-outline-secondary',
27621 html : this.beforePageText +
27622 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27623 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27626 this.field = field.el.select('input', true).first();
27627 this.field.on("keydown", this.onPagingKeydown, this);
27628 this.field.on("focus", function(){this.dom.select();});
27631 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27632 //this.field.setHeight(18);
27633 //this.addSeparator();
27634 this.next = this.navgroup.addItem({
27635 tooltip: this.nextText,
27636 cls: "next btn-outline-secondary",
27637 html : ' <i class="fa fa-forward"></i>',
27639 preventDefault: true,
27640 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27642 this.last = this.navgroup.addItem({
27643 tooltip: this.lastText,
27644 html : ' <i class="fa fa-step-forward"></i>',
27645 cls: "next btn-outline-secondary",
27647 preventDefault: true,
27648 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27650 //this.addSeparator();
27651 this.loading = this.navgroup.addItem({
27652 tooltip: this.refreshText,
27653 cls: "btn-outline-secondary",
27654 html : ' <i class="fa fa-refresh"></i>',
27655 preventDefault: true,
27656 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27662 updateInfo : function(){
27663 if(this.displayEl){
27664 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27665 var msg = count == 0 ?
27669 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27671 this.displayEl.update(msg);
27676 onLoad : function(ds, r, o)
27678 this.cursor = o.params && o.params.start ? o.params.start : 0;
27680 var d = this.getPageData(),
27685 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27686 this.field.dom.value = ap;
27687 this.first.setDisabled(ap == 1);
27688 this.prev.setDisabled(ap == 1);
27689 this.next.setDisabled(ap == ps);
27690 this.last.setDisabled(ap == ps);
27691 this.loading.enable();
27696 getPageData : function(){
27697 var total = this.ds.getTotalCount();
27700 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27701 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27706 onLoadError : function(){
27707 this.loading.enable();
27711 onPagingKeydown : function(e){
27712 var k = e.getKey();
27713 var d = this.getPageData();
27715 var v = this.field.dom.value, pageNum;
27716 if(!v || isNaN(pageNum = parseInt(v, 10))){
27717 this.field.dom.value = d.activePage;
27720 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27721 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27724 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))
27726 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27727 this.field.dom.value = pageNum;
27728 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27731 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27733 var v = this.field.dom.value, pageNum;
27734 var increment = (e.shiftKey) ? 10 : 1;
27735 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27738 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27739 this.field.dom.value = d.activePage;
27742 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27744 this.field.dom.value = parseInt(v, 10) + increment;
27745 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27746 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27753 beforeLoad : function(){
27755 this.loading.disable();
27760 onClick : function(which){
27769 ds.load({params:{start: 0, limit: this.pageSize}});
27772 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27775 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27778 var total = ds.getTotalCount();
27779 var extra = total % this.pageSize;
27780 var lastStart = extra ? (total - extra) : total-this.pageSize;
27781 ds.load({params:{start: lastStart, limit: this.pageSize}});
27784 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27790 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27791 * @param {Roo.data.Store} store The data store to unbind
27793 unbind : function(ds){
27794 ds.un("beforeload", this.beforeLoad, this);
27795 ds.un("load", this.onLoad, this);
27796 ds.un("loadexception", this.onLoadError, this);
27797 ds.un("remove", this.updateInfo, this);
27798 ds.un("add", this.updateInfo, this);
27799 this.ds = undefined;
27803 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27804 * @param {Roo.data.Store} store The data store to bind
27806 bind : function(ds){
27807 ds.on("beforeload", this.beforeLoad, this);
27808 ds.on("load", this.onLoad, this);
27809 ds.on("loadexception", this.onLoadError, this);
27810 ds.on("remove", this.updateInfo, this);
27811 ds.on("add", this.updateInfo, this);
27822 * @class Roo.bootstrap.MessageBar
27823 * @extends Roo.bootstrap.Component
27824 * Bootstrap MessageBar class
27825 * @cfg {String} html contents of the MessageBar
27826 * @cfg {String} weight (info | success | warning | danger) default info
27827 * @cfg {String} beforeClass insert the bar before the given class
27828 * @cfg {Boolean} closable (true | false) default false
27829 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27832 * Create a new Element
27833 * @param {Object} config The config object
27836 Roo.bootstrap.MessageBar = function(config){
27837 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27840 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27846 beforeClass: 'bootstrap-sticky-wrap',
27848 getAutoCreate : function(){
27852 cls: 'alert alert-dismissable alert-' + this.weight,
27857 html: this.html || ''
27863 cfg.cls += ' alert-messages-fixed';
27877 onRender : function(ct, position)
27879 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27882 var cfg = Roo.apply({}, this.getAutoCreate());
27886 cfg.cls += ' ' + this.cls;
27889 cfg.style = this.style;
27891 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27893 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27896 this.el.select('>button.close').on('click', this.hide, this);
27902 if (!this.rendered) {
27908 this.fireEvent('show', this);
27914 if (!this.rendered) {
27920 this.fireEvent('hide', this);
27923 update : function()
27925 // var e = this.el.dom.firstChild;
27927 // if(this.closable){
27928 // e = e.nextSibling;
27931 // e.data = this.html || '';
27933 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27949 * @class Roo.bootstrap.Graph
27950 * @extends Roo.bootstrap.Component
27951 * Bootstrap Graph class
27955 @cfg {String} graphtype bar | vbar | pie
27956 @cfg {number} g_x coodinator | centre x (pie)
27957 @cfg {number} g_y coodinator | centre y (pie)
27958 @cfg {number} g_r radius (pie)
27959 @cfg {number} g_height height of the chart (respected by all elements in the set)
27960 @cfg {number} g_width width of the chart (respected by all elements in the set)
27961 @cfg {Object} title The title of the chart
27964 -opts (object) options for the chart
27966 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27967 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27969 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.
27970 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27972 o stretch (boolean)
27974 -opts (object) options for the pie
27977 o startAngle (number)
27978 o endAngle (number)
27982 * Create a new Input
27983 * @param {Object} config The config object
27986 Roo.bootstrap.Graph = function(config){
27987 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27993 * The img click event for the img.
27994 * @param {Roo.EventObject} e
28000 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28011 //g_colors: this.colors,
28018 getAutoCreate : function(){
28029 onRender : function(ct,position){
28032 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28034 if (typeof(Raphael) == 'undefined') {
28035 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28039 this.raphael = Raphael(this.el.dom);
28041 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28042 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28043 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28044 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28046 r.text(160, 10, "Single Series Chart").attr(txtattr);
28047 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28048 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28049 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28051 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28052 r.barchart(330, 10, 300, 220, data1);
28053 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28054 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28057 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28058 // r.barchart(30, 30, 560, 250, xdata, {
28059 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28060 // axis : "0 0 1 1",
28061 // axisxlabels : xdata
28062 // //yvalues : cols,
28065 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28067 // this.load(null,xdata,{
28068 // axis : "0 0 1 1",
28069 // axisxlabels : xdata
28074 load : function(graphtype,xdata,opts)
28076 this.raphael.clear();
28078 graphtype = this.graphtype;
28083 var r = this.raphael,
28084 fin = function () {
28085 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28087 fout = function () {
28088 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28090 pfin = function() {
28091 this.sector.stop();
28092 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28095 this.label[0].stop();
28096 this.label[0].attr({ r: 7.5 });
28097 this.label[1].attr({ "font-weight": 800 });
28100 pfout = function() {
28101 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28104 this.label[0].animate({ r: 5 }, 500, "bounce");
28105 this.label[1].attr({ "font-weight": 400 });
28111 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28114 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28117 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28118 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28120 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28127 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28132 setTitle: function(o)
28137 initEvents: function() {
28140 this.el.on('click', this.onClick, this);
28144 onClick : function(e)
28146 Roo.log('img onclick');
28147 this.fireEvent('click', this, e);
28159 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28162 * @class Roo.bootstrap.dash.NumberBox
28163 * @extends Roo.bootstrap.Component
28164 * Bootstrap NumberBox class
28165 * @cfg {String} headline Box headline
28166 * @cfg {String} content Box content
28167 * @cfg {String} icon Box icon
28168 * @cfg {String} footer Footer text
28169 * @cfg {String} fhref Footer href
28172 * Create a new NumberBox
28173 * @param {Object} config The config object
28177 Roo.bootstrap.dash.NumberBox = function(config){
28178 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28182 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28191 getAutoCreate : function(){
28195 cls : 'small-box ',
28203 cls : 'roo-headline',
28204 html : this.headline
28208 cls : 'roo-content',
28209 html : this.content
28223 cls : 'ion ' + this.icon
28232 cls : 'small-box-footer',
28233 href : this.fhref || '#',
28237 cfg.cn.push(footer);
28244 onRender : function(ct,position){
28245 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28252 setHeadline: function (value)
28254 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28257 setFooter: function (value, href)
28259 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28262 this.el.select('a.small-box-footer',true).first().attr('href', href);
28267 setContent: function (value)
28269 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28272 initEvents: function()
28286 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28289 * @class Roo.bootstrap.dash.TabBox
28290 * @extends Roo.bootstrap.Component
28291 * Bootstrap TabBox class
28292 * @cfg {String} title Title of the TabBox
28293 * @cfg {String} icon Icon of the TabBox
28294 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28295 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28298 * Create a new TabBox
28299 * @param {Object} config The config object
28303 Roo.bootstrap.dash.TabBox = function(config){
28304 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28309 * When a pane is added
28310 * @param {Roo.bootstrap.dash.TabPane} pane
28314 * @event activatepane
28315 * When a pane is activated
28316 * @param {Roo.bootstrap.dash.TabPane} pane
28318 "activatepane" : true
28326 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28331 tabScrollable : false,
28333 getChildContainer : function()
28335 return this.el.select('.tab-content', true).first();
28338 getAutoCreate : function(){
28342 cls: 'pull-left header',
28350 cls: 'fa ' + this.icon
28356 cls: 'nav nav-tabs pull-right',
28362 if(this.tabScrollable){
28369 cls: 'nav nav-tabs pull-right',
28380 cls: 'nav-tabs-custom',
28385 cls: 'tab-content no-padding',
28393 initEvents : function()
28395 //Roo.log('add add pane handler');
28396 this.on('addpane', this.onAddPane, this);
28399 * Updates the box title
28400 * @param {String} html to set the title to.
28402 setTitle : function(value)
28404 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28406 onAddPane : function(pane)
28408 this.panes.push(pane);
28409 //Roo.log('addpane');
28411 // tabs are rendere left to right..
28412 if(!this.showtabs){
28416 var ctr = this.el.select('.nav-tabs', true).first();
28419 var existing = ctr.select('.nav-tab',true);
28420 var qty = existing.getCount();;
28423 var tab = ctr.createChild({
28425 cls : 'nav-tab' + (qty ? '' : ' active'),
28433 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28436 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28438 pane.el.addClass('active');
28443 onTabClick : function(ev,un,ob,pane)
28445 //Roo.log('tab - prev default');
28446 ev.preventDefault();
28449 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28450 pane.tab.addClass('active');
28451 //Roo.log(pane.title);
28452 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28453 // technically we should have a deactivate event.. but maybe add later.
28454 // and it should not de-activate the selected tab...
28455 this.fireEvent('activatepane', pane);
28456 pane.el.addClass('active');
28457 pane.fireEvent('activate');
28462 getActivePane : function()
28465 Roo.each(this.panes, function(p) {
28466 if(p.el.hasClass('active')){
28487 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28489 * @class Roo.bootstrap.TabPane
28490 * @extends Roo.bootstrap.Component
28491 * Bootstrap TabPane class
28492 * @cfg {Boolean} active (false | true) Default false
28493 * @cfg {String} title title of panel
28497 * Create a new TabPane
28498 * @param {Object} config The config object
28501 Roo.bootstrap.dash.TabPane = function(config){
28502 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28508 * When a pane is activated
28509 * @param {Roo.bootstrap.dash.TabPane} pane
28516 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28521 // the tabBox that this is attached to.
28524 getAutoCreate : function()
28532 cfg.cls += ' active';
28537 initEvents : function()
28539 //Roo.log('trigger add pane handler');
28540 this.parent().fireEvent('addpane', this)
28544 * Updates the tab title
28545 * @param {String} html to set the title to.
28547 setTitle: function(str)
28553 this.tab.select('a', true).first().dom.innerHTML = str;
28570 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28573 * @class Roo.bootstrap.menu.Menu
28574 * @extends Roo.bootstrap.Component
28575 * Bootstrap Menu class - container for Menu
28576 * @cfg {String} html Text of the menu
28577 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28578 * @cfg {String} icon Font awesome icon
28579 * @cfg {String} pos Menu align to (top | bottom) default bottom
28583 * Create a new Menu
28584 * @param {Object} config The config object
28588 Roo.bootstrap.menu.Menu = function(config){
28589 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28593 * @event beforeshow
28594 * Fires before this menu is displayed
28595 * @param {Roo.bootstrap.menu.Menu} this
28599 * @event beforehide
28600 * Fires before this menu is hidden
28601 * @param {Roo.bootstrap.menu.Menu} this
28606 * Fires after this menu is displayed
28607 * @param {Roo.bootstrap.menu.Menu} this
28612 * Fires after this menu is hidden
28613 * @param {Roo.bootstrap.menu.Menu} this
28618 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28619 * @param {Roo.bootstrap.menu.Menu} this
28620 * @param {Roo.EventObject} e
28627 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28631 weight : 'default',
28636 getChildContainer : function() {
28637 if(this.isSubMenu){
28641 return this.el.select('ul.dropdown-menu', true).first();
28644 getAutoCreate : function()
28649 cls : 'roo-menu-text',
28657 cls : 'fa ' + this.icon
28668 cls : 'dropdown-button btn btn-' + this.weight,
28673 cls : 'dropdown-toggle btn btn-' + this.weight,
28683 cls : 'dropdown-menu'
28689 if(this.pos == 'top'){
28690 cfg.cls += ' dropup';
28693 if(this.isSubMenu){
28696 cls : 'dropdown-menu'
28703 onRender : function(ct, position)
28705 this.isSubMenu = ct.hasClass('dropdown-submenu');
28707 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28710 initEvents : function()
28712 if(this.isSubMenu){
28716 this.hidden = true;
28718 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28719 this.triggerEl.on('click', this.onTriggerPress, this);
28721 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28722 this.buttonEl.on('click', this.onClick, this);
28728 if(this.isSubMenu){
28732 return this.el.select('ul.dropdown-menu', true).first();
28735 onClick : function(e)
28737 this.fireEvent("click", this, e);
28740 onTriggerPress : function(e)
28742 if (this.isVisible()) {
28749 isVisible : function(){
28750 return !this.hidden;
28755 this.fireEvent("beforeshow", this);
28757 this.hidden = false;
28758 this.el.addClass('open');
28760 Roo.get(document).on("mouseup", this.onMouseUp, this);
28762 this.fireEvent("show", this);
28769 this.fireEvent("beforehide", this);
28771 this.hidden = true;
28772 this.el.removeClass('open');
28774 Roo.get(document).un("mouseup", this.onMouseUp);
28776 this.fireEvent("hide", this);
28779 onMouseUp : function()
28793 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28796 * @class Roo.bootstrap.menu.Item
28797 * @extends Roo.bootstrap.Component
28798 * Bootstrap MenuItem class
28799 * @cfg {Boolean} submenu (true | false) default false
28800 * @cfg {String} html text of the item
28801 * @cfg {String} href the link
28802 * @cfg {Boolean} disable (true | false) default false
28803 * @cfg {Boolean} preventDefault (true | false) default true
28804 * @cfg {String} icon Font awesome icon
28805 * @cfg {String} pos Submenu align to (left | right) default right
28809 * Create a new Item
28810 * @param {Object} config The config object
28814 Roo.bootstrap.menu.Item = function(config){
28815 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28819 * Fires when the mouse is hovering over this menu
28820 * @param {Roo.bootstrap.menu.Item} this
28821 * @param {Roo.EventObject} e
28826 * Fires when the mouse exits this menu
28827 * @param {Roo.bootstrap.menu.Item} this
28828 * @param {Roo.EventObject} e
28834 * The raw click event for the entire grid.
28835 * @param {Roo.EventObject} e
28841 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28846 preventDefault: true,
28851 getAutoCreate : function()
28856 cls : 'roo-menu-item-text',
28864 cls : 'fa ' + this.icon
28873 href : this.href || '#',
28880 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28884 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28886 if(this.pos == 'left'){
28887 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28894 initEvents : function()
28896 this.el.on('mouseover', this.onMouseOver, this);
28897 this.el.on('mouseout', this.onMouseOut, this);
28899 this.el.select('a', true).first().on('click', this.onClick, this);
28903 onClick : function(e)
28905 if(this.preventDefault){
28906 e.preventDefault();
28909 this.fireEvent("click", this, e);
28912 onMouseOver : function(e)
28914 if(this.submenu && this.pos == 'left'){
28915 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28918 this.fireEvent("mouseover", this, e);
28921 onMouseOut : function(e)
28923 this.fireEvent("mouseout", this, e);
28935 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28938 * @class Roo.bootstrap.menu.Separator
28939 * @extends Roo.bootstrap.Component
28940 * Bootstrap Separator class
28943 * Create a new Separator
28944 * @param {Object} config The config object
28948 Roo.bootstrap.menu.Separator = function(config){
28949 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28952 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28954 getAutoCreate : function(){
28975 * @class Roo.bootstrap.Tooltip
28976 * Bootstrap Tooltip class
28977 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28978 * to determine which dom element triggers the tooltip.
28980 * It needs to add support for additional attributes like tooltip-position
28983 * Create a new Toolti
28984 * @param {Object} config The config object
28987 Roo.bootstrap.Tooltip = function(config){
28988 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28990 this.alignment = Roo.bootstrap.Tooltip.alignment;
28992 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28993 this.alignment = config.alignment;
28998 Roo.apply(Roo.bootstrap.Tooltip, {
29000 * @function init initialize tooltip monitoring.
29004 currentTip : false,
29005 currentRegion : false,
29011 Roo.get(document).on('mouseover', this.enter ,this);
29012 Roo.get(document).on('mouseout', this.leave, this);
29015 this.currentTip = new Roo.bootstrap.Tooltip();
29018 enter : function(ev)
29020 var dom = ev.getTarget();
29022 //Roo.log(['enter',dom]);
29023 var el = Roo.fly(dom);
29024 if (this.currentEl) {
29026 //Roo.log(this.currentEl);
29027 //Roo.log(this.currentEl.contains(dom));
29028 if (this.currentEl == el) {
29031 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29037 if (this.currentTip.el) {
29038 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29042 if(!el || el.dom == document){
29048 // you can not look for children, as if el is the body.. then everythign is the child..
29049 if (!el.attr('tooltip')) { //
29050 if (!el.select("[tooltip]").elements.length) {
29053 // is the mouse over this child...?
29054 bindEl = el.select("[tooltip]").first();
29055 var xy = ev.getXY();
29056 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29057 //Roo.log("not in region.");
29060 //Roo.log("child element over..");
29063 this.currentEl = bindEl;
29064 this.currentTip.bind(bindEl);
29065 this.currentRegion = Roo.lib.Region.getRegion(dom);
29066 this.currentTip.enter();
29069 leave : function(ev)
29071 var dom = ev.getTarget();
29072 //Roo.log(['leave',dom]);
29073 if (!this.currentEl) {
29078 if (dom != this.currentEl.dom) {
29081 var xy = ev.getXY();
29082 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29085 // only activate leave if mouse cursor is outside... bounding box..
29090 if (this.currentTip) {
29091 this.currentTip.leave();
29093 //Roo.log('clear currentEl');
29094 this.currentEl = false;
29099 'left' : ['r-l', [-2,0], 'right'],
29100 'right' : ['l-r', [2,0], 'left'],
29101 'bottom' : ['t-b', [0,2], 'top'],
29102 'top' : [ 'b-t', [0,-2], 'bottom']
29108 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29113 delay : null, // can be { show : 300 , hide: 500}
29117 hoverState : null, //???
29119 placement : 'bottom',
29123 getAutoCreate : function(){
29130 cls : 'tooltip-arrow arrow'
29133 cls : 'tooltip-inner'
29140 bind : function(el)
29145 initEvents : function()
29147 this.arrowEl = this.el.select('.arrow', true).first();
29148 this.innerEl = this.el.select('.tooltip-inner', true).first();
29151 enter : function () {
29153 if (this.timeout != null) {
29154 clearTimeout(this.timeout);
29157 this.hoverState = 'in';
29158 //Roo.log("enter - show");
29159 if (!this.delay || !this.delay.show) {
29164 this.timeout = setTimeout(function () {
29165 if (_t.hoverState == 'in') {
29168 }, this.delay.show);
29172 clearTimeout(this.timeout);
29174 this.hoverState = 'out';
29175 if (!this.delay || !this.delay.hide) {
29181 this.timeout = setTimeout(function () {
29182 //Roo.log("leave - timeout");
29184 if (_t.hoverState == 'out') {
29186 Roo.bootstrap.Tooltip.currentEl = false;
29191 show : function (msg)
29194 this.render(document.body);
29197 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29199 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29201 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29203 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29204 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29206 var placement = typeof this.placement == 'function' ?
29207 this.placement.call(this, this.el, on_el) :
29210 var autoToken = /\s?auto?\s?/i;
29211 var autoPlace = autoToken.test(placement);
29213 placement = placement.replace(autoToken, '') || 'top';
29217 //this.el.setXY([0,0]);
29219 //this.el.dom.style.display='block';
29221 //this.el.appendTo(on_el);
29223 var p = this.getPosition();
29224 var box = this.el.getBox();
29230 var align = this.alignment[placement];
29232 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29234 if(placement == 'top' || placement == 'bottom'){
29236 placement = 'right';
29239 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29240 placement = 'left';
29243 var scroll = Roo.select('body', true).first().getScroll();
29245 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29249 align = this.alignment[placement];
29251 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29255 this.el.alignTo(this.bindEl, align[0],align[1]);
29256 //var arrow = this.el.select('.arrow',true).first();
29257 //arrow.set(align[2],
29259 this.el.addClass(placement);
29260 this.el.addClass("bs-tooltip-"+ placement);
29262 this.el.addClass('in fade show');
29264 this.hoverState = null;
29266 if (this.el.hasClass('fade')) {
29281 //this.el.setXY([0,0]);
29282 this.el.removeClass(['show', 'in']);
29298 * @class Roo.bootstrap.LocationPicker
29299 * @extends Roo.bootstrap.Component
29300 * Bootstrap LocationPicker class
29301 * @cfg {Number} latitude Position when init default 0
29302 * @cfg {Number} longitude Position when init default 0
29303 * @cfg {Number} zoom default 15
29304 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29305 * @cfg {Boolean} mapTypeControl default false
29306 * @cfg {Boolean} disableDoubleClickZoom default false
29307 * @cfg {Boolean} scrollwheel default true
29308 * @cfg {Boolean} streetViewControl default false
29309 * @cfg {Number} radius default 0
29310 * @cfg {String} locationName
29311 * @cfg {Boolean} draggable default true
29312 * @cfg {Boolean} enableAutocomplete default false
29313 * @cfg {Boolean} enableReverseGeocode default true
29314 * @cfg {String} markerTitle
29317 * Create a new LocationPicker
29318 * @param {Object} config The config object
29322 Roo.bootstrap.LocationPicker = function(config){
29324 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29329 * Fires when the picker initialized.
29330 * @param {Roo.bootstrap.LocationPicker} this
29331 * @param {Google Location} location
29335 * @event positionchanged
29336 * Fires when the picker position changed.
29337 * @param {Roo.bootstrap.LocationPicker} this
29338 * @param {Google Location} location
29340 positionchanged : true,
29343 * Fires when the map resize.
29344 * @param {Roo.bootstrap.LocationPicker} this
29349 * Fires when the map show.
29350 * @param {Roo.bootstrap.LocationPicker} this
29355 * Fires when the map hide.
29356 * @param {Roo.bootstrap.LocationPicker} this
29361 * Fires when click the map.
29362 * @param {Roo.bootstrap.LocationPicker} this
29363 * @param {Map event} e
29367 * @event mapRightClick
29368 * Fires when right click the map.
29369 * @param {Roo.bootstrap.LocationPicker} this
29370 * @param {Map event} e
29372 mapRightClick : true,
29374 * @event markerClick
29375 * Fires when click the marker.
29376 * @param {Roo.bootstrap.LocationPicker} this
29377 * @param {Map event} e
29379 markerClick : true,
29381 * @event markerRightClick
29382 * Fires when right click the marker.
29383 * @param {Roo.bootstrap.LocationPicker} this
29384 * @param {Map event} e
29386 markerRightClick : true,
29388 * @event OverlayViewDraw
29389 * Fires when OverlayView Draw
29390 * @param {Roo.bootstrap.LocationPicker} this
29392 OverlayViewDraw : true,
29394 * @event OverlayViewOnAdd
29395 * Fires when OverlayView Draw
29396 * @param {Roo.bootstrap.LocationPicker} this
29398 OverlayViewOnAdd : true,
29400 * @event OverlayViewOnRemove
29401 * Fires when OverlayView Draw
29402 * @param {Roo.bootstrap.LocationPicker} this
29404 OverlayViewOnRemove : true,
29406 * @event OverlayViewShow
29407 * Fires when OverlayView Draw
29408 * @param {Roo.bootstrap.LocationPicker} this
29409 * @param {Pixel} cpx
29411 OverlayViewShow : true,
29413 * @event OverlayViewHide
29414 * Fires when OverlayView Draw
29415 * @param {Roo.bootstrap.LocationPicker} this
29417 OverlayViewHide : true,
29419 * @event loadexception
29420 * Fires when load google lib failed.
29421 * @param {Roo.bootstrap.LocationPicker} this
29423 loadexception : true
29428 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29430 gMapContext: false,
29436 mapTypeControl: false,
29437 disableDoubleClickZoom: false,
29439 streetViewControl: false,
29443 enableAutocomplete: false,
29444 enableReverseGeocode: true,
29447 getAutoCreate: function()
29452 cls: 'roo-location-picker'
29458 initEvents: function(ct, position)
29460 if(!this.el.getWidth() || this.isApplied()){
29464 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29469 initial: function()
29471 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29472 this.fireEvent('loadexception', this);
29476 if(!this.mapTypeId){
29477 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29480 this.gMapContext = this.GMapContext();
29482 this.initOverlayView();
29484 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29488 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29489 _this.setPosition(_this.gMapContext.marker.position);
29492 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29493 _this.fireEvent('mapClick', this, event);
29497 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29498 _this.fireEvent('mapRightClick', this, event);
29502 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29503 _this.fireEvent('markerClick', this, event);
29507 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29508 _this.fireEvent('markerRightClick', this, event);
29512 this.setPosition(this.gMapContext.location);
29514 this.fireEvent('initial', this, this.gMapContext.location);
29517 initOverlayView: function()
29521 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29525 _this.fireEvent('OverlayViewDraw', _this);
29530 _this.fireEvent('OverlayViewOnAdd', _this);
29533 onRemove: function()
29535 _this.fireEvent('OverlayViewOnRemove', _this);
29538 show: function(cpx)
29540 _this.fireEvent('OverlayViewShow', _this, cpx);
29545 _this.fireEvent('OverlayViewHide', _this);
29551 fromLatLngToContainerPixel: function(event)
29553 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29556 isApplied: function()
29558 return this.getGmapContext() == false ? false : true;
29561 getGmapContext: function()
29563 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29566 GMapContext: function()
29568 var position = new google.maps.LatLng(this.latitude, this.longitude);
29570 var _map = new google.maps.Map(this.el.dom, {
29573 mapTypeId: this.mapTypeId,
29574 mapTypeControl: this.mapTypeControl,
29575 disableDoubleClickZoom: this.disableDoubleClickZoom,
29576 scrollwheel: this.scrollwheel,
29577 streetViewControl: this.streetViewControl,
29578 locationName: this.locationName,
29579 draggable: this.draggable,
29580 enableAutocomplete: this.enableAutocomplete,
29581 enableReverseGeocode: this.enableReverseGeocode
29584 var _marker = new google.maps.Marker({
29585 position: position,
29587 title: this.markerTitle,
29588 draggable: this.draggable
29595 location: position,
29596 radius: this.radius,
29597 locationName: this.locationName,
29598 addressComponents: {
29599 formatted_address: null,
29600 addressLine1: null,
29601 addressLine2: null,
29603 streetNumber: null,
29607 stateOrProvince: null
29610 domContainer: this.el.dom,
29611 geodecoder: new google.maps.Geocoder()
29615 drawCircle: function(center, radius, options)
29617 if (this.gMapContext.circle != null) {
29618 this.gMapContext.circle.setMap(null);
29622 options = Roo.apply({}, options, {
29623 strokeColor: "#0000FF",
29624 strokeOpacity: .35,
29626 fillColor: "#0000FF",
29630 options.map = this.gMapContext.map;
29631 options.radius = radius;
29632 options.center = center;
29633 this.gMapContext.circle = new google.maps.Circle(options);
29634 return this.gMapContext.circle;
29640 setPosition: function(location)
29642 this.gMapContext.location = location;
29643 this.gMapContext.marker.setPosition(location);
29644 this.gMapContext.map.panTo(location);
29645 this.drawCircle(location, this.gMapContext.radius, {});
29649 if (this.gMapContext.settings.enableReverseGeocode) {
29650 this.gMapContext.geodecoder.geocode({
29651 latLng: this.gMapContext.location
29652 }, function(results, status) {
29654 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29655 _this.gMapContext.locationName = results[0].formatted_address;
29656 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29658 _this.fireEvent('positionchanged', this, location);
29665 this.fireEvent('positionchanged', this, location);
29670 google.maps.event.trigger(this.gMapContext.map, "resize");
29672 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29674 this.fireEvent('resize', this);
29677 setPositionByLatLng: function(latitude, longitude)
29679 this.setPosition(new google.maps.LatLng(latitude, longitude));
29682 getCurrentPosition: function()
29685 latitude: this.gMapContext.location.lat(),
29686 longitude: this.gMapContext.location.lng()
29690 getAddressName: function()
29692 return this.gMapContext.locationName;
29695 getAddressComponents: function()
29697 return this.gMapContext.addressComponents;
29700 address_component_from_google_geocode: function(address_components)
29704 for (var i = 0; i < address_components.length; i++) {
29705 var component = address_components[i];
29706 if (component.types.indexOf("postal_code") >= 0) {
29707 result.postalCode = component.short_name;
29708 } else if (component.types.indexOf("street_number") >= 0) {
29709 result.streetNumber = component.short_name;
29710 } else if (component.types.indexOf("route") >= 0) {
29711 result.streetName = component.short_name;
29712 } else if (component.types.indexOf("neighborhood") >= 0) {
29713 result.city = component.short_name;
29714 } else if (component.types.indexOf("locality") >= 0) {
29715 result.city = component.short_name;
29716 } else if (component.types.indexOf("sublocality") >= 0) {
29717 result.district = component.short_name;
29718 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29719 result.stateOrProvince = component.short_name;
29720 } else if (component.types.indexOf("country") >= 0) {
29721 result.country = component.short_name;
29725 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29726 result.addressLine2 = "";
29730 setZoomLevel: function(zoom)
29732 this.gMapContext.map.setZoom(zoom);
29745 this.fireEvent('show', this);
29756 this.fireEvent('hide', this);
29761 Roo.apply(Roo.bootstrap.LocationPicker, {
29763 OverlayView : function(map, options)
29765 options = options || {};
29772 * @class Roo.bootstrap.Alert
29773 * @extends Roo.bootstrap.Component
29774 * Bootstrap Alert class - shows an alert area box
29776 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29777 Enter a valid email address
29780 * @cfg {String} title The title of alert
29781 * @cfg {String} html The content of alert
29782 * @cfg {String} weight ( success | info | warning | danger )
29783 * @cfg {String} faicon font-awesomeicon
29786 * Create a new alert
29787 * @param {Object} config The config object
29791 Roo.bootstrap.Alert = function(config){
29792 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29796 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29803 getAutoCreate : function()
29812 cls : 'roo-alert-icon'
29817 cls : 'roo-alert-title',
29822 cls : 'roo-alert-text',
29829 cfg.cn[0].cls += ' fa ' + this.faicon;
29833 cfg.cls += ' alert-' + this.weight;
29839 initEvents: function()
29841 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29844 setTitle : function(str)
29846 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29849 setText : function(str)
29851 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29854 setWeight : function(weight)
29857 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29860 this.weight = weight;
29862 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29865 setIcon : function(icon)
29868 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29871 this.faicon = icon;
29873 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29894 * @class Roo.bootstrap.UploadCropbox
29895 * @extends Roo.bootstrap.Component
29896 * Bootstrap UploadCropbox class
29897 * @cfg {String} emptyText show when image has been loaded
29898 * @cfg {String} rotateNotify show when image too small to rotate
29899 * @cfg {Number} errorTimeout default 3000
29900 * @cfg {Number} minWidth default 300
29901 * @cfg {Number} minHeight default 300
29902 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29903 * @cfg {Boolean} isDocument (true|false) default false
29904 * @cfg {String} url action url
29905 * @cfg {String} paramName default 'imageUpload'
29906 * @cfg {String} method default POST
29907 * @cfg {Boolean} loadMask (true|false) default true
29908 * @cfg {Boolean} loadingText default 'Loading...'
29911 * Create a new UploadCropbox
29912 * @param {Object} config The config object
29915 Roo.bootstrap.UploadCropbox = function(config){
29916 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29920 * @event beforeselectfile
29921 * Fire before select file
29922 * @param {Roo.bootstrap.UploadCropbox} this
29924 "beforeselectfile" : true,
29927 * Fire after initEvent
29928 * @param {Roo.bootstrap.UploadCropbox} this
29933 * Fire after initEvent
29934 * @param {Roo.bootstrap.UploadCropbox} this
29935 * @param {String} data
29940 * Fire when preparing the file data
29941 * @param {Roo.bootstrap.UploadCropbox} this
29942 * @param {Object} file
29947 * Fire when get exception
29948 * @param {Roo.bootstrap.UploadCropbox} this
29949 * @param {XMLHttpRequest} xhr
29951 "exception" : true,
29953 * @event beforeloadcanvas
29954 * Fire before load the canvas
29955 * @param {Roo.bootstrap.UploadCropbox} this
29956 * @param {String} src
29958 "beforeloadcanvas" : true,
29961 * Fire when trash image
29962 * @param {Roo.bootstrap.UploadCropbox} this
29967 * Fire when download the image
29968 * @param {Roo.bootstrap.UploadCropbox} this
29972 * @event footerbuttonclick
29973 * Fire when footerbuttonclick
29974 * @param {Roo.bootstrap.UploadCropbox} this
29975 * @param {String} type
29977 "footerbuttonclick" : true,
29981 * @param {Roo.bootstrap.UploadCropbox} this
29986 * Fire when rotate the image
29987 * @param {Roo.bootstrap.UploadCropbox} this
29988 * @param {String} pos
29993 * Fire when inspect the file
29994 * @param {Roo.bootstrap.UploadCropbox} this
29995 * @param {Object} file
30000 * Fire when xhr upload the file
30001 * @param {Roo.bootstrap.UploadCropbox} this
30002 * @param {Object} data
30007 * Fire when arrange the file data
30008 * @param {Roo.bootstrap.UploadCropbox} this
30009 * @param {Object} formData
30014 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30017 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30019 emptyText : 'Click to upload image',
30020 rotateNotify : 'Image is too small to rotate',
30021 errorTimeout : 3000,
30035 cropType : 'image/jpeg',
30037 canvasLoaded : false,
30038 isDocument : false,
30040 paramName : 'imageUpload',
30042 loadingText : 'Loading...',
30045 getAutoCreate : function()
30049 cls : 'roo-upload-cropbox',
30053 cls : 'roo-upload-cropbox-selector',
30058 cls : 'roo-upload-cropbox-body',
30059 style : 'cursor:pointer',
30063 cls : 'roo-upload-cropbox-preview'
30067 cls : 'roo-upload-cropbox-thumb'
30071 cls : 'roo-upload-cropbox-empty-notify',
30072 html : this.emptyText
30076 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30077 html : this.rotateNotify
30083 cls : 'roo-upload-cropbox-footer',
30086 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30096 onRender : function(ct, position)
30098 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30100 if (this.buttons.length) {
30102 Roo.each(this.buttons, function(bb) {
30104 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30106 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30112 this.maskEl = this.el;
30116 initEvents : function()
30118 this.urlAPI = (window.createObjectURL && window) ||
30119 (window.URL && URL.revokeObjectURL && URL) ||
30120 (window.webkitURL && webkitURL);
30122 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30123 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30125 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30126 this.selectorEl.hide();
30128 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30129 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30131 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30132 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30133 this.thumbEl.hide();
30135 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30136 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30138 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30139 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30140 this.errorEl.hide();
30142 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30143 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30144 this.footerEl.hide();
30146 this.setThumbBoxSize();
30152 this.fireEvent('initial', this);
30159 window.addEventListener("resize", function() { _this.resize(); } );
30161 this.bodyEl.on('click', this.beforeSelectFile, this);
30164 this.bodyEl.on('touchstart', this.onTouchStart, this);
30165 this.bodyEl.on('touchmove', this.onTouchMove, this);
30166 this.bodyEl.on('touchend', this.onTouchEnd, this);
30170 this.bodyEl.on('mousedown', this.onMouseDown, this);
30171 this.bodyEl.on('mousemove', this.onMouseMove, this);
30172 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30173 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30174 Roo.get(document).on('mouseup', this.onMouseUp, this);
30177 this.selectorEl.on('change', this.onFileSelected, this);
30183 this.baseScale = 1;
30185 this.baseRotate = 1;
30186 this.dragable = false;
30187 this.pinching = false;
30190 this.cropData = false;
30191 this.notifyEl.dom.innerHTML = this.emptyText;
30193 this.selectorEl.dom.value = '';
30197 resize : function()
30199 if(this.fireEvent('resize', this) != false){
30200 this.setThumbBoxPosition();
30201 this.setCanvasPosition();
30205 onFooterButtonClick : function(e, el, o, type)
30208 case 'rotate-left' :
30209 this.onRotateLeft(e);
30211 case 'rotate-right' :
30212 this.onRotateRight(e);
30215 this.beforeSelectFile(e);
30230 this.fireEvent('footerbuttonclick', this, type);
30233 beforeSelectFile : function(e)
30235 e.preventDefault();
30237 if(this.fireEvent('beforeselectfile', this) != false){
30238 this.selectorEl.dom.click();
30242 onFileSelected : function(e)
30244 e.preventDefault();
30246 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30250 var file = this.selectorEl.dom.files[0];
30252 if(this.fireEvent('inspect', this, file) != false){
30253 this.prepare(file);
30258 trash : function(e)
30260 this.fireEvent('trash', this);
30263 download : function(e)
30265 this.fireEvent('download', this);
30268 loadCanvas : function(src)
30270 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30274 this.imageEl = document.createElement('img');
30278 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30280 this.imageEl.src = src;
30284 onLoadCanvas : function()
30286 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30287 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30289 this.bodyEl.un('click', this.beforeSelectFile, this);
30291 this.notifyEl.hide();
30292 this.thumbEl.show();
30293 this.footerEl.show();
30295 this.baseRotateLevel();
30297 if(this.isDocument){
30298 this.setThumbBoxSize();
30301 this.setThumbBoxPosition();
30303 this.baseScaleLevel();
30309 this.canvasLoaded = true;
30312 this.maskEl.unmask();
30317 setCanvasPosition : function()
30319 if(!this.canvasEl){
30323 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30324 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30326 this.previewEl.setLeft(pw);
30327 this.previewEl.setTop(ph);
30331 onMouseDown : function(e)
30335 this.dragable = true;
30336 this.pinching = false;
30338 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30339 this.dragable = false;
30343 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30344 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30348 onMouseMove : function(e)
30352 if(!this.canvasLoaded){
30356 if (!this.dragable){
30360 var minX = Math.ceil(this.thumbEl.getLeft(true));
30361 var minY = Math.ceil(this.thumbEl.getTop(true));
30363 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30364 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30366 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30367 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30369 x = x - this.mouseX;
30370 y = y - this.mouseY;
30372 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30373 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30375 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30376 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30378 this.previewEl.setLeft(bgX);
30379 this.previewEl.setTop(bgY);
30381 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30382 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30385 onMouseUp : function(e)
30389 this.dragable = false;
30392 onMouseWheel : function(e)
30396 this.startScale = this.scale;
30398 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30400 if(!this.zoomable()){
30401 this.scale = this.startScale;
30410 zoomable : function()
30412 var minScale = this.thumbEl.getWidth() / this.minWidth;
30414 if(this.minWidth < this.minHeight){
30415 minScale = this.thumbEl.getHeight() / this.minHeight;
30418 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30419 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30423 (this.rotate == 0 || this.rotate == 180) &&
30425 width > this.imageEl.OriginWidth ||
30426 height > this.imageEl.OriginHeight ||
30427 (width < this.minWidth && height < this.minHeight)
30435 (this.rotate == 90 || this.rotate == 270) &&
30437 width > this.imageEl.OriginWidth ||
30438 height > this.imageEl.OriginHeight ||
30439 (width < this.minHeight && height < this.minWidth)
30446 !this.isDocument &&
30447 (this.rotate == 0 || this.rotate == 180) &&
30449 width < this.minWidth ||
30450 width > this.imageEl.OriginWidth ||
30451 height < this.minHeight ||
30452 height > this.imageEl.OriginHeight
30459 !this.isDocument &&
30460 (this.rotate == 90 || this.rotate == 270) &&
30462 width < this.minHeight ||
30463 width > this.imageEl.OriginWidth ||
30464 height < this.minWidth ||
30465 height > this.imageEl.OriginHeight
30475 onRotateLeft : function(e)
30477 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30479 var minScale = this.thumbEl.getWidth() / this.minWidth;
30481 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30482 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30484 this.startScale = this.scale;
30486 while (this.getScaleLevel() < minScale){
30488 this.scale = this.scale + 1;
30490 if(!this.zoomable()){
30495 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30496 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30501 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30508 this.scale = this.startScale;
30510 this.onRotateFail();
30515 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30517 if(this.isDocument){
30518 this.setThumbBoxSize();
30519 this.setThumbBoxPosition();
30520 this.setCanvasPosition();
30525 this.fireEvent('rotate', this, 'left');
30529 onRotateRight : function(e)
30531 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30533 var minScale = this.thumbEl.getWidth() / this.minWidth;
30535 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30536 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30538 this.startScale = this.scale;
30540 while (this.getScaleLevel() < minScale){
30542 this.scale = this.scale + 1;
30544 if(!this.zoomable()){
30549 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30550 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30555 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30562 this.scale = this.startScale;
30564 this.onRotateFail();
30569 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30571 if(this.isDocument){
30572 this.setThumbBoxSize();
30573 this.setThumbBoxPosition();
30574 this.setCanvasPosition();
30579 this.fireEvent('rotate', this, 'right');
30582 onRotateFail : function()
30584 this.errorEl.show(true);
30588 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30593 this.previewEl.dom.innerHTML = '';
30595 var canvasEl = document.createElement("canvas");
30597 var contextEl = canvasEl.getContext("2d");
30599 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30600 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30601 var center = this.imageEl.OriginWidth / 2;
30603 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30604 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30605 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30606 center = this.imageEl.OriginHeight / 2;
30609 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30611 contextEl.translate(center, center);
30612 contextEl.rotate(this.rotate * Math.PI / 180);
30614 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30616 this.canvasEl = document.createElement("canvas");
30618 this.contextEl = this.canvasEl.getContext("2d");
30620 switch (this.rotate) {
30623 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30624 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30626 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30631 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30632 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30634 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30635 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);
30639 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30644 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30645 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30647 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30648 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);
30652 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);
30657 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30658 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30660 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30661 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30665 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);
30672 this.previewEl.appendChild(this.canvasEl);
30674 this.setCanvasPosition();
30679 if(!this.canvasLoaded){
30683 var imageCanvas = document.createElement("canvas");
30685 var imageContext = imageCanvas.getContext("2d");
30687 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30688 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30690 var center = imageCanvas.width / 2;
30692 imageContext.translate(center, center);
30694 imageContext.rotate(this.rotate * Math.PI / 180);
30696 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30698 var canvas = document.createElement("canvas");
30700 var context = canvas.getContext("2d");
30702 canvas.width = this.minWidth;
30703 canvas.height = this.minHeight;
30705 switch (this.rotate) {
30708 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30709 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30711 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30712 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30714 var targetWidth = this.minWidth - 2 * x;
30715 var targetHeight = this.minHeight - 2 * y;
30719 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30720 scale = targetWidth / width;
30723 if(x > 0 && y == 0){
30724 scale = targetHeight / height;
30727 if(x > 0 && y > 0){
30728 scale = targetWidth / width;
30730 if(width < height){
30731 scale = targetHeight / height;
30735 context.scale(scale, scale);
30737 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30738 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30740 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30741 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30743 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30748 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30749 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30751 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30752 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30754 var targetWidth = this.minWidth - 2 * x;
30755 var targetHeight = this.minHeight - 2 * y;
30759 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30760 scale = targetWidth / width;
30763 if(x > 0 && y == 0){
30764 scale = targetHeight / height;
30767 if(x > 0 && y > 0){
30768 scale = targetWidth / width;
30770 if(width < height){
30771 scale = targetHeight / height;
30775 context.scale(scale, scale);
30777 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30778 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30780 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30781 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30783 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30785 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30790 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30791 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30793 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30794 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30796 var targetWidth = this.minWidth - 2 * x;
30797 var targetHeight = this.minHeight - 2 * y;
30801 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30802 scale = targetWidth / width;
30805 if(x > 0 && y == 0){
30806 scale = targetHeight / height;
30809 if(x > 0 && y > 0){
30810 scale = targetWidth / width;
30812 if(width < height){
30813 scale = targetHeight / height;
30817 context.scale(scale, scale);
30819 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30820 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30822 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30823 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30825 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30826 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30828 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30833 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30834 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30836 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30837 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30839 var targetWidth = this.minWidth - 2 * x;
30840 var targetHeight = this.minHeight - 2 * y;
30844 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30845 scale = targetWidth / width;
30848 if(x > 0 && y == 0){
30849 scale = targetHeight / height;
30852 if(x > 0 && y > 0){
30853 scale = targetWidth / width;
30855 if(width < height){
30856 scale = targetHeight / height;
30860 context.scale(scale, scale);
30862 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30863 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30865 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30866 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30868 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30870 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30877 this.cropData = canvas.toDataURL(this.cropType);
30879 if(this.fireEvent('crop', this, this.cropData) !== false){
30880 this.process(this.file, this.cropData);
30887 setThumbBoxSize : function()
30891 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30892 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30893 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30895 this.minWidth = width;
30896 this.minHeight = height;
30898 if(this.rotate == 90 || this.rotate == 270){
30899 this.minWidth = height;
30900 this.minHeight = width;
30905 width = Math.ceil(this.minWidth * height / this.minHeight);
30907 if(this.minWidth > this.minHeight){
30909 height = Math.ceil(this.minHeight * width / this.minWidth);
30912 this.thumbEl.setStyle({
30913 width : width + 'px',
30914 height : height + 'px'
30921 setThumbBoxPosition : function()
30923 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30924 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30926 this.thumbEl.setLeft(x);
30927 this.thumbEl.setTop(y);
30931 baseRotateLevel : function()
30933 this.baseRotate = 1;
30936 typeof(this.exif) != 'undefined' &&
30937 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30938 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30940 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30943 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30947 baseScaleLevel : function()
30951 if(this.isDocument){
30953 if(this.baseRotate == 6 || this.baseRotate == 8){
30955 height = this.thumbEl.getHeight();
30956 this.baseScale = height / this.imageEl.OriginWidth;
30958 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30959 width = this.thumbEl.getWidth();
30960 this.baseScale = width / this.imageEl.OriginHeight;
30966 height = this.thumbEl.getHeight();
30967 this.baseScale = height / this.imageEl.OriginHeight;
30969 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30970 width = this.thumbEl.getWidth();
30971 this.baseScale = width / this.imageEl.OriginWidth;
30977 if(this.baseRotate == 6 || this.baseRotate == 8){
30979 width = this.thumbEl.getHeight();
30980 this.baseScale = width / this.imageEl.OriginHeight;
30982 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30983 height = this.thumbEl.getWidth();
30984 this.baseScale = height / this.imageEl.OriginHeight;
30987 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30988 height = this.thumbEl.getWidth();
30989 this.baseScale = height / this.imageEl.OriginHeight;
30991 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30992 width = this.thumbEl.getHeight();
30993 this.baseScale = width / this.imageEl.OriginWidth;
31000 width = this.thumbEl.getWidth();
31001 this.baseScale = width / this.imageEl.OriginWidth;
31003 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31004 height = this.thumbEl.getHeight();
31005 this.baseScale = height / this.imageEl.OriginHeight;
31008 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31010 height = this.thumbEl.getHeight();
31011 this.baseScale = height / this.imageEl.OriginHeight;
31013 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31014 width = this.thumbEl.getWidth();
31015 this.baseScale = width / this.imageEl.OriginWidth;
31023 getScaleLevel : function()
31025 return this.baseScale * Math.pow(1.1, this.scale);
31028 onTouchStart : function(e)
31030 if(!this.canvasLoaded){
31031 this.beforeSelectFile(e);
31035 var touches = e.browserEvent.touches;
31041 if(touches.length == 1){
31042 this.onMouseDown(e);
31046 if(touches.length != 2){
31052 for(var i = 0, finger; finger = touches[i]; i++){
31053 coords.push(finger.pageX, finger.pageY);
31056 var x = Math.pow(coords[0] - coords[2], 2);
31057 var y = Math.pow(coords[1] - coords[3], 2);
31059 this.startDistance = Math.sqrt(x + y);
31061 this.startScale = this.scale;
31063 this.pinching = true;
31064 this.dragable = false;
31068 onTouchMove : function(e)
31070 if(!this.pinching && !this.dragable){
31074 var touches = e.browserEvent.touches;
31081 this.onMouseMove(e);
31087 for(var i = 0, finger; finger = touches[i]; i++){
31088 coords.push(finger.pageX, finger.pageY);
31091 var x = Math.pow(coords[0] - coords[2], 2);
31092 var y = Math.pow(coords[1] - coords[3], 2);
31094 this.endDistance = Math.sqrt(x + y);
31096 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31098 if(!this.zoomable()){
31099 this.scale = this.startScale;
31107 onTouchEnd : function(e)
31109 this.pinching = false;
31110 this.dragable = false;
31114 process : function(file, crop)
31117 this.maskEl.mask(this.loadingText);
31120 this.xhr = new XMLHttpRequest();
31122 file.xhr = this.xhr;
31124 this.xhr.open(this.method, this.url, true);
31127 "Accept": "application/json",
31128 "Cache-Control": "no-cache",
31129 "X-Requested-With": "XMLHttpRequest"
31132 for (var headerName in headers) {
31133 var headerValue = headers[headerName];
31135 this.xhr.setRequestHeader(headerName, headerValue);
31141 this.xhr.onload = function()
31143 _this.xhrOnLoad(_this.xhr);
31146 this.xhr.onerror = function()
31148 _this.xhrOnError(_this.xhr);
31151 var formData = new FormData();
31153 formData.append('returnHTML', 'NO');
31156 formData.append('crop', crop);
31159 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31160 formData.append(this.paramName, file, file.name);
31163 if(typeof(file.filename) != 'undefined'){
31164 formData.append('filename', file.filename);
31167 if(typeof(file.mimetype) != 'undefined'){
31168 formData.append('mimetype', file.mimetype);
31171 if(this.fireEvent('arrange', this, formData) != false){
31172 this.xhr.send(formData);
31176 xhrOnLoad : function(xhr)
31179 this.maskEl.unmask();
31182 if (xhr.readyState !== 4) {
31183 this.fireEvent('exception', this, xhr);
31187 var response = Roo.decode(xhr.responseText);
31189 if(!response.success){
31190 this.fireEvent('exception', this, xhr);
31194 var response = Roo.decode(xhr.responseText);
31196 this.fireEvent('upload', this, response);
31200 xhrOnError : function()
31203 this.maskEl.unmask();
31206 Roo.log('xhr on error');
31208 var response = Roo.decode(xhr.responseText);
31214 prepare : function(file)
31217 this.maskEl.mask(this.loadingText);
31223 if(typeof(file) === 'string'){
31224 this.loadCanvas(file);
31228 if(!file || !this.urlAPI){
31233 this.cropType = file.type;
31237 if(this.fireEvent('prepare', this, this.file) != false){
31239 var reader = new FileReader();
31241 reader.onload = function (e) {
31242 if (e.target.error) {
31243 Roo.log(e.target.error);
31247 var buffer = e.target.result,
31248 dataView = new DataView(buffer),
31250 maxOffset = dataView.byteLength - 4,
31254 if (dataView.getUint16(0) === 0xffd8) {
31255 while (offset < maxOffset) {
31256 markerBytes = dataView.getUint16(offset);
31258 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31259 markerLength = dataView.getUint16(offset + 2) + 2;
31260 if (offset + markerLength > dataView.byteLength) {
31261 Roo.log('Invalid meta data: Invalid segment size.');
31265 if(markerBytes == 0xffe1){
31266 _this.parseExifData(
31273 offset += markerLength;
31283 var url = _this.urlAPI.createObjectURL(_this.file);
31285 _this.loadCanvas(url);
31290 reader.readAsArrayBuffer(this.file);
31296 parseExifData : function(dataView, offset, length)
31298 var tiffOffset = offset + 10,
31302 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31303 // No Exif data, might be XMP data instead
31307 // Check for the ASCII code for "Exif" (0x45786966):
31308 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31309 // No Exif data, might be XMP data instead
31312 if (tiffOffset + 8 > dataView.byteLength) {
31313 Roo.log('Invalid Exif data: Invalid segment size.');
31316 // Check for the two null bytes:
31317 if (dataView.getUint16(offset + 8) !== 0x0000) {
31318 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31321 // Check the byte alignment:
31322 switch (dataView.getUint16(tiffOffset)) {
31324 littleEndian = true;
31327 littleEndian = false;
31330 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31333 // Check for the TIFF tag marker (0x002A):
31334 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31335 Roo.log('Invalid Exif data: Missing TIFF marker.');
31338 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31339 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31341 this.parseExifTags(
31344 tiffOffset + dirOffset,
31349 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31354 if (dirOffset + 6 > dataView.byteLength) {
31355 Roo.log('Invalid Exif data: Invalid directory offset.');
31358 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31359 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31360 if (dirEndOffset + 4 > dataView.byteLength) {
31361 Roo.log('Invalid Exif data: Invalid directory size.');
31364 for (i = 0; i < tagsNumber; i += 1) {
31368 dirOffset + 2 + 12 * i, // tag offset
31372 // Return the offset to the next directory:
31373 return dataView.getUint32(dirEndOffset, littleEndian);
31376 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31378 var tag = dataView.getUint16(offset, littleEndian);
31380 this.exif[tag] = this.getExifValue(
31384 dataView.getUint16(offset + 2, littleEndian), // tag type
31385 dataView.getUint32(offset + 4, littleEndian), // tag length
31390 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31392 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31401 Roo.log('Invalid Exif data: Invalid tag type.');
31405 tagSize = tagType.size * length;
31406 // Determine if the value is contained in the dataOffset bytes,
31407 // or if the value at the dataOffset is a pointer to the actual data:
31408 dataOffset = tagSize > 4 ?
31409 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31410 if (dataOffset + tagSize > dataView.byteLength) {
31411 Roo.log('Invalid Exif data: Invalid data offset.');
31414 if (length === 1) {
31415 return tagType.getValue(dataView, dataOffset, littleEndian);
31418 for (i = 0; i < length; i += 1) {
31419 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31422 if (tagType.ascii) {
31424 // Concatenate the chars:
31425 for (i = 0; i < values.length; i += 1) {
31427 // Ignore the terminating NULL byte(s):
31428 if (c === '\u0000') {
31440 Roo.apply(Roo.bootstrap.UploadCropbox, {
31442 'Orientation': 0x0112
31446 1: 0, //'top-left',
31448 3: 180, //'bottom-right',
31449 // 4: 'bottom-left',
31451 6: 90, //'right-top',
31452 // 7: 'right-bottom',
31453 8: 270 //'left-bottom'
31457 // byte, 8-bit unsigned int:
31459 getValue: function (dataView, dataOffset) {
31460 return dataView.getUint8(dataOffset);
31464 // ascii, 8-bit byte:
31466 getValue: function (dataView, dataOffset) {
31467 return String.fromCharCode(dataView.getUint8(dataOffset));
31472 // short, 16 bit int:
31474 getValue: function (dataView, dataOffset, littleEndian) {
31475 return dataView.getUint16(dataOffset, littleEndian);
31479 // long, 32 bit int:
31481 getValue: function (dataView, dataOffset, littleEndian) {
31482 return dataView.getUint32(dataOffset, littleEndian);
31486 // rational = two long values, first is numerator, second is denominator:
31488 getValue: function (dataView, dataOffset, littleEndian) {
31489 return dataView.getUint32(dataOffset, littleEndian) /
31490 dataView.getUint32(dataOffset + 4, littleEndian);
31494 // slong, 32 bit signed int:
31496 getValue: function (dataView, dataOffset, littleEndian) {
31497 return dataView.getInt32(dataOffset, littleEndian);
31501 // srational, two slongs, first is numerator, second is denominator:
31503 getValue: function (dataView, dataOffset, littleEndian) {
31504 return dataView.getInt32(dataOffset, littleEndian) /
31505 dataView.getInt32(dataOffset + 4, littleEndian);
31515 cls : 'btn-group roo-upload-cropbox-rotate-left',
31516 action : 'rotate-left',
31520 cls : 'btn btn-default',
31521 html : '<i class="fa fa-undo"></i>'
31527 cls : 'btn-group roo-upload-cropbox-picture',
31528 action : 'picture',
31532 cls : 'btn btn-default',
31533 html : '<i class="fa fa-picture-o"></i>'
31539 cls : 'btn-group roo-upload-cropbox-rotate-right',
31540 action : 'rotate-right',
31544 cls : 'btn btn-default',
31545 html : '<i class="fa fa-repeat"></i>'
31553 cls : 'btn-group roo-upload-cropbox-rotate-left',
31554 action : 'rotate-left',
31558 cls : 'btn btn-default',
31559 html : '<i class="fa fa-undo"></i>'
31565 cls : 'btn-group roo-upload-cropbox-download',
31566 action : 'download',
31570 cls : 'btn btn-default',
31571 html : '<i class="fa fa-download"></i>'
31577 cls : 'btn-group roo-upload-cropbox-crop',
31582 cls : 'btn btn-default',
31583 html : '<i class="fa fa-crop"></i>'
31589 cls : 'btn-group roo-upload-cropbox-trash',
31594 cls : 'btn btn-default',
31595 html : '<i class="fa fa-trash"></i>'
31601 cls : 'btn-group roo-upload-cropbox-rotate-right',
31602 action : 'rotate-right',
31606 cls : 'btn btn-default',
31607 html : '<i class="fa fa-repeat"></i>'
31615 cls : 'btn-group roo-upload-cropbox-rotate-left',
31616 action : 'rotate-left',
31620 cls : 'btn btn-default',
31621 html : '<i class="fa fa-undo"></i>'
31627 cls : 'btn-group roo-upload-cropbox-rotate-right',
31628 action : 'rotate-right',
31632 cls : 'btn btn-default',
31633 html : '<i class="fa fa-repeat"></i>'
31646 * @class Roo.bootstrap.DocumentManager
31647 * @extends Roo.bootstrap.Component
31648 * Bootstrap DocumentManager class
31649 * @cfg {String} paramName default 'imageUpload'
31650 * @cfg {String} toolTipName default 'filename'
31651 * @cfg {String} method default POST
31652 * @cfg {String} url action url
31653 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31654 * @cfg {Boolean} multiple multiple upload default true
31655 * @cfg {Number} thumbSize default 300
31656 * @cfg {String} fieldLabel
31657 * @cfg {Number} labelWidth default 4
31658 * @cfg {String} labelAlign (left|top) default left
31659 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31660 * @cfg {Number} labellg set the width of label (1-12)
31661 * @cfg {Number} labelmd set the width of label (1-12)
31662 * @cfg {Number} labelsm set the width of label (1-12)
31663 * @cfg {Number} labelxs set the width of label (1-12)
31666 * Create a new DocumentManager
31667 * @param {Object} config The config object
31670 Roo.bootstrap.DocumentManager = function(config){
31671 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31674 this.delegates = [];
31679 * Fire when initial the DocumentManager
31680 * @param {Roo.bootstrap.DocumentManager} this
31685 * inspect selected file
31686 * @param {Roo.bootstrap.DocumentManager} this
31687 * @param {File} file
31692 * Fire when xhr load exception
31693 * @param {Roo.bootstrap.DocumentManager} this
31694 * @param {XMLHttpRequest} xhr
31696 "exception" : true,
31698 * @event afterupload
31699 * Fire when xhr load exception
31700 * @param {Roo.bootstrap.DocumentManager} this
31701 * @param {XMLHttpRequest} xhr
31703 "afterupload" : true,
31706 * prepare the form data
31707 * @param {Roo.bootstrap.DocumentManager} this
31708 * @param {Object} formData
31713 * Fire when remove the file
31714 * @param {Roo.bootstrap.DocumentManager} this
31715 * @param {Object} file
31720 * Fire after refresh the file
31721 * @param {Roo.bootstrap.DocumentManager} this
31726 * Fire after click the image
31727 * @param {Roo.bootstrap.DocumentManager} this
31728 * @param {Object} file
31733 * Fire when upload a image and editable set to true
31734 * @param {Roo.bootstrap.DocumentManager} this
31735 * @param {Object} file
31739 * @event beforeselectfile
31740 * Fire before select file
31741 * @param {Roo.bootstrap.DocumentManager} this
31743 "beforeselectfile" : true,
31746 * Fire before process file
31747 * @param {Roo.bootstrap.DocumentManager} this
31748 * @param {Object} file
31752 * @event previewrendered
31753 * Fire when preview rendered
31754 * @param {Roo.bootstrap.DocumentManager} this
31755 * @param {Object} file
31757 "previewrendered" : true,
31760 "previewResize" : true
31765 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31774 paramName : 'imageUpload',
31775 toolTipName : 'filename',
31778 labelAlign : 'left',
31788 getAutoCreate : function()
31790 var managerWidget = {
31792 cls : 'roo-document-manager',
31796 cls : 'roo-document-manager-selector',
31801 cls : 'roo-document-manager-uploader',
31805 cls : 'roo-document-manager-upload-btn',
31806 html : '<i class="fa fa-plus"></i>'
31817 cls : 'column col-md-12',
31822 if(this.fieldLabel.length){
31827 cls : 'column col-md-12',
31828 html : this.fieldLabel
31832 cls : 'column col-md-12',
31837 if(this.labelAlign == 'left'){
31842 html : this.fieldLabel
31851 if(this.labelWidth > 12){
31852 content[0].style = "width: " + this.labelWidth + 'px';
31855 if(this.labelWidth < 13 && this.labelmd == 0){
31856 this.labelmd = this.labelWidth;
31859 if(this.labellg > 0){
31860 content[0].cls += ' col-lg-' + this.labellg;
31861 content[1].cls += ' col-lg-' + (12 - this.labellg);
31864 if(this.labelmd > 0){
31865 content[0].cls += ' col-md-' + this.labelmd;
31866 content[1].cls += ' col-md-' + (12 - this.labelmd);
31869 if(this.labelsm > 0){
31870 content[0].cls += ' col-sm-' + this.labelsm;
31871 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31874 if(this.labelxs > 0){
31875 content[0].cls += ' col-xs-' + this.labelxs;
31876 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31884 cls : 'row clearfix',
31892 initEvents : function()
31894 this.managerEl = this.el.select('.roo-document-manager', true).first();
31895 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31897 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31898 this.selectorEl.hide();
31901 this.selectorEl.attr('multiple', 'multiple');
31904 this.selectorEl.on('change', this.onFileSelected, this);
31906 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31907 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31909 this.uploader.on('click', this.onUploaderClick, this);
31911 this.renderProgressDialog();
31915 window.addEventListener("resize", function() { _this.refresh(); } );
31917 this.fireEvent('initial', this);
31920 renderProgressDialog : function()
31924 this.progressDialog = new Roo.bootstrap.Modal({
31925 cls : 'roo-document-manager-progress-dialog',
31926 allow_close : false,
31937 btnclick : function() {
31938 _this.uploadCancel();
31944 this.progressDialog.render(Roo.get(document.body));
31946 this.progress = new Roo.bootstrap.Progress({
31947 cls : 'roo-document-manager-progress',
31952 this.progress.render(this.progressDialog.getChildContainer());
31954 this.progressBar = new Roo.bootstrap.ProgressBar({
31955 cls : 'roo-document-manager-progress-bar',
31958 aria_valuemax : 12,
31962 this.progressBar.render(this.progress.getChildContainer());
31965 onUploaderClick : function(e)
31967 e.preventDefault();
31969 if(this.fireEvent('beforeselectfile', this) != false){
31970 this.selectorEl.dom.click();
31975 onFileSelected : function(e)
31977 e.preventDefault();
31979 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31983 Roo.each(this.selectorEl.dom.files, function(file){
31984 if(this.fireEvent('inspect', this, file) != false){
31985 this.files.push(file);
31995 this.selectorEl.dom.value = '';
31997 if(!this.files || !this.files.length){
32001 if(this.boxes > 0 && this.files.length > this.boxes){
32002 this.files = this.files.slice(0, this.boxes);
32005 this.uploader.show();
32007 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32008 this.uploader.hide();
32017 Roo.each(this.files, function(file){
32019 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32020 var f = this.renderPreview(file);
32025 if(file.type.indexOf('image') != -1){
32026 this.delegates.push(
32028 _this.process(file);
32029 }).createDelegate(this)
32037 _this.process(file);
32038 }).createDelegate(this)
32043 this.files = files;
32045 this.delegates = this.delegates.concat(docs);
32047 if(!this.delegates.length){
32052 this.progressBar.aria_valuemax = this.delegates.length;
32059 arrange : function()
32061 if(!this.delegates.length){
32062 this.progressDialog.hide();
32067 var delegate = this.delegates.shift();
32069 this.progressDialog.show();
32071 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32073 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32078 refresh : function()
32080 this.uploader.show();
32082 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32083 this.uploader.hide();
32086 Roo.isTouch ? this.closable(false) : this.closable(true);
32088 this.fireEvent('refresh', this);
32091 onRemove : function(e, el, o)
32093 e.preventDefault();
32095 this.fireEvent('remove', this, o);
32099 remove : function(o)
32103 Roo.each(this.files, function(file){
32104 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32113 this.files = files;
32120 Roo.each(this.files, function(file){
32125 file.target.remove();
32134 onClick : function(e, el, o)
32136 e.preventDefault();
32138 this.fireEvent('click', this, o);
32142 closable : function(closable)
32144 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32146 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32158 xhrOnLoad : function(xhr)
32160 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32164 if (xhr.readyState !== 4) {
32166 this.fireEvent('exception', this, xhr);
32170 var response = Roo.decode(xhr.responseText);
32172 if(!response.success){
32174 this.fireEvent('exception', this, xhr);
32178 var file = this.renderPreview(response.data);
32180 this.files.push(file);
32184 this.fireEvent('afterupload', this, xhr);
32188 xhrOnError : function(xhr)
32190 Roo.log('xhr on error');
32192 var response = Roo.decode(xhr.responseText);
32199 process : function(file)
32201 if(this.fireEvent('process', this, file) !== false){
32202 if(this.editable && file.type.indexOf('image') != -1){
32203 this.fireEvent('edit', this, file);
32207 this.uploadStart(file, false);
32214 uploadStart : function(file, crop)
32216 this.xhr = new XMLHttpRequest();
32218 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32223 file.xhr = this.xhr;
32225 this.managerEl.createChild({
32227 cls : 'roo-document-manager-loading',
32231 tooltip : file.name,
32232 cls : 'roo-document-manager-thumb',
32233 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32239 this.xhr.open(this.method, this.url, true);
32242 "Accept": "application/json",
32243 "Cache-Control": "no-cache",
32244 "X-Requested-With": "XMLHttpRequest"
32247 for (var headerName in headers) {
32248 var headerValue = headers[headerName];
32250 this.xhr.setRequestHeader(headerName, headerValue);
32256 this.xhr.onload = function()
32258 _this.xhrOnLoad(_this.xhr);
32261 this.xhr.onerror = function()
32263 _this.xhrOnError(_this.xhr);
32266 var formData = new FormData();
32268 formData.append('returnHTML', 'NO');
32271 formData.append('crop', crop);
32274 formData.append(this.paramName, file, file.name);
32281 if(this.fireEvent('prepare', this, formData, options) != false){
32283 if(options.manually){
32287 this.xhr.send(formData);
32291 this.uploadCancel();
32294 uploadCancel : function()
32300 this.delegates = [];
32302 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32309 renderPreview : function(file)
32311 if(typeof(file.target) != 'undefined' && file.target){
32315 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32317 var previewEl = this.managerEl.createChild({
32319 cls : 'roo-document-manager-preview',
32323 tooltip : file[this.toolTipName],
32324 cls : 'roo-document-manager-thumb',
32325 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32330 html : '<i class="fa fa-times-circle"></i>'
32335 var close = previewEl.select('button.close', true).first();
32337 close.on('click', this.onRemove, this, file);
32339 file.target = previewEl;
32341 var image = previewEl.select('img', true).first();
32345 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32347 image.on('click', this.onClick, this, file);
32349 this.fireEvent('previewrendered', this, file);
32355 onPreviewLoad : function(file, image)
32357 if(typeof(file.target) == 'undefined' || !file.target){
32361 var width = image.dom.naturalWidth || image.dom.width;
32362 var height = image.dom.naturalHeight || image.dom.height;
32364 if(!this.previewResize) {
32368 if(width > height){
32369 file.target.addClass('wide');
32373 file.target.addClass('tall');
32378 uploadFromSource : function(file, crop)
32380 this.xhr = new XMLHttpRequest();
32382 this.managerEl.createChild({
32384 cls : 'roo-document-manager-loading',
32388 tooltip : file.name,
32389 cls : 'roo-document-manager-thumb',
32390 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32396 this.xhr.open(this.method, this.url, true);
32399 "Accept": "application/json",
32400 "Cache-Control": "no-cache",
32401 "X-Requested-With": "XMLHttpRequest"
32404 for (var headerName in headers) {
32405 var headerValue = headers[headerName];
32407 this.xhr.setRequestHeader(headerName, headerValue);
32413 this.xhr.onload = function()
32415 _this.xhrOnLoad(_this.xhr);
32418 this.xhr.onerror = function()
32420 _this.xhrOnError(_this.xhr);
32423 var formData = new FormData();
32425 formData.append('returnHTML', 'NO');
32427 formData.append('crop', crop);
32429 if(typeof(file.filename) != 'undefined'){
32430 formData.append('filename', file.filename);
32433 if(typeof(file.mimetype) != 'undefined'){
32434 formData.append('mimetype', file.mimetype);
32439 if(this.fireEvent('prepare', this, formData) != false){
32440 this.xhr.send(formData);
32450 * @class Roo.bootstrap.DocumentViewer
32451 * @extends Roo.bootstrap.Component
32452 * Bootstrap DocumentViewer class
32453 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32454 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32457 * Create a new DocumentViewer
32458 * @param {Object} config The config object
32461 Roo.bootstrap.DocumentViewer = function(config){
32462 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32467 * Fire after initEvent
32468 * @param {Roo.bootstrap.DocumentViewer} this
32474 * @param {Roo.bootstrap.DocumentViewer} this
32479 * Fire after download button
32480 * @param {Roo.bootstrap.DocumentViewer} this
32485 * Fire after trash button
32486 * @param {Roo.bootstrap.DocumentViewer} this
32493 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32495 showDownload : true,
32499 getAutoCreate : function()
32503 cls : 'roo-document-viewer',
32507 cls : 'roo-document-viewer-body',
32511 cls : 'roo-document-viewer-thumb',
32515 cls : 'roo-document-viewer-image'
32523 cls : 'roo-document-viewer-footer',
32526 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32530 cls : 'btn-group roo-document-viewer-download',
32534 cls : 'btn btn-default',
32535 html : '<i class="fa fa-download"></i>'
32541 cls : 'btn-group roo-document-viewer-trash',
32545 cls : 'btn btn-default',
32546 html : '<i class="fa fa-trash"></i>'
32559 initEvents : function()
32561 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32562 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32564 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32565 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32567 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32568 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32570 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32571 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32573 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32574 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32576 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32577 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32579 this.bodyEl.on('click', this.onClick, this);
32580 this.downloadBtn.on('click', this.onDownload, this);
32581 this.trashBtn.on('click', this.onTrash, this);
32583 this.downloadBtn.hide();
32584 this.trashBtn.hide();
32586 if(this.showDownload){
32587 this.downloadBtn.show();
32590 if(this.showTrash){
32591 this.trashBtn.show();
32594 if(!this.showDownload && !this.showTrash) {
32595 this.footerEl.hide();
32600 initial : function()
32602 this.fireEvent('initial', this);
32606 onClick : function(e)
32608 e.preventDefault();
32610 this.fireEvent('click', this);
32613 onDownload : function(e)
32615 e.preventDefault();
32617 this.fireEvent('download', this);
32620 onTrash : function(e)
32622 e.preventDefault();
32624 this.fireEvent('trash', this);
32636 * @class Roo.bootstrap.NavProgressBar
32637 * @extends Roo.bootstrap.Component
32638 * Bootstrap NavProgressBar class
32641 * Create a new nav progress bar
32642 * @param {Object} config The config object
32645 Roo.bootstrap.NavProgressBar = function(config){
32646 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32648 this.bullets = this.bullets || [];
32650 // Roo.bootstrap.NavProgressBar.register(this);
32654 * Fires when the active item changes
32655 * @param {Roo.bootstrap.NavProgressBar} this
32656 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32657 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32664 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32669 getAutoCreate : function()
32671 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32675 cls : 'roo-navigation-bar-group',
32679 cls : 'roo-navigation-top-bar'
32683 cls : 'roo-navigation-bullets-bar',
32687 cls : 'roo-navigation-bar'
32694 cls : 'roo-navigation-bottom-bar'
32704 initEvents: function()
32709 onRender : function(ct, position)
32711 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32713 if(this.bullets.length){
32714 Roo.each(this.bullets, function(b){
32723 addItem : function(cfg)
32725 var item = new Roo.bootstrap.NavProgressItem(cfg);
32727 item.parentId = this.id;
32728 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32731 var top = new Roo.bootstrap.Element({
32733 cls : 'roo-navigation-bar-text'
32736 var bottom = new Roo.bootstrap.Element({
32738 cls : 'roo-navigation-bar-text'
32741 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32742 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32744 var topText = new Roo.bootstrap.Element({
32746 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32749 var bottomText = new Roo.bootstrap.Element({
32751 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32754 topText.onRender(top.el, null);
32755 bottomText.onRender(bottom.el, null);
32758 item.bottomEl = bottom;
32761 this.barItems.push(item);
32766 getActive : function()
32768 var active = false;
32770 Roo.each(this.barItems, function(v){
32772 if (!v.isActive()) {
32784 setActiveItem : function(item)
32788 Roo.each(this.barItems, function(v){
32789 if (v.rid == item.rid) {
32793 if (v.isActive()) {
32794 v.setActive(false);
32799 item.setActive(true);
32801 this.fireEvent('changed', this, item, prev);
32804 getBarItem: function(rid)
32808 Roo.each(this.barItems, function(e) {
32809 if (e.rid != rid) {
32820 indexOfItem : function(item)
32824 Roo.each(this.barItems, function(v, i){
32826 if (v.rid != item.rid) {
32837 setActiveNext : function()
32839 var i = this.indexOfItem(this.getActive());
32841 if (i > this.barItems.length) {
32845 this.setActiveItem(this.barItems[i+1]);
32848 setActivePrev : function()
32850 var i = this.indexOfItem(this.getActive());
32856 this.setActiveItem(this.barItems[i-1]);
32859 format : function()
32861 if(!this.barItems.length){
32865 var width = 100 / this.barItems.length;
32867 Roo.each(this.barItems, function(i){
32868 i.el.setStyle('width', width + '%');
32869 i.topEl.el.setStyle('width', width + '%');
32870 i.bottomEl.el.setStyle('width', width + '%');
32879 * Nav Progress Item
32884 * @class Roo.bootstrap.NavProgressItem
32885 * @extends Roo.bootstrap.Component
32886 * Bootstrap NavProgressItem class
32887 * @cfg {String} rid the reference id
32888 * @cfg {Boolean} active (true|false) Is item active default false
32889 * @cfg {Boolean} disabled (true|false) Is item active default false
32890 * @cfg {String} html
32891 * @cfg {String} position (top|bottom) text position default bottom
32892 * @cfg {String} icon show icon instead of number
32895 * Create a new NavProgressItem
32896 * @param {Object} config The config object
32898 Roo.bootstrap.NavProgressItem = function(config){
32899 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32904 * The raw click event for the entire grid.
32905 * @param {Roo.bootstrap.NavProgressItem} this
32906 * @param {Roo.EventObject} e
32913 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32919 position : 'bottom',
32922 getAutoCreate : function()
32924 var iconCls = 'roo-navigation-bar-item-icon';
32926 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32930 cls: 'roo-navigation-bar-item',
32940 cfg.cls += ' active';
32943 cfg.cls += ' disabled';
32949 disable : function()
32951 this.setDisabled(true);
32954 enable : function()
32956 this.setDisabled(false);
32959 initEvents: function()
32961 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32963 this.iconEl.on('click', this.onClick, this);
32966 onClick : function(e)
32968 e.preventDefault();
32974 if(this.fireEvent('click', this, e) === false){
32978 this.parent().setActiveItem(this);
32981 isActive: function ()
32983 return this.active;
32986 setActive : function(state)
32988 if(this.active == state){
32992 this.active = state;
32995 this.el.addClass('active');
32999 this.el.removeClass('active');
33004 setDisabled : function(state)
33006 if(this.disabled == state){
33010 this.disabled = state;
33013 this.el.addClass('disabled');
33017 this.el.removeClass('disabled');
33020 tooltipEl : function()
33022 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33035 * @class Roo.bootstrap.FieldLabel
33036 * @extends Roo.bootstrap.Component
33037 * Bootstrap FieldLabel class
33038 * @cfg {String} html contents of the element
33039 * @cfg {String} tag tag of the element default label
33040 * @cfg {String} cls class of the element
33041 * @cfg {String} target label target
33042 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33043 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33044 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33045 * @cfg {String} iconTooltip default "This field is required"
33046 * @cfg {String} indicatorpos (left|right) default left
33049 * Create a new FieldLabel
33050 * @param {Object} config The config object
33053 Roo.bootstrap.FieldLabel = function(config){
33054 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33059 * Fires after the field has been marked as invalid.
33060 * @param {Roo.form.FieldLabel} this
33061 * @param {String} msg The validation message
33066 * Fires after the field has been validated with no errors.
33067 * @param {Roo.form.FieldLabel} this
33073 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33080 invalidClass : 'has-warning',
33081 validClass : 'has-success',
33082 iconTooltip : 'This field is required',
33083 indicatorpos : 'left',
33085 getAutoCreate : function(){
33088 if (!this.allowBlank) {
33094 cls : 'roo-bootstrap-field-label ' + this.cls,
33099 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33100 tooltip : this.iconTooltip
33109 if(this.indicatorpos == 'right'){
33112 cls : 'roo-bootstrap-field-label ' + this.cls,
33121 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33122 tooltip : this.iconTooltip
33131 initEvents: function()
33133 Roo.bootstrap.Element.superclass.initEvents.call(this);
33135 this.indicator = this.indicatorEl();
33137 if(this.indicator){
33138 this.indicator.removeClass('visible');
33139 this.indicator.addClass('invisible');
33142 Roo.bootstrap.FieldLabel.register(this);
33145 indicatorEl : function()
33147 var indicator = this.el.select('i.roo-required-indicator',true).first();
33158 * Mark this field as valid
33160 markValid : function()
33162 if(this.indicator){
33163 this.indicator.removeClass('visible');
33164 this.indicator.addClass('invisible');
33166 if (Roo.bootstrap.version == 3) {
33167 this.el.removeClass(this.invalidClass);
33168 this.el.addClass(this.validClass);
33170 this.el.removeClass('is-invalid');
33171 this.el.addClass('is-valid');
33175 this.fireEvent('valid', this);
33179 * Mark this field as invalid
33180 * @param {String} msg The validation message
33182 markInvalid : function(msg)
33184 if(this.indicator){
33185 this.indicator.removeClass('invisible');
33186 this.indicator.addClass('visible');
33188 if (Roo.bootstrap.version == 3) {
33189 this.el.removeClass(this.validClass);
33190 this.el.addClass(this.invalidClass);
33192 this.el.removeClass('is-valid');
33193 this.el.addClass('is-invalid');
33197 this.fireEvent('invalid', this, msg);
33203 Roo.apply(Roo.bootstrap.FieldLabel, {
33208 * register a FieldLabel Group
33209 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33211 register : function(label)
33213 if(this.groups.hasOwnProperty(label.target)){
33217 this.groups[label.target] = label;
33221 * fetch a FieldLabel Group based on the target
33222 * @param {string} target
33223 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33225 get: function(target) {
33226 if (typeof(this.groups[target]) == 'undefined') {
33230 return this.groups[target] ;
33239 * page DateSplitField.
33245 * @class Roo.bootstrap.DateSplitField
33246 * @extends Roo.bootstrap.Component
33247 * Bootstrap DateSplitField class
33248 * @cfg {string} fieldLabel - the label associated
33249 * @cfg {Number} labelWidth set the width of label (0-12)
33250 * @cfg {String} labelAlign (top|left)
33251 * @cfg {Boolean} dayAllowBlank (true|false) default false
33252 * @cfg {Boolean} monthAllowBlank (true|false) default false
33253 * @cfg {Boolean} yearAllowBlank (true|false) default false
33254 * @cfg {string} dayPlaceholder
33255 * @cfg {string} monthPlaceholder
33256 * @cfg {string} yearPlaceholder
33257 * @cfg {string} dayFormat default 'd'
33258 * @cfg {string} monthFormat default 'm'
33259 * @cfg {string} yearFormat default 'Y'
33260 * @cfg {Number} labellg set the width of label (1-12)
33261 * @cfg {Number} labelmd set the width of label (1-12)
33262 * @cfg {Number} labelsm set the width of label (1-12)
33263 * @cfg {Number} labelxs set the width of label (1-12)
33267 * Create a new DateSplitField
33268 * @param {Object} config The config object
33271 Roo.bootstrap.DateSplitField = function(config){
33272 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33278 * getting the data of years
33279 * @param {Roo.bootstrap.DateSplitField} this
33280 * @param {Object} years
33285 * getting the data of days
33286 * @param {Roo.bootstrap.DateSplitField} this
33287 * @param {Object} days
33292 * Fires after the field has been marked as invalid.
33293 * @param {Roo.form.Field} this
33294 * @param {String} msg The validation message
33299 * Fires after the field has been validated with no errors.
33300 * @param {Roo.form.Field} this
33306 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33309 labelAlign : 'top',
33311 dayAllowBlank : false,
33312 monthAllowBlank : false,
33313 yearAllowBlank : false,
33314 dayPlaceholder : '',
33315 monthPlaceholder : '',
33316 yearPlaceholder : '',
33320 isFormField : true,
33326 getAutoCreate : function()
33330 cls : 'row roo-date-split-field-group',
33335 cls : 'form-hidden-field roo-date-split-field-group-value',
33341 var labelCls = 'col-md-12';
33342 var contentCls = 'col-md-4';
33344 if(this.fieldLabel){
33348 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33352 html : this.fieldLabel
33357 if(this.labelAlign == 'left'){
33359 if(this.labelWidth > 12){
33360 label.style = "width: " + this.labelWidth + 'px';
33363 if(this.labelWidth < 13 && this.labelmd == 0){
33364 this.labelmd = this.labelWidth;
33367 if(this.labellg > 0){
33368 labelCls = ' col-lg-' + this.labellg;
33369 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33372 if(this.labelmd > 0){
33373 labelCls = ' col-md-' + this.labelmd;
33374 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33377 if(this.labelsm > 0){
33378 labelCls = ' col-sm-' + this.labelsm;
33379 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33382 if(this.labelxs > 0){
33383 labelCls = ' col-xs-' + this.labelxs;
33384 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33388 label.cls += ' ' + labelCls;
33390 cfg.cn.push(label);
33393 Roo.each(['day', 'month', 'year'], function(t){
33396 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33403 inputEl: function ()
33405 return this.el.select('.roo-date-split-field-group-value', true).first();
33408 onRender : function(ct, position)
33412 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33414 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33416 this.dayField = new Roo.bootstrap.ComboBox({
33417 allowBlank : this.dayAllowBlank,
33418 alwaysQuery : true,
33419 displayField : 'value',
33422 forceSelection : true,
33424 placeholder : this.dayPlaceholder,
33425 selectOnFocus : true,
33426 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33427 triggerAction : 'all',
33429 valueField : 'value',
33430 store : new Roo.data.SimpleStore({
33431 data : (function() {
33433 _this.fireEvent('days', _this, days);
33436 fields : [ 'value' ]
33439 select : function (_self, record, index)
33441 _this.setValue(_this.getValue());
33446 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33448 this.monthField = new Roo.bootstrap.MonthField({
33449 after : '<i class=\"fa fa-calendar\"></i>',
33450 allowBlank : this.monthAllowBlank,
33451 placeholder : this.monthPlaceholder,
33454 render : function (_self)
33456 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33457 e.preventDefault();
33461 select : function (_self, oldvalue, newvalue)
33463 _this.setValue(_this.getValue());
33468 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33470 this.yearField = new Roo.bootstrap.ComboBox({
33471 allowBlank : this.yearAllowBlank,
33472 alwaysQuery : true,
33473 displayField : 'value',
33476 forceSelection : true,
33478 placeholder : this.yearPlaceholder,
33479 selectOnFocus : true,
33480 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33481 triggerAction : 'all',
33483 valueField : 'value',
33484 store : new Roo.data.SimpleStore({
33485 data : (function() {
33487 _this.fireEvent('years', _this, years);
33490 fields : [ 'value' ]
33493 select : function (_self, record, index)
33495 _this.setValue(_this.getValue());
33500 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33503 setValue : function(v, format)
33505 this.inputEl.dom.value = v;
33507 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33509 var d = Date.parseDate(v, f);
33516 this.setDay(d.format(this.dayFormat));
33517 this.setMonth(d.format(this.monthFormat));
33518 this.setYear(d.format(this.yearFormat));
33525 setDay : function(v)
33527 this.dayField.setValue(v);
33528 this.inputEl.dom.value = this.getValue();
33533 setMonth : function(v)
33535 this.monthField.setValue(v, true);
33536 this.inputEl.dom.value = this.getValue();
33541 setYear : function(v)
33543 this.yearField.setValue(v);
33544 this.inputEl.dom.value = this.getValue();
33549 getDay : function()
33551 return this.dayField.getValue();
33554 getMonth : function()
33556 return this.monthField.getValue();
33559 getYear : function()
33561 return this.yearField.getValue();
33564 getValue : function()
33566 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33568 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33578 this.inputEl.dom.value = '';
33583 validate : function()
33585 var d = this.dayField.validate();
33586 var m = this.monthField.validate();
33587 var y = this.yearField.validate();
33592 (!this.dayAllowBlank && !d) ||
33593 (!this.monthAllowBlank && !m) ||
33594 (!this.yearAllowBlank && !y)
33599 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33608 this.markInvalid();
33613 markValid : function()
33616 var label = this.el.select('label', true).first();
33617 var icon = this.el.select('i.fa-star', true).first();
33623 this.fireEvent('valid', this);
33627 * Mark this field as invalid
33628 * @param {String} msg The validation message
33630 markInvalid : function(msg)
33633 var label = this.el.select('label', true).first();
33634 var icon = this.el.select('i.fa-star', true).first();
33636 if(label && !icon){
33637 this.el.select('.roo-date-split-field-label', true).createChild({
33639 cls : 'text-danger fa fa-lg fa-star',
33640 tooltip : 'This field is required',
33641 style : 'margin-right:5px;'
33645 this.fireEvent('invalid', this, msg);
33648 clearInvalid : function()
33650 var label = this.el.select('label', true).first();
33651 var icon = this.el.select('i.fa-star', true).first();
33657 this.fireEvent('valid', this);
33660 getName: function()
33670 * http://masonry.desandro.com
33672 * The idea is to render all the bricks based on vertical width...
33674 * The original code extends 'outlayer' - we might need to use that....
33680 * @class Roo.bootstrap.LayoutMasonry
33681 * @extends Roo.bootstrap.Component
33682 * Bootstrap Layout Masonry class
33685 * Create a new Element
33686 * @param {Object} config The config object
33689 Roo.bootstrap.LayoutMasonry = function(config){
33691 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33695 Roo.bootstrap.LayoutMasonry.register(this);
33701 * Fire after layout the items
33702 * @param {Roo.bootstrap.LayoutMasonry} this
33703 * @param {Roo.EventObject} e
33710 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33713 * @cfg {Boolean} isLayoutInstant = no animation?
33715 isLayoutInstant : false, // needed?
33718 * @cfg {Number} boxWidth width of the columns
33723 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33728 * @cfg {Number} padWidth padding below box..
33733 * @cfg {Number} gutter gutter width..
33738 * @cfg {Number} maxCols maximum number of columns
33744 * @cfg {Boolean} isAutoInitial defalut true
33746 isAutoInitial : true,
33751 * @cfg {Boolean} isHorizontal defalut false
33753 isHorizontal : false,
33755 currentSize : null,
33761 bricks: null, //CompositeElement
33765 _isLayoutInited : false,
33767 // isAlternative : false, // only use for vertical layout...
33770 * @cfg {Number} alternativePadWidth padding below box..
33772 alternativePadWidth : 50,
33774 selectedBrick : [],
33776 getAutoCreate : function(){
33778 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33782 cls: 'blog-masonary-wrapper ' + this.cls,
33784 cls : 'mas-boxes masonary'
33791 getChildContainer: function( )
33793 if (this.boxesEl) {
33794 return this.boxesEl;
33797 this.boxesEl = this.el.select('.mas-boxes').first();
33799 return this.boxesEl;
33803 initEvents : function()
33807 if(this.isAutoInitial){
33808 Roo.log('hook children rendered');
33809 this.on('childrenrendered', function() {
33810 Roo.log('children rendered');
33816 initial : function()
33818 this.selectedBrick = [];
33820 this.currentSize = this.el.getBox(true);
33822 Roo.EventManager.onWindowResize(this.resize, this);
33824 if(!this.isAutoInitial){
33832 //this.layout.defer(500,this);
33836 resize : function()
33838 var cs = this.el.getBox(true);
33841 this.currentSize.width == cs.width &&
33842 this.currentSize.x == cs.x &&
33843 this.currentSize.height == cs.height &&
33844 this.currentSize.y == cs.y
33846 Roo.log("no change in with or X or Y");
33850 this.currentSize = cs;
33856 layout : function()
33858 this._resetLayout();
33860 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33862 this.layoutItems( isInstant );
33864 this._isLayoutInited = true;
33866 this.fireEvent('layout', this);
33870 _resetLayout : function()
33872 if(this.isHorizontal){
33873 this.horizontalMeasureColumns();
33877 this.verticalMeasureColumns();
33881 verticalMeasureColumns : function()
33883 this.getContainerWidth();
33885 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33886 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33890 var boxWidth = this.boxWidth + this.padWidth;
33892 if(this.containerWidth < this.boxWidth){
33893 boxWidth = this.containerWidth
33896 var containerWidth = this.containerWidth;
33898 var cols = Math.floor(containerWidth / boxWidth);
33900 this.cols = Math.max( cols, 1 );
33902 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33904 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33906 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33908 this.colWidth = boxWidth + avail - this.padWidth;
33910 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33911 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33914 horizontalMeasureColumns : function()
33916 this.getContainerWidth();
33918 var boxWidth = this.boxWidth;
33920 if(this.containerWidth < boxWidth){
33921 boxWidth = this.containerWidth;
33924 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33926 this.el.setHeight(boxWidth);
33930 getContainerWidth : function()
33932 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33935 layoutItems : function( isInstant )
33937 Roo.log(this.bricks);
33939 var items = Roo.apply([], this.bricks);
33941 if(this.isHorizontal){
33942 this._horizontalLayoutItems( items , isInstant );
33946 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33947 // this._verticalAlternativeLayoutItems( items , isInstant );
33951 this._verticalLayoutItems( items , isInstant );
33955 _verticalLayoutItems : function ( items , isInstant)
33957 if ( !items || !items.length ) {
33962 ['xs', 'xs', 'xs', 'tall'],
33963 ['xs', 'xs', 'tall'],
33964 ['xs', 'xs', 'sm'],
33965 ['xs', 'xs', 'xs'],
33971 ['sm', 'xs', 'xs'],
33975 ['tall', 'xs', 'xs', 'xs'],
33976 ['tall', 'xs', 'xs'],
33988 Roo.each(items, function(item, k){
33990 switch (item.size) {
33991 // these layouts take up a full box,
34002 boxes.push([item]);
34025 var filterPattern = function(box, length)
34033 var pattern = box.slice(0, length);
34037 Roo.each(pattern, function(i){
34038 format.push(i.size);
34041 Roo.each(standard, function(s){
34043 if(String(s) != String(format)){
34052 if(!match && length == 1){
34057 filterPattern(box, length - 1);
34061 queue.push(pattern);
34063 box = box.slice(length, box.length);
34065 filterPattern(box, 4);
34071 Roo.each(boxes, function(box, k){
34077 if(box.length == 1){
34082 filterPattern(box, 4);
34086 this._processVerticalLayoutQueue( queue, isInstant );
34090 // _verticalAlternativeLayoutItems : function( items , isInstant )
34092 // if ( !items || !items.length ) {
34096 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34100 _horizontalLayoutItems : function ( items , isInstant)
34102 if ( !items || !items.length || items.length < 3) {
34108 var eItems = items.slice(0, 3);
34110 items = items.slice(3, items.length);
34113 ['xs', 'xs', 'xs', 'wide'],
34114 ['xs', 'xs', 'wide'],
34115 ['xs', 'xs', 'sm'],
34116 ['xs', 'xs', 'xs'],
34122 ['sm', 'xs', 'xs'],
34126 ['wide', 'xs', 'xs', 'xs'],
34127 ['wide', 'xs', 'xs'],
34140 Roo.each(items, function(item, k){
34142 switch (item.size) {
34153 boxes.push([item]);
34177 var filterPattern = function(box, length)
34185 var pattern = box.slice(0, length);
34189 Roo.each(pattern, function(i){
34190 format.push(i.size);
34193 Roo.each(standard, function(s){
34195 if(String(s) != String(format)){
34204 if(!match && length == 1){
34209 filterPattern(box, length - 1);
34213 queue.push(pattern);
34215 box = box.slice(length, box.length);
34217 filterPattern(box, 4);
34223 Roo.each(boxes, function(box, k){
34229 if(box.length == 1){
34234 filterPattern(box, 4);
34241 var pos = this.el.getBox(true);
34245 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34247 var hit_end = false;
34249 Roo.each(queue, function(box){
34253 Roo.each(box, function(b){
34255 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34265 Roo.each(box, function(b){
34267 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34270 mx = Math.max(mx, b.x);
34274 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34278 Roo.each(box, function(b){
34280 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34294 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34297 /** Sets position of item in DOM
34298 * @param {Element} item
34299 * @param {Number} x - horizontal position
34300 * @param {Number} y - vertical position
34301 * @param {Boolean} isInstant - disables transitions
34303 _processVerticalLayoutQueue : function( queue, isInstant )
34305 var pos = this.el.getBox(true);
34310 for (var i = 0; i < this.cols; i++){
34314 Roo.each(queue, function(box, k){
34316 var col = k % this.cols;
34318 Roo.each(box, function(b,kk){
34320 b.el.position('absolute');
34322 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34323 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34325 if(b.size == 'md-left' || b.size == 'md-right'){
34326 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34327 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34330 b.el.setWidth(width);
34331 b.el.setHeight(height);
34333 b.el.select('iframe',true).setSize(width,height);
34337 for (var i = 0; i < this.cols; i++){
34339 if(maxY[i] < maxY[col]){
34344 col = Math.min(col, i);
34348 x = pos.x + col * (this.colWidth + this.padWidth);
34352 var positions = [];
34354 switch (box.length){
34356 positions = this.getVerticalOneBoxColPositions(x, y, box);
34359 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34362 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34365 positions = this.getVerticalFourBoxColPositions(x, y, box);
34371 Roo.each(box, function(b,kk){
34373 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34375 var sz = b.el.getSize();
34377 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34385 for (var i = 0; i < this.cols; i++){
34386 mY = Math.max(mY, maxY[i]);
34389 this.el.setHeight(mY - pos.y);
34393 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34395 // var pos = this.el.getBox(true);
34398 // var maxX = pos.right;
34400 // var maxHeight = 0;
34402 // Roo.each(items, function(item, k){
34406 // item.el.position('absolute');
34408 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34410 // item.el.setWidth(width);
34412 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34414 // item.el.setHeight(height);
34417 // item.el.setXY([x, y], isInstant ? false : true);
34419 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34422 // y = y + height + this.alternativePadWidth;
34424 // maxHeight = maxHeight + height + this.alternativePadWidth;
34428 // this.el.setHeight(maxHeight);
34432 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34434 var pos = this.el.getBox(true);
34439 var maxX = pos.right;
34441 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34443 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34445 Roo.each(queue, function(box, k){
34447 Roo.each(box, function(b, kk){
34449 b.el.position('absolute');
34451 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34452 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34454 if(b.size == 'md-left' || b.size == 'md-right'){
34455 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34456 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34459 b.el.setWidth(width);
34460 b.el.setHeight(height);
34468 var positions = [];
34470 switch (box.length){
34472 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34475 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34478 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34481 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34487 Roo.each(box, function(b,kk){
34489 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34491 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34499 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34501 Roo.each(eItems, function(b,k){
34503 b.size = (k == 0) ? 'sm' : 'xs';
34504 b.x = (k == 0) ? 2 : 1;
34505 b.y = (k == 0) ? 2 : 1;
34507 b.el.position('absolute');
34509 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34511 b.el.setWidth(width);
34513 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34515 b.el.setHeight(height);
34519 var positions = [];
34522 x : maxX - this.unitWidth * 2 - this.gutter,
34527 x : maxX - this.unitWidth,
34528 y : minY + (this.unitWidth + this.gutter) * 2
34532 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34536 Roo.each(eItems, function(b,k){
34538 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34544 getVerticalOneBoxColPositions : function(x, y, box)
34548 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34550 if(box[0].size == 'md-left'){
34554 if(box[0].size == 'md-right'){
34559 x : x + (this.unitWidth + this.gutter) * rand,
34566 getVerticalTwoBoxColPositions : function(x, y, box)
34570 if(box[0].size == 'xs'){
34574 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34578 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34592 x : x + (this.unitWidth + this.gutter) * 2,
34593 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34600 getVerticalThreeBoxColPositions : function(x, y, box)
34604 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34612 x : x + (this.unitWidth + this.gutter) * 1,
34617 x : x + (this.unitWidth + this.gutter) * 2,
34625 if(box[0].size == 'xs' && box[1].size == 'xs'){
34634 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34638 x : x + (this.unitWidth + this.gutter) * 1,
34652 x : x + (this.unitWidth + this.gutter) * 2,
34657 x : x + (this.unitWidth + this.gutter) * 2,
34658 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34665 getVerticalFourBoxColPositions : function(x, y, box)
34669 if(box[0].size == 'xs'){
34678 y : y + (this.unitHeight + this.gutter) * 1
34683 y : y + (this.unitHeight + this.gutter) * 2
34687 x : x + (this.unitWidth + this.gutter) * 1,
34701 x : x + (this.unitWidth + this.gutter) * 2,
34706 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34707 y : y + (this.unitHeight + this.gutter) * 1
34711 x : x + (this.unitWidth + this.gutter) * 2,
34712 y : y + (this.unitWidth + this.gutter) * 2
34719 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34723 if(box[0].size == 'md-left'){
34725 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34732 if(box[0].size == 'md-right'){
34734 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34735 y : minY + (this.unitWidth + this.gutter) * 1
34741 var rand = Math.floor(Math.random() * (4 - box[0].y));
34744 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34745 y : minY + (this.unitWidth + this.gutter) * rand
34752 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34756 if(box[0].size == 'xs'){
34759 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34764 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34765 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34773 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34778 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34779 y : minY + (this.unitWidth + this.gutter) * 2
34786 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34790 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34793 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34798 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34799 y : minY + (this.unitWidth + this.gutter) * 1
34803 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34804 y : minY + (this.unitWidth + this.gutter) * 2
34811 if(box[0].size == 'xs' && box[1].size == 'xs'){
34814 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34819 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34824 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34825 y : minY + (this.unitWidth + this.gutter) * 1
34833 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34838 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34839 y : minY + (this.unitWidth + this.gutter) * 2
34843 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34844 y : minY + (this.unitWidth + this.gutter) * 2
34851 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34855 if(box[0].size == 'xs'){
34858 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34863 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34868 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),
34873 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34874 y : minY + (this.unitWidth + this.gutter) * 1
34882 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34887 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34888 y : minY + (this.unitWidth + this.gutter) * 2
34892 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34893 y : minY + (this.unitWidth + this.gutter) * 2
34897 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),
34898 y : minY + (this.unitWidth + this.gutter) * 2
34906 * remove a Masonry Brick
34907 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34909 removeBrick : function(brick_id)
34915 for (var i = 0; i<this.bricks.length; i++) {
34916 if (this.bricks[i].id == brick_id) {
34917 this.bricks.splice(i,1);
34918 this.el.dom.removeChild(Roo.get(brick_id).dom);
34925 * adds a Masonry Brick
34926 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34928 addBrick : function(cfg)
34930 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34931 //this.register(cn);
34932 cn.parentId = this.id;
34933 cn.render(this.el);
34938 * register a Masonry Brick
34939 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34942 register : function(brick)
34944 this.bricks.push(brick);
34945 brick.masonryId = this.id;
34949 * clear all the Masonry Brick
34951 clearAll : function()
34954 //this.getChildContainer().dom.innerHTML = "";
34955 this.el.dom.innerHTML = '';
34958 getSelected : function()
34960 if (!this.selectedBrick) {
34964 return this.selectedBrick;
34968 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34972 * register a Masonry Layout
34973 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34976 register : function(layout)
34978 this.groups[layout.id] = layout;
34981 * fetch a Masonry Layout based on the masonry layout ID
34982 * @param {string} the masonry layout to add
34983 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34986 get: function(layout_id) {
34987 if (typeof(this.groups[layout_id]) == 'undefined') {
34990 return this.groups[layout_id] ;
35002 * http://masonry.desandro.com
35004 * The idea is to render all the bricks based on vertical width...
35006 * The original code extends 'outlayer' - we might need to use that....
35012 * @class Roo.bootstrap.LayoutMasonryAuto
35013 * @extends Roo.bootstrap.Component
35014 * Bootstrap Layout Masonry class
35017 * Create a new Element
35018 * @param {Object} config The config object
35021 Roo.bootstrap.LayoutMasonryAuto = function(config){
35022 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35025 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35028 * @cfg {Boolean} isFitWidth - resize the width..
35030 isFitWidth : false, // options..
35032 * @cfg {Boolean} isOriginLeft = left align?
35034 isOriginLeft : true,
35036 * @cfg {Boolean} isOriginTop = top align?
35038 isOriginTop : false,
35040 * @cfg {Boolean} isLayoutInstant = no animation?
35042 isLayoutInstant : false, // needed?
35044 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35046 isResizingContainer : true,
35048 * @cfg {Number} columnWidth width of the columns
35054 * @cfg {Number} maxCols maximum number of columns
35059 * @cfg {Number} padHeight padding below box..
35065 * @cfg {Boolean} isAutoInitial defalut true
35068 isAutoInitial : true,
35074 initialColumnWidth : 0,
35075 currentSize : null,
35077 colYs : null, // array.
35084 bricks: null, //CompositeElement
35085 cols : 0, // array?
35086 // element : null, // wrapped now this.el
35087 _isLayoutInited : null,
35090 getAutoCreate : function(){
35094 cls: 'blog-masonary-wrapper ' + this.cls,
35096 cls : 'mas-boxes masonary'
35103 getChildContainer: function( )
35105 if (this.boxesEl) {
35106 return this.boxesEl;
35109 this.boxesEl = this.el.select('.mas-boxes').first();
35111 return this.boxesEl;
35115 initEvents : function()
35119 if(this.isAutoInitial){
35120 Roo.log('hook children rendered');
35121 this.on('childrenrendered', function() {
35122 Roo.log('children rendered');
35129 initial : function()
35131 this.reloadItems();
35133 this.currentSize = this.el.getBox(true);
35135 /// was window resize... - let's see if this works..
35136 Roo.EventManager.onWindowResize(this.resize, this);
35138 if(!this.isAutoInitial){
35143 this.layout.defer(500,this);
35146 reloadItems: function()
35148 this.bricks = this.el.select('.masonry-brick', true);
35150 this.bricks.each(function(b) {
35151 //Roo.log(b.getSize());
35152 if (!b.attr('originalwidth')) {
35153 b.attr('originalwidth', b.getSize().width);
35158 Roo.log(this.bricks.elements.length);
35161 resize : function()
35164 var cs = this.el.getBox(true);
35166 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35167 Roo.log("no change in with or X");
35170 this.currentSize = cs;
35174 layout : function()
35177 this._resetLayout();
35178 //this._manageStamps();
35180 // don't animate first layout
35181 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35182 this.layoutItems( isInstant );
35184 // flag for initalized
35185 this._isLayoutInited = true;
35188 layoutItems : function( isInstant )
35190 //var items = this._getItemsForLayout( this.items );
35191 // original code supports filtering layout items.. we just ignore it..
35193 this._layoutItems( this.bricks , isInstant );
35195 this._postLayout();
35197 _layoutItems : function ( items , isInstant)
35199 //this.fireEvent( 'layout', this, items );
35202 if ( !items || !items.elements.length ) {
35203 // no items, emit event with empty array
35208 items.each(function(item) {
35209 Roo.log("layout item");
35211 // get x/y object from method
35212 var position = this._getItemLayoutPosition( item );
35214 position.item = item;
35215 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35216 queue.push( position );
35219 this._processLayoutQueue( queue );
35221 /** Sets position of item in DOM
35222 * @param {Element} item
35223 * @param {Number} x - horizontal position
35224 * @param {Number} y - vertical position
35225 * @param {Boolean} isInstant - disables transitions
35227 _processLayoutQueue : function( queue )
35229 for ( var i=0, len = queue.length; i < len; i++ ) {
35230 var obj = queue[i];
35231 obj.item.position('absolute');
35232 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35238 * Any logic you want to do after each layout,
35239 * i.e. size the container
35241 _postLayout : function()
35243 this.resizeContainer();
35246 resizeContainer : function()
35248 if ( !this.isResizingContainer ) {
35251 var size = this._getContainerSize();
35253 this.el.setSize(size.width,size.height);
35254 this.boxesEl.setSize(size.width,size.height);
35260 _resetLayout : function()
35262 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35263 this.colWidth = this.el.getWidth();
35264 //this.gutter = this.el.getWidth();
35266 this.measureColumns();
35272 this.colYs.push( 0 );
35278 measureColumns : function()
35280 this.getContainerWidth();
35281 // if columnWidth is 0, default to outerWidth of first item
35282 if ( !this.columnWidth ) {
35283 var firstItem = this.bricks.first();
35284 Roo.log(firstItem);
35285 this.columnWidth = this.containerWidth;
35286 if (firstItem && firstItem.attr('originalwidth') ) {
35287 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35289 // columnWidth fall back to item of first element
35290 Roo.log("set column width?");
35291 this.initialColumnWidth = this.columnWidth ;
35293 // if first elem has no width, default to size of container
35298 if (this.initialColumnWidth) {
35299 this.columnWidth = this.initialColumnWidth;
35304 // column width is fixed at the top - however if container width get's smaller we should
35307 // this bit calcs how man columns..
35309 var columnWidth = this.columnWidth += this.gutter;
35311 // calculate columns
35312 var containerWidth = this.containerWidth + this.gutter;
35314 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35315 // fix rounding errors, typically with gutters
35316 var excess = columnWidth - containerWidth % columnWidth;
35319 // if overshoot is less than a pixel, round up, otherwise floor it
35320 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35321 cols = Math[ mathMethod ]( cols );
35322 this.cols = Math.max( cols, 1 );
35323 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35325 // padding positioning..
35326 var totalColWidth = this.cols * this.columnWidth;
35327 var padavail = this.containerWidth - totalColWidth;
35328 // so for 2 columns - we need 3 'pads'
35330 var padNeeded = (1+this.cols) * this.padWidth;
35332 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35334 this.columnWidth += padExtra
35335 //this.padWidth = Math.floor(padavail / ( this.cols));
35337 // adjust colum width so that padding is fixed??
35339 // we have 3 columns ... total = width * 3
35340 // we have X left over... that should be used by
35342 //if (this.expandC) {
35350 getContainerWidth : function()
35352 /* // container is parent if fit width
35353 var container = this.isFitWidth ? this.element.parentNode : this.element;
35354 // check that this.size and size are there
35355 // IE8 triggers resize on body size change, so they might not be
35357 var size = getSize( container ); //FIXME
35358 this.containerWidth = size && size.innerWidth; //FIXME
35361 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35365 _getItemLayoutPosition : function( item ) // what is item?
35367 // we resize the item to our columnWidth..
35369 item.setWidth(this.columnWidth);
35370 item.autoBoxAdjust = false;
35372 var sz = item.getSize();
35374 // how many columns does this brick span
35375 var remainder = this.containerWidth % this.columnWidth;
35377 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35378 // round if off by 1 pixel, otherwise use ceil
35379 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35380 colSpan = Math.min( colSpan, this.cols );
35382 // normally this should be '1' as we dont' currently allow multi width columns..
35384 var colGroup = this._getColGroup( colSpan );
35385 // get the minimum Y value from the columns
35386 var minimumY = Math.min.apply( Math, colGroup );
35387 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35389 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35391 // position the brick
35393 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35394 y: this.currentSize.y + minimumY + this.padHeight
35398 // apply setHeight to necessary columns
35399 var setHeight = minimumY + sz.height + this.padHeight;
35400 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35402 var setSpan = this.cols + 1 - colGroup.length;
35403 for ( var i = 0; i < setSpan; i++ ) {
35404 this.colYs[ shortColIndex + i ] = setHeight ;
35411 * @param {Number} colSpan - number of columns the element spans
35412 * @returns {Array} colGroup
35414 _getColGroup : function( colSpan )
35416 if ( colSpan < 2 ) {
35417 // if brick spans only one column, use all the column Ys
35422 // how many different places could this brick fit horizontally
35423 var groupCount = this.cols + 1 - colSpan;
35424 // for each group potential horizontal position
35425 for ( var i = 0; i < groupCount; i++ ) {
35426 // make an array of colY values for that one group
35427 var groupColYs = this.colYs.slice( i, i + colSpan );
35428 // and get the max value of the array
35429 colGroup[i] = Math.max.apply( Math, groupColYs );
35434 _manageStamp : function( stamp )
35436 var stampSize = stamp.getSize();
35437 var offset = stamp.getBox();
35438 // get the columns that this stamp affects
35439 var firstX = this.isOriginLeft ? offset.x : offset.right;
35440 var lastX = firstX + stampSize.width;
35441 var firstCol = Math.floor( firstX / this.columnWidth );
35442 firstCol = Math.max( 0, firstCol );
35444 var lastCol = Math.floor( lastX / this.columnWidth );
35445 // lastCol should not go over if multiple of columnWidth #425
35446 lastCol -= lastX % this.columnWidth ? 0 : 1;
35447 lastCol = Math.min( this.cols - 1, lastCol );
35449 // set colYs to bottom of the stamp
35450 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35453 for ( var i = firstCol; i <= lastCol; i++ ) {
35454 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35459 _getContainerSize : function()
35461 this.maxY = Math.max.apply( Math, this.colYs );
35466 if ( this.isFitWidth ) {
35467 size.width = this._getContainerFitWidth();
35473 _getContainerFitWidth : function()
35475 var unusedCols = 0;
35476 // count unused columns
35479 if ( this.colYs[i] !== 0 ) {
35484 // fit container to columns that have been used
35485 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35488 needsResizeLayout : function()
35490 var previousWidth = this.containerWidth;
35491 this.getContainerWidth();
35492 return previousWidth !== this.containerWidth;
35507 * @class Roo.bootstrap.MasonryBrick
35508 * @extends Roo.bootstrap.Component
35509 * Bootstrap MasonryBrick class
35512 * Create a new MasonryBrick
35513 * @param {Object} config The config object
35516 Roo.bootstrap.MasonryBrick = function(config){
35518 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35520 Roo.bootstrap.MasonryBrick.register(this);
35526 * When a MasonryBrick is clcik
35527 * @param {Roo.bootstrap.MasonryBrick} this
35528 * @param {Roo.EventObject} e
35534 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35537 * @cfg {String} title
35541 * @cfg {String} html
35545 * @cfg {String} bgimage
35549 * @cfg {String} videourl
35553 * @cfg {String} cls
35557 * @cfg {String} href
35561 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35566 * @cfg {String} placetitle (center|bottom)
35571 * @cfg {Boolean} isFitContainer defalut true
35573 isFitContainer : true,
35576 * @cfg {Boolean} preventDefault defalut false
35578 preventDefault : false,
35581 * @cfg {Boolean} inverse defalut false
35583 maskInverse : false,
35585 getAutoCreate : function()
35587 if(!this.isFitContainer){
35588 return this.getSplitAutoCreate();
35591 var cls = 'masonry-brick masonry-brick-full';
35593 if(this.href.length){
35594 cls += ' masonry-brick-link';
35597 if(this.bgimage.length){
35598 cls += ' masonry-brick-image';
35601 if(this.maskInverse){
35602 cls += ' mask-inverse';
35605 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35606 cls += ' enable-mask';
35610 cls += ' masonry-' + this.size + '-brick';
35613 if(this.placetitle.length){
35615 switch (this.placetitle) {
35617 cls += ' masonry-center-title';
35620 cls += ' masonry-bottom-title';
35627 if(!this.html.length && !this.bgimage.length){
35628 cls += ' masonry-center-title';
35631 if(!this.html.length && this.bgimage.length){
35632 cls += ' masonry-bottom-title';
35637 cls += ' ' + this.cls;
35641 tag: (this.href.length) ? 'a' : 'div',
35646 cls: 'masonry-brick-mask'
35650 cls: 'masonry-brick-paragraph',
35656 if(this.href.length){
35657 cfg.href = this.href;
35660 var cn = cfg.cn[1].cn;
35662 if(this.title.length){
35665 cls: 'masonry-brick-title',
35670 if(this.html.length){
35673 cls: 'masonry-brick-text',
35678 if (!this.title.length && !this.html.length) {
35679 cfg.cn[1].cls += ' hide';
35682 if(this.bgimage.length){
35685 cls: 'masonry-brick-image-view',
35690 if(this.videourl.length){
35691 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35692 // youtube support only?
35695 cls: 'masonry-brick-image-view',
35698 allowfullscreen : true
35706 getSplitAutoCreate : function()
35708 var cls = 'masonry-brick masonry-brick-split';
35710 if(this.href.length){
35711 cls += ' masonry-brick-link';
35714 if(this.bgimage.length){
35715 cls += ' masonry-brick-image';
35719 cls += ' masonry-' + this.size + '-brick';
35722 switch (this.placetitle) {
35724 cls += ' masonry-center-title';
35727 cls += ' masonry-bottom-title';
35730 if(!this.bgimage.length){
35731 cls += ' masonry-center-title';
35734 if(this.bgimage.length){
35735 cls += ' masonry-bottom-title';
35741 cls += ' ' + this.cls;
35745 tag: (this.href.length) ? 'a' : 'div',
35750 cls: 'masonry-brick-split-head',
35754 cls: 'masonry-brick-paragraph',
35761 cls: 'masonry-brick-split-body',
35767 if(this.href.length){
35768 cfg.href = this.href;
35771 if(this.title.length){
35772 cfg.cn[0].cn[0].cn.push({
35774 cls: 'masonry-brick-title',
35779 if(this.html.length){
35780 cfg.cn[1].cn.push({
35782 cls: 'masonry-brick-text',
35787 if(this.bgimage.length){
35788 cfg.cn[0].cn.push({
35790 cls: 'masonry-brick-image-view',
35795 if(this.videourl.length){
35796 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35797 // youtube support only?
35798 cfg.cn[0].cn.cn.push({
35800 cls: 'masonry-brick-image-view',
35803 allowfullscreen : true
35810 initEvents: function()
35812 switch (this.size) {
35845 this.el.on('touchstart', this.onTouchStart, this);
35846 this.el.on('touchmove', this.onTouchMove, this);
35847 this.el.on('touchend', this.onTouchEnd, this);
35848 this.el.on('contextmenu', this.onContextMenu, this);
35850 this.el.on('mouseenter' ,this.enter, this);
35851 this.el.on('mouseleave', this.leave, this);
35852 this.el.on('click', this.onClick, this);
35855 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35856 this.parent().bricks.push(this);
35861 onClick: function(e, el)
35863 var time = this.endTimer - this.startTimer;
35864 // Roo.log(e.preventDefault());
35867 e.preventDefault();
35872 if(!this.preventDefault){
35876 e.preventDefault();
35878 if (this.activeClass != '') {
35879 this.selectBrick();
35882 this.fireEvent('click', this, e);
35885 enter: function(e, el)
35887 e.preventDefault();
35889 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35893 if(this.bgimage.length && this.html.length){
35894 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35898 leave: function(e, el)
35900 e.preventDefault();
35902 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35906 if(this.bgimage.length && this.html.length){
35907 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35911 onTouchStart: function(e, el)
35913 // e.preventDefault();
35915 this.touchmoved = false;
35917 if(!this.isFitContainer){
35921 if(!this.bgimage.length || !this.html.length){
35925 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35927 this.timer = new Date().getTime();
35931 onTouchMove: function(e, el)
35933 this.touchmoved = true;
35936 onContextMenu : function(e,el)
35938 e.preventDefault();
35939 e.stopPropagation();
35943 onTouchEnd: function(e, el)
35945 // e.preventDefault();
35947 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35954 if(!this.bgimage.length || !this.html.length){
35956 if(this.href.length){
35957 window.location.href = this.href;
35963 if(!this.isFitContainer){
35967 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35969 window.location.href = this.href;
35972 //selection on single brick only
35973 selectBrick : function() {
35975 if (!this.parentId) {
35979 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35980 var index = m.selectedBrick.indexOf(this.id);
35983 m.selectedBrick.splice(index,1);
35984 this.el.removeClass(this.activeClass);
35988 for(var i = 0; i < m.selectedBrick.length; i++) {
35989 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35990 b.el.removeClass(b.activeClass);
35993 m.selectedBrick = [];
35995 m.selectedBrick.push(this.id);
35996 this.el.addClass(this.activeClass);
36000 isSelected : function(){
36001 return this.el.hasClass(this.activeClass);
36006 Roo.apply(Roo.bootstrap.MasonryBrick, {
36009 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36011 * register a Masonry Brick
36012 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36015 register : function(brick)
36017 //this.groups[brick.id] = brick;
36018 this.groups.add(brick.id, brick);
36021 * fetch a masonry brick based on the masonry brick ID
36022 * @param {string} the masonry brick to add
36023 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36026 get: function(brick_id)
36028 // if (typeof(this.groups[brick_id]) == 'undefined') {
36031 // return this.groups[brick_id] ;
36033 if(this.groups.key(brick_id)) {
36034 return this.groups.key(brick_id);
36052 * @class Roo.bootstrap.Brick
36053 * @extends Roo.bootstrap.Component
36054 * Bootstrap Brick class
36057 * Create a new Brick
36058 * @param {Object} config The config object
36061 Roo.bootstrap.Brick = function(config){
36062 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36068 * When a Brick is click
36069 * @param {Roo.bootstrap.Brick} this
36070 * @param {Roo.EventObject} e
36076 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36079 * @cfg {String} title
36083 * @cfg {String} html
36087 * @cfg {String} bgimage
36091 * @cfg {String} cls
36095 * @cfg {String} href
36099 * @cfg {String} video
36103 * @cfg {Boolean} square
36107 getAutoCreate : function()
36109 var cls = 'roo-brick';
36111 if(this.href.length){
36112 cls += ' roo-brick-link';
36115 if(this.bgimage.length){
36116 cls += ' roo-brick-image';
36119 if(!this.html.length && !this.bgimage.length){
36120 cls += ' roo-brick-center-title';
36123 if(!this.html.length && this.bgimage.length){
36124 cls += ' roo-brick-bottom-title';
36128 cls += ' ' + this.cls;
36132 tag: (this.href.length) ? 'a' : 'div',
36137 cls: 'roo-brick-paragraph',
36143 if(this.href.length){
36144 cfg.href = this.href;
36147 var cn = cfg.cn[0].cn;
36149 if(this.title.length){
36152 cls: 'roo-brick-title',
36157 if(this.html.length){
36160 cls: 'roo-brick-text',
36167 if(this.bgimage.length){
36170 cls: 'roo-brick-image-view',
36178 initEvents: function()
36180 if(this.title.length || this.html.length){
36181 this.el.on('mouseenter' ,this.enter, this);
36182 this.el.on('mouseleave', this.leave, this);
36185 Roo.EventManager.onWindowResize(this.resize, this);
36187 if(this.bgimage.length){
36188 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36189 this.imageEl.on('load', this.onImageLoad, this);
36196 onImageLoad : function()
36201 resize : function()
36203 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36205 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36207 if(this.bgimage.length){
36208 var image = this.el.select('.roo-brick-image-view', true).first();
36210 image.setWidth(paragraph.getWidth());
36213 image.setHeight(paragraph.getWidth());
36216 this.el.setHeight(image.getHeight());
36217 paragraph.setHeight(image.getHeight());
36223 enter: function(e, el)
36225 e.preventDefault();
36227 if(this.bgimage.length){
36228 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36229 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36233 leave: function(e, el)
36235 e.preventDefault();
36237 if(this.bgimage.length){
36238 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36239 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36254 * @class Roo.bootstrap.NumberField
36255 * @extends Roo.bootstrap.Input
36256 * Bootstrap NumberField class
36262 * Create a new NumberField
36263 * @param {Object} config The config object
36266 Roo.bootstrap.NumberField = function(config){
36267 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36270 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36273 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36275 allowDecimals : true,
36277 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36279 decimalSeparator : ".",
36281 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36283 decimalPrecision : 2,
36285 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36287 allowNegative : true,
36290 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36294 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36296 minValue : Number.NEGATIVE_INFINITY,
36298 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36300 maxValue : Number.MAX_VALUE,
36302 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36304 minText : "The minimum value for this field is {0}",
36306 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36308 maxText : "The maximum value for this field is {0}",
36310 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36311 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36313 nanText : "{0} is not a valid number",
36315 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36317 thousandsDelimiter : false,
36319 * @cfg {String} valueAlign alignment of value
36321 valueAlign : "left",
36323 getAutoCreate : function()
36325 var hiddenInput = {
36329 cls: 'hidden-number-input'
36333 hiddenInput.name = this.name;
36338 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36340 this.name = hiddenInput.name;
36342 if(cfg.cn.length > 0) {
36343 cfg.cn.push(hiddenInput);
36350 initEvents : function()
36352 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36354 var allowed = "0123456789";
36356 if(this.allowDecimals){
36357 allowed += this.decimalSeparator;
36360 if(this.allowNegative){
36364 if(this.thousandsDelimiter) {
36368 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36370 var keyPress = function(e){
36372 var k = e.getKey();
36374 var c = e.getCharCode();
36377 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36378 allowed.indexOf(String.fromCharCode(c)) === -1
36384 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36388 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36393 this.el.on("keypress", keyPress, this);
36396 validateValue : function(value)
36399 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36403 var num = this.parseValue(value);
36406 this.markInvalid(String.format(this.nanText, value));
36410 if(num < this.minValue){
36411 this.markInvalid(String.format(this.minText, this.minValue));
36415 if(num > this.maxValue){
36416 this.markInvalid(String.format(this.maxText, this.maxValue));
36423 getValue : function()
36425 var v = this.hiddenEl().getValue();
36427 return this.fixPrecision(this.parseValue(v));
36430 parseValue : function(value)
36432 if(this.thousandsDelimiter) {
36434 r = new RegExp(",", "g");
36435 value = value.replace(r, "");
36438 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36439 return isNaN(value) ? '' : value;
36442 fixPrecision : function(value)
36444 if(this.thousandsDelimiter) {
36446 r = new RegExp(",", "g");
36447 value = value.replace(r, "");
36450 var nan = isNaN(value);
36452 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36453 return nan ? '' : value;
36455 return parseFloat(value).toFixed(this.decimalPrecision);
36458 setValue : function(v)
36460 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36466 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36468 this.inputEl().dom.value = (v == '') ? '' :
36469 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36471 if(!this.allowZero && v === '0') {
36472 this.hiddenEl().dom.value = '';
36473 this.inputEl().dom.value = '';
36480 decimalPrecisionFcn : function(v)
36482 return Math.floor(v);
36485 beforeBlur : function()
36487 var v = this.parseValue(this.getRawValue());
36489 if(v || v === 0 || v === ''){
36494 hiddenEl : function()
36496 return this.el.select('input.hidden-number-input',true).first();
36508 * @class Roo.bootstrap.DocumentSlider
36509 * @extends Roo.bootstrap.Component
36510 * Bootstrap DocumentSlider class
36513 * Create a new DocumentViewer
36514 * @param {Object} config The config object
36517 Roo.bootstrap.DocumentSlider = function(config){
36518 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36525 * Fire after initEvent
36526 * @param {Roo.bootstrap.DocumentSlider} this
36531 * Fire after update
36532 * @param {Roo.bootstrap.DocumentSlider} this
36538 * @param {Roo.bootstrap.DocumentSlider} this
36544 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36550 getAutoCreate : function()
36554 cls : 'roo-document-slider',
36558 cls : 'roo-document-slider-header',
36562 cls : 'roo-document-slider-header-title'
36568 cls : 'roo-document-slider-body',
36572 cls : 'roo-document-slider-prev',
36576 cls : 'fa fa-chevron-left'
36582 cls : 'roo-document-slider-thumb',
36586 cls : 'roo-document-slider-image'
36592 cls : 'roo-document-slider-next',
36596 cls : 'fa fa-chevron-right'
36608 initEvents : function()
36610 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36611 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36613 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36614 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36616 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36617 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36619 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36620 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36622 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36623 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36625 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36626 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36628 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36629 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36631 this.thumbEl.on('click', this.onClick, this);
36633 this.prevIndicator.on('click', this.prev, this);
36635 this.nextIndicator.on('click', this.next, this);
36639 initial : function()
36641 if(this.files.length){
36642 this.indicator = 1;
36646 this.fireEvent('initial', this);
36649 update : function()
36651 this.imageEl.attr('src', this.files[this.indicator - 1]);
36653 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36655 this.prevIndicator.show();
36657 if(this.indicator == 1){
36658 this.prevIndicator.hide();
36661 this.nextIndicator.show();
36663 if(this.indicator == this.files.length){
36664 this.nextIndicator.hide();
36667 this.thumbEl.scrollTo('top');
36669 this.fireEvent('update', this);
36672 onClick : function(e)
36674 e.preventDefault();
36676 this.fireEvent('click', this);
36681 e.preventDefault();
36683 this.indicator = Math.max(1, this.indicator - 1);
36690 e.preventDefault();
36692 this.indicator = Math.min(this.files.length, this.indicator + 1);
36706 * @class Roo.bootstrap.RadioSet
36707 * @extends Roo.bootstrap.Input
36708 * Bootstrap RadioSet class
36709 * @cfg {String} indicatorpos (left|right) default left
36710 * @cfg {Boolean} inline (true|false) inline the element (default true)
36711 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36713 * Create a new RadioSet
36714 * @param {Object} config The config object
36717 Roo.bootstrap.RadioSet = function(config){
36719 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36723 Roo.bootstrap.RadioSet.register(this);
36728 * Fires when the element is checked or unchecked.
36729 * @param {Roo.bootstrap.RadioSet} this This radio
36730 * @param {Roo.bootstrap.Radio} item The checked item
36735 * Fires when the element is click.
36736 * @param {Roo.bootstrap.RadioSet} this This radio set
36737 * @param {Roo.bootstrap.Radio} item The checked item
36738 * @param {Roo.EventObject} e The event object
36745 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36753 indicatorpos : 'left',
36755 getAutoCreate : function()
36759 cls : 'roo-radio-set-label',
36763 html : this.fieldLabel
36767 if (Roo.bootstrap.version == 3) {
36770 if(this.indicatorpos == 'left'){
36773 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36774 tooltip : 'This field is required'
36779 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36780 tooltip : 'This field is required'
36786 cls : 'roo-radio-set-items'
36789 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36791 if (align === 'left' && this.fieldLabel.length) {
36794 cls : "roo-radio-set-right",
36800 if(this.labelWidth > 12){
36801 label.style = "width: " + this.labelWidth + 'px';
36804 if(this.labelWidth < 13 && this.labelmd == 0){
36805 this.labelmd = this.labelWidth;
36808 if(this.labellg > 0){
36809 label.cls += ' col-lg-' + this.labellg;
36810 items.cls += ' col-lg-' + (12 - this.labellg);
36813 if(this.labelmd > 0){
36814 label.cls += ' col-md-' + this.labelmd;
36815 items.cls += ' col-md-' + (12 - this.labelmd);
36818 if(this.labelsm > 0){
36819 label.cls += ' col-sm-' + this.labelsm;
36820 items.cls += ' col-sm-' + (12 - this.labelsm);
36823 if(this.labelxs > 0){
36824 label.cls += ' col-xs-' + this.labelxs;
36825 items.cls += ' col-xs-' + (12 - this.labelxs);
36831 cls : 'roo-radio-set',
36835 cls : 'roo-radio-set-input',
36838 value : this.value ? this.value : ''
36845 if(this.weight.length){
36846 cfg.cls += ' roo-radio-' + this.weight;
36850 cfg.cls += ' roo-radio-set-inline';
36854 ['xs','sm','md','lg'].map(function(size){
36855 if (settings[size]) {
36856 cfg.cls += ' col-' + size + '-' + settings[size];
36864 initEvents : function()
36866 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36867 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36869 if(!this.fieldLabel.length){
36870 this.labelEl.hide();
36873 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36874 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36876 this.indicator = this.indicatorEl();
36878 if(this.indicator){
36879 this.indicator.addClass('invisible');
36882 this.originalValue = this.getValue();
36886 inputEl: function ()
36888 return this.el.select('.roo-radio-set-input', true).first();
36891 getChildContainer : function()
36893 return this.itemsEl;
36896 register : function(item)
36898 this.radioes.push(item);
36902 validate : function()
36904 if(this.getVisibilityEl().hasClass('hidden')){
36910 Roo.each(this.radioes, function(i){
36919 if(this.allowBlank) {
36923 if(this.disabled || valid){
36928 this.markInvalid();
36933 markValid : function()
36935 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36936 this.indicatorEl().removeClass('visible');
36937 this.indicatorEl().addClass('invisible');
36941 if (Roo.bootstrap.version == 3) {
36942 this.el.removeClass([this.invalidClass, this.validClass]);
36943 this.el.addClass(this.validClass);
36945 this.el.removeClass(['is-invalid','is-valid']);
36946 this.el.addClass(['is-valid']);
36948 this.fireEvent('valid', this);
36951 markInvalid : function(msg)
36953 if(this.allowBlank || this.disabled){
36957 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36958 this.indicatorEl().removeClass('invisible');
36959 this.indicatorEl().addClass('visible');
36961 if (Roo.bootstrap.version == 3) {
36962 this.el.removeClass([this.invalidClass, this.validClass]);
36963 this.el.addClass(this.invalidClass);
36965 this.el.removeClass(['is-invalid','is-valid']);
36966 this.el.addClass(['is-invalid']);
36969 this.fireEvent('invalid', this, msg);
36973 setValue : function(v, suppressEvent)
36975 if(this.value === v){
36982 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36985 Roo.each(this.radioes, function(i){
36987 i.el.removeClass('checked');
36990 Roo.each(this.radioes, function(i){
36992 if(i.value === v || i.value.toString() === v.toString()){
36994 i.el.addClass('checked');
36996 if(suppressEvent !== true){
36997 this.fireEvent('check', this, i);
37008 clearInvalid : function(){
37010 if(!this.el || this.preventMark){
37014 this.el.removeClass([this.invalidClass]);
37016 this.fireEvent('valid', this);
37021 Roo.apply(Roo.bootstrap.RadioSet, {
37025 register : function(set)
37027 this.groups[set.name] = set;
37030 get: function(name)
37032 if (typeof(this.groups[name]) == 'undefined') {
37036 return this.groups[name] ;
37042 * Ext JS Library 1.1.1
37043 * Copyright(c) 2006-2007, Ext JS, LLC.
37045 * Originally Released Under LGPL - original licence link has changed is not relivant.
37048 * <script type="text/javascript">
37053 * @class Roo.bootstrap.SplitBar
37054 * @extends Roo.util.Observable
37055 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37059 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37060 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37061 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37062 split.minSize = 100;
37063 split.maxSize = 600;
37064 split.animate = true;
37065 split.on('moved', splitterMoved);
37068 * Create a new SplitBar
37069 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37070 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37071 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37072 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37073 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37074 position of the SplitBar).
37076 Roo.bootstrap.SplitBar = function(cfg){
37081 // dragElement : elm
37082 // resizingElement: el,
37084 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37085 // placement : Roo.bootstrap.SplitBar.LEFT ,
37086 // existingProxy ???
37089 this.el = Roo.get(cfg.dragElement, true);
37090 this.el.dom.unselectable = "on";
37092 this.resizingEl = Roo.get(cfg.resizingElement, true);
37096 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37097 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37100 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37103 * The minimum size of the resizing element. (Defaults to 0)
37109 * The maximum size of the resizing element. (Defaults to 2000)
37112 this.maxSize = 2000;
37115 * Whether to animate the transition to the new size
37118 this.animate = false;
37121 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37124 this.useShim = false;
37129 if(!cfg.existingProxy){
37131 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37133 this.proxy = Roo.get(cfg.existingProxy).dom;
37136 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37139 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37142 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37145 this.dragSpecs = {};
37148 * @private The adapter to use to positon and resize elements
37150 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37151 this.adapter.init(this);
37153 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37155 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37156 this.el.addClass("roo-splitbar-h");
37159 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37160 this.el.addClass("roo-splitbar-v");
37166 * Fires when the splitter is moved (alias for {@link #event-moved})
37167 * @param {Roo.bootstrap.SplitBar} this
37168 * @param {Number} newSize the new width or height
37173 * Fires when the splitter is moved
37174 * @param {Roo.bootstrap.SplitBar} this
37175 * @param {Number} newSize the new width or height
37179 * @event beforeresize
37180 * Fires before the splitter is dragged
37181 * @param {Roo.bootstrap.SplitBar} this
37183 "beforeresize" : true,
37185 "beforeapply" : true
37188 Roo.util.Observable.call(this);
37191 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37192 onStartProxyDrag : function(x, y){
37193 this.fireEvent("beforeresize", this);
37195 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37197 o.enableDisplayMode("block");
37198 // all splitbars share the same overlay
37199 Roo.bootstrap.SplitBar.prototype.overlay = o;
37201 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37202 this.overlay.show();
37203 Roo.get(this.proxy).setDisplayed("block");
37204 var size = this.adapter.getElementSize(this);
37205 this.activeMinSize = this.getMinimumSize();;
37206 this.activeMaxSize = this.getMaximumSize();;
37207 var c1 = size - this.activeMinSize;
37208 var c2 = Math.max(this.activeMaxSize - size, 0);
37209 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37210 this.dd.resetConstraints();
37211 this.dd.setXConstraint(
37212 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37213 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37215 this.dd.setYConstraint(0, 0);
37217 this.dd.resetConstraints();
37218 this.dd.setXConstraint(0, 0);
37219 this.dd.setYConstraint(
37220 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37221 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37224 this.dragSpecs.startSize = size;
37225 this.dragSpecs.startPoint = [x, y];
37226 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37230 * @private Called after the drag operation by the DDProxy
37232 onEndProxyDrag : function(e){
37233 Roo.get(this.proxy).setDisplayed(false);
37234 var endPoint = Roo.lib.Event.getXY(e);
37236 this.overlay.hide();
37239 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37240 newSize = this.dragSpecs.startSize +
37241 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37242 endPoint[0] - this.dragSpecs.startPoint[0] :
37243 this.dragSpecs.startPoint[0] - endPoint[0]
37246 newSize = this.dragSpecs.startSize +
37247 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37248 endPoint[1] - this.dragSpecs.startPoint[1] :
37249 this.dragSpecs.startPoint[1] - endPoint[1]
37252 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37253 if(newSize != this.dragSpecs.startSize){
37254 if(this.fireEvent('beforeapply', this, newSize) !== false){
37255 this.adapter.setElementSize(this, newSize);
37256 this.fireEvent("moved", this, newSize);
37257 this.fireEvent("resize", this, newSize);
37263 * Get the adapter this SplitBar uses
37264 * @return The adapter object
37266 getAdapter : function(){
37267 return this.adapter;
37271 * Set the adapter this SplitBar uses
37272 * @param {Object} adapter A SplitBar adapter object
37274 setAdapter : function(adapter){
37275 this.adapter = adapter;
37276 this.adapter.init(this);
37280 * Gets the minimum size for the resizing element
37281 * @return {Number} The minimum size
37283 getMinimumSize : function(){
37284 return this.minSize;
37288 * Sets the minimum size for the resizing element
37289 * @param {Number} minSize The minimum size
37291 setMinimumSize : function(minSize){
37292 this.minSize = minSize;
37296 * Gets the maximum size for the resizing element
37297 * @return {Number} The maximum size
37299 getMaximumSize : function(){
37300 return this.maxSize;
37304 * Sets the maximum size for the resizing element
37305 * @param {Number} maxSize The maximum size
37307 setMaximumSize : function(maxSize){
37308 this.maxSize = maxSize;
37312 * Sets the initialize size for the resizing element
37313 * @param {Number} size The initial size
37315 setCurrentSize : function(size){
37316 var oldAnimate = this.animate;
37317 this.animate = false;
37318 this.adapter.setElementSize(this, size);
37319 this.animate = oldAnimate;
37323 * Destroy this splitbar.
37324 * @param {Boolean} removeEl True to remove the element
37326 destroy : function(removeEl){
37328 this.shim.remove();
37331 this.proxy.parentNode.removeChild(this.proxy);
37339 * @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.
37341 Roo.bootstrap.SplitBar.createProxy = function(dir){
37342 var proxy = new Roo.Element(document.createElement("div"));
37343 proxy.unselectable();
37344 var cls = 'roo-splitbar-proxy';
37345 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37346 document.body.appendChild(proxy.dom);
37351 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37352 * Default Adapter. It assumes the splitter and resizing element are not positioned
37353 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37355 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37358 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37359 // do nothing for now
37360 init : function(s){
37364 * Called before drag operations to get the current size of the resizing element.
37365 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37367 getElementSize : function(s){
37368 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37369 return s.resizingEl.getWidth();
37371 return s.resizingEl.getHeight();
37376 * Called after drag operations to set the size of the resizing element.
37377 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37378 * @param {Number} newSize The new size to set
37379 * @param {Function} onComplete A function to be invoked when resizing is complete
37381 setElementSize : function(s, newSize, onComplete){
37382 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37384 s.resizingEl.setWidth(newSize);
37386 onComplete(s, newSize);
37389 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37394 s.resizingEl.setHeight(newSize);
37396 onComplete(s, newSize);
37399 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37406 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37407 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37408 * Adapter that moves the splitter element to align with the resized sizing element.
37409 * Used with an absolute positioned SplitBar.
37410 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37411 * document.body, make sure you assign an id to the body element.
37413 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37414 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37415 this.container = Roo.get(container);
37418 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37419 init : function(s){
37420 this.basic.init(s);
37423 getElementSize : function(s){
37424 return this.basic.getElementSize(s);
37427 setElementSize : function(s, newSize, onComplete){
37428 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37431 moveSplitter : function(s){
37432 var yes = Roo.bootstrap.SplitBar;
37433 switch(s.placement){
37435 s.el.setX(s.resizingEl.getRight());
37438 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37441 s.el.setY(s.resizingEl.getBottom());
37444 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37451 * Orientation constant - Create a vertical SplitBar
37455 Roo.bootstrap.SplitBar.VERTICAL = 1;
37458 * Orientation constant - Create a horizontal SplitBar
37462 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37465 * Placement constant - The resizing element is to the left of the splitter element
37469 Roo.bootstrap.SplitBar.LEFT = 1;
37472 * Placement constant - The resizing element is to the right of the splitter element
37476 Roo.bootstrap.SplitBar.RIGHT = 2;
37479 * Placement constant - The resizing element is positioned above the splitter element
37483 Roo.bootstrap.SplitBar.TOP = 3;
37486 * Placement constant - The resizing element is positioned under splitter element
37490 Roo.bootstrap.SplitBar.BOTTOM = 4;
37491 Roo.namespace("Roo.bootstrap.layout");/*
37493 * Ext JS Library 1.1.1
37494 * Copyright(c) 2006-2007, Ext JS, LLC.
37496 * Originally Released Under LGPL - original licence link has changed is not relivant.
37499 * <script type="text/javascript">
37503 * @class Roo.bootstrap.layout.Manager
37504 * @extends Roo.bootstrap.Component
37505 * Base class for layout managers.
37507 Roo.bootstrap.layout.Manager = function(config)
37509 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37515 /** false to disable window resize monitoring @type Boolean */
37516 this.monitorWindowResize = true;
37521 * Fires when a layout is performed.
37522 * @param {Roo.LayoutManager} this
37526 * @event regionresized
37527 * Fires when the user resizes a region.
37528 * @param {Roo.LayoutRegion} region The resized region
37529 * @param {Number} newSize The new size (width for east/west, height for north/south)
37531 "regionresized" : true,
37533 * @event regioncollapsed
37534 * Fires when a region is collapsed.
37535 * @param {Roo.LayoutRegion} region The collapsed region
37537 "regioncollapsed" : true,
37539 * @event regionexpanded
37540 * Fires when a region is expanded.
37541 * @param {Roo.LayoutRegion} region The expanded region
37543 "regionexpanded" : true
37545 this.updating = false;
37548 this.el = Roo.get(config.el);
37554 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37559 monitorWindowResize : true,
37565 onRender : function(ct, position)
37568 this.el = Roo.get(ct);
37571 //this.fireEvent('render',this);
37575 initEvents: function()
37579 // ie scrollbar fix
37580 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37581 document.body.scroll = "no";
37582 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37583 this.el.position('relative');
37585 this.id = this.el.id;
37586 this.el.addClass("roo-layout-container");
37587 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37588 if(this.el.dom != document.body ) {
37589 this.el.on('resize', this.layout,this);
37590 this.el.on('show', this.layout,this);
37596 * Returns true if this layout is currently being updated
37597 * @return {Boolean}
37599 isUpdating : function(){
37600 return this.updating;
37604 * Suspend the LayoutManager from doing auto-layouts while
37605 * making multiple add or remove calls
37607 beginUpdate : function(){
37608 this.updating = true;
37612 * Restore auto-layouts and optionally disable the manager from performing a layout
37613 * @param {Boolean} noLayout true to disable a layout update
37615 endUpdate : function(noLayout){
37616 this.updating = false;
37622 layout: function(){
37626 onRegionResized : function(region, newSize){
37627 this.fireEvent("regionresized", region, newSize);
37631 onRegionCollapsed : function(region){
37632 this.fireEvent("regioncollapsed", region);
37635 onRegionExpanded : function(region){
37636 this.fireEvent("regionexpanded", region);
37640 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37641 * performs box-model adjustments.
37642 * @return {Object} The size as an object {width: (the width), height: (the height)}
37644 getViewSize : function()
37647 if(this.el.dom != document.body){
37648 size = this.el.getSize();
37650 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37652 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37653 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37658 * Returns the Element this layout is bound to.
37659 * @return {Roo.Element}
37661 getEl : function(){
37666 * Returns the specified region.
37667 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37668 * @return {Roo.LayoutRegion}
37670 getRegion : function(target){
37671 return this.regions[target.toLowerCase()];
37674 onWindowResize : function(){
37675 if(this.monitorWindowResize){
37682 * Ext JS Library 1.1.1
37683 * Copyright(c) 2006-2007, Ext JS, LLC.
37685 * Originally Released Under LGPL - original licence link has changed is not relivant.
37688 * <script type="text/javascript">
37691 * @class Roo.bootstrap.layout.Border
37692 * @extends Roo.bootstrap.layout.Manager
37693 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37694 * please see: examples/bootstrap/nested.html<br><br>
37696 <b>The container the layout is rendered into can be either the body element or any other element.
37697 If it is not the body element, the container needs to either be an absolute positioned element,
37698 or you will need to add "position:relative" to the css of the container. You will also need to specify
37699 the container size if it is not the body element.</b>
37702 * Create a new Border
37703 * @param {Object} config Configuration options
37705 Roo.bootstrap.layout.Border = function(config){
37706 config = config || {};
37707 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37711 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37712 if(config[region]){
37713 config[region].region = region;
37714 this.addRegion(config[region]);
37720 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37722 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37724 parent : false, // this might point to a 'nest' or a ???
37727 * Creates and adds a new region if it doesn't already exist.
37728 * @param {String} target The target region key (north, south, east, west or center).
37729 * @param {Object} config The regions config object
37730 * @return {BorderLayoutRegion} The new region
37732 addRegion : function(config)
37734 if(!this.regions[config.region]){
37735 var r = this.factory(config);
37736 this.bindRegion(r);
37738 return this.regions[config.region];
37742 bindRegion : function(r){
37743 this.regions[r.config.region] = r;
37745 r.on("visibilitychange", this.layout, this);
37746 r.on("paneladded", this.layout, this);
37747 r.on("panelremoved", this.layout, this);
37748 r.on("invalidated", this.layout, this);
37749 r.on("resized", this.onRegionResized, this);
37750 r.on("collapsed", this.onRegionCollapsed, this);
37751 r.on("expanded", this.onRegionExpanded, this);
37755 * Performs a layout update.
37757 layout : function()
37759 if(this.updating) {
37763 // render all the rebions if they have not been done alreayd?
37764 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37765 if(this.regions[region] && !this.regions[region].bodyEl){
37766 this.regions[region].onRender(this.el)
37770 var size = this.getViewSize();
37771 var w = size.width;
37772 var h = size.height;
37777 //var x = 0, y = 0;
37779 var rs = this.regions;
37780 var north = rs["north"];
37781 var south = rs["south"];
37782 var west = rs["west"];
37783 var east = rs["east"];
37784 var center = rs["center"];
37785 //if(this.hideOnLayout){ // not supported anymore
37786 //c.el.setStyle("display", "none");
37788 if(north && north.isVisible()){
37789 var b = north.getBox();
37790 var m = north.getMargins();
37791 b.width = w - (m.left+m.right);
37794 centerY = b.height + b.y + m.bottom;
37795 centerH -= centerY;
37796 north.updateBox(this.safeBox(b));
37798 if(south && south.isVisible()){
37799 var b = south.getBox();
37800 var m = south.getMargins();
37801 b.width = w - (m.left+m.right);
37803 var totalHeight = (b.height + m.top + m.bottom);
37804 b.y = h - totalHeight + m.top;
37805 centerH -= totalHeight;
37806 south.updateBox(this.safeBox(b));
37808 if(west && west.isVisible()){
37809 var b = west.getBox();
37810 var m = west.getMargins();
37811 b.height = centerH - (m.top+m.bottom);
37813 b.y = centerY + m.top;
37814 var totalWidth = (b.width + m.left + m.right);
37815 centerX += totalWidth;
37816 centerW -= totalWidth;
37817 west.updateBox(this.safeBox(b));
37819 if(east && east.isVisible()){
37820 var b = east.getBox();
37821 var m = east.getMargins();
37822 b.height = centerH - (m.top+m.bottom);
37823 var totalWidth = (b.width + m.left + m.right);
37824 b.x = w - totalWidth + m.left;
37825 b.y = centerY + m.top;
37826 centerW -= totalWidth;
37827 east.updateBox(this.safeBox(b));
37830 var m = center.getMargins();
37832 x: centerX + m.left,
37833 y: centerY + m.top,
37834 width: centerW - (m.left+m.right),
37835 height: centerH - (m.top+m.bottom)
37837 //if(this.hideOnLayout){
37838 //center.el.setStyle("display", "block");
37840 center.updateBox(this.safeBox(centerBox));
37843 this.fireEvent("layout", this);
37847 safeBox : function(box){
37848 box.width = Math.max(0, box.width);
37849 box.height = Math.max(0, box.height);
37854 * Adds a ContentPanel (or subclass) to this layout.
37855 * @param {String} target The target region key (north, south, east, west or center).
37856 * @param {Roo.ContentPanel} panel The panel to add
37857 * @return {Roo.ContentPanel} The added panel
37859 add : function(target, panel){
37861 target = target.toLowerCase();
37862 return this.regions[target].add(panel);
37866 * Remove a ContentPanel (or subclass) to this layout.
37867 * @param {String} target The target region key (north, south, east, west or center).
37868 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37869 * @return {Roo.ContentPanel} The removed panel
37871 remove : function(target, panel){
37872 target = target.toLowerCase();
37873 return this.regions[target].remove(panel);
37877 * Searches all regions for a panel with the specified id
37878 * @param {String} panelId
37879 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37881 findPanel : function(panelId){
37882 var rs = this.regions;
37883 for(var target in rs){
37884 if(typeof rs[target] != "function"){
37885 var p = rs[target].getPanel(panelId);
37895 * Searches all regions for a panel with the specified id and activates (shows) it.
37896 * @param {String/ContentPanel} panelId The panels id or the panel itself
37897 * @return {Roo.ContentPanel} The shown panel or null
37899 showPanel : function(panelId) {
37900 var rs = this.regions;
37901 for(var target in rs){
37902 var r = rs[target];
37903 if(typeof r != "function"){
37904 if(r.hasPanel(panelId)){
37905 return r.showPanel(panelId);
37913 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37914 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37917 restoreState : function(provider){
37919 provider = Roo.state.Manager;
37921 var sm = new Roo.LayoutStateManager();
37922 sm.init(this, provider);
37928 * Adds a xtype elements to the layout.
37932 xtype : 'ContentPanel',
37939 xtype : 'NestedLayoutPanel',
37945 items : [ ... list of content panels or nested layout panels.. ]
37949 * @param {Object} cfg Xtype definition of item to add.
37951 addxtype : function(cfg)
37953 // basically accepts a pannel...
37954 // can accept a layout region..!?!?
37955 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37958 // theory? children can only be panels??
37960 //if (!cfg.xtype.match(/Panel$/)) {
37965 if (typeof(cfg.region) == 'undefined') {
37966 Roo.log("Failed to add Panel, region was not set");
37970 var region = cfg.region;
37976 xitems = cfg.items;
37981 if ( region == 'center') {
37982 Roo.log("Center: " + cfg.title);
37988 case 'Content': // ContentPanel (el, cfg)
37989 case 'Scroll': // ContentPanel (el, cfg)
37991 cfg.autoCreate = cfg.autoCreate || true;
37992 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37994 // var el = this.el.createChild();
37995 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37998 this.add(region, ret);
38002 case 'TreePanel': // our new panel!
38003 cfg.el = this.el.createChild();
38004 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38005 this.add(region, ret);
38010 // create a new Layout (which is a Border Layout...
38012 var clayout = cfg.layout;
38013 clayout.el = this.el.createChild();
38014 clayout.items = clayout.items || [];
38018 // replace this exitems with the clayout ones..
38019 xitems = clayout.items;
38021 // force background off if it's in center...
38022 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38023 cfg.background = false;
38025 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38028 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38029 //console.log('adding nested layout panel ' + cfg.toSource());
38030 this.add(region, ret);
38031 nb = {}; /// find first...
38036 // needs grid and region
38038 //var el = this.getRegion(region).el.createChild();
38040 *var el = this.el.createChild();
38041 // create the grid first...
38042 cfg.grid.container = el;
38043 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38046 if (region == 'center' && this.active ) {
38047 cfg.background = false;
38050 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38052 this.add(region, ret);
38054 if (cfg.background) {
38055 // render grid on panel activation (if panel background)
38056 ret.on('activate', function(gp) {
38057 if (!gp.grid.rendered) {
38058 // gp.grid.render(el);
38062 // cfg.grid.render(el);
38068 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38069 // it was the old xcomponent building that caused this before.
38070 // espeically if border is the top element in the tree.
38080 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38082 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38083 this.add(region, ret);
38087 throw "Can not add '" + cfg.xtype + "' to Border";
38093 this.beginUpdate();
38097 Roo.each(xitems, function(i) {
38098 region = nb && i.region ? i.region : false;
38100 var add = ret.addxtype(i);
38103 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38104 if (!i.background) {
38105 abn[region] = nb[region] ;
38112 // make the last non-background panel active..
38113 //if (nb) { Roo.log(abn); }
38116 for(var r in abn) {
38117 region = this.getRegion(r);
38119 // tried using nb[r], but it does not work..
38121 region.showPanel(abn[r]);
38132 factory : function(cfg)
38135 var validRegions = Roo.bootstrap.layout.Border.regions;
38137 var target = cfg.region;
38140 var r = Roo.bootstrap.layout;
38144 return new r.North(cfg);
38146 return new r.South(cfg);
38148 return new r.East(cfg);
38150 return new r.West(cfg);
38152 return new r.Center(cfg);
38154 throw 'Layout region "'+target+'" not supported.';
38161 * Ext JS Library 1.1.1
38162 * Copyright(c) 2006-2007, Ext JS, LLC.
38164 * Originally Released Under LGPL - original licence link has changed is not relivant.
38167 * <script type="text/javascript">
38171 * @class Roo.bootstrap.layout.Basic
38172 * @extends Roo.util.Observable
38173 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38174 * and does not have a titlebar, tabs or any other features. All it does is size and position
38175 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38176 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38177 * @cfg {string} region the region that it inhabits..
38178 * @cfg {bool} skipConfig skip config?
38182 Roo.bootstrap.layout.Basic = function(config){
38184 this.mgr = config.mgr;
38186 this.position = config.region;
38188 var skipConfig = config.skipConfig;
38192 * @scope Roo.BasicLayoutRegion
38196 * @event beforeremove
38197 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38198 * @param {Roo.LayoutRegion} this
38199 * @param {Roo.ContentPanel} panel The panel
38200 * @param {Object} e The cancel event object
38202 "beforeremove" : true,
38204 * @event invalidated
38205 * Fires when the layout for this region is changed.
38206 * @param {Roo.LayoutRegion} this
38208 "invalidated" : true,
38210 * @event visibilitychange
38211 * Fires when this region is shown or hidden
38212 * @param {Roo.LayoutRegion} this
38213 * @param {Boolean} visibility true or false
38215 "visibilitychange" : true,
38217 * @event paneladded
38218 * Fires when a panel is added.
38219 * @param {Roo.LayoutRegion} this
38220 * @param {Roo.ContentPanel} panel The panel
38222 "paneladded" : true,
38224 * @event panelremoved
38225 * Fires when a panel is removed.
38226 * @param {Roo.LayoutRegion} this
38227 * @param {Roo.ContentPanel} panel The panel
38229 "panelremoved" : true,
38231 * @event beforecollapse
38232 * Fires when this region before collapse.
38233 * @param {Roo.LayoutRegion} this
38235 "beforecollapse" : true,
38238 * Fires when this region is collapsed.
38239 * @param {Roo.LayoutRegion} this
38241 "collapsed" : true,
38244 * Fires when this region is expanded.
38245 * @param {Roo.LayoutRegion} this
38250 * Fires when this region is slid into view.
38251 * @param {Roo.LayoutRegion} this
38253 "slideshow" : true,
38256 * Fires when this region slides out of view.
38257 * @param {Roo.LayoutRegion} this
38259 "slidehide" : true,
38261 * @event panelactivated
38262 * Fires when a panel is activated.
38263 * @param {Roo.LayoutRegion} this
38264 * @param {Roo.ContentPanel} panel The activated panel
38266 "panelactivated" : true,
38269 * Fires when the user resizes this region.
38270 * @param {Roo.LayoutRegion} this
38271 * @param {Number} newSize The new size (width for east/west, height for north/south)
38275 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38276 this.panels = new Roo.util.MixedCollection();
38277 this.panels.getKey = this.getPanelId.createDelegate(this);
38279 this.activePanel = null;
38280 // ensure listeners are added...
38282 if (config.listeners || config.events) {
38283 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38284 listeners : config.listeners || {},
38285 events : config.events || {}
38289 if(skipConfig !== true){
38290 this.applyConfig(config);
38294 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38296 getPanelId : function(p){
38300 applyConfig : function(config){
38301 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38302 this.config = config;
38307 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38308 * the width, for horizontal (north, south) the height.
38309 * @param {Number} newSize The new width or height
38311 resizeTo : function(newSize){
38312 var el = this.el ? this.el :
38313 (this.activePanel ? this.activePanel.getEl() : null);
38315 switch(this.position){
38318 el.setWidth(newSize);
38319 this.fireEvent("resized", this, newSize);
38323 el.setHeight(newSize);
38324 this.fireEvent("resized", this, newSize);
38330 getBox : function(){
38331 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38334 getMargins : function(){
38335 return this.margins;
38338 updateBox : function(box){
38340 var el = this.activePanel.getEl();
38341 el.dom.style.left = box.x + "px";
38342 el.dom.style.top = box.y + "px";
38343 this.activePanel.setSize(box.width, box.height);
38347 * Returns the container element for this region.
38348 * @return {Roo.Element}
38350 getEl : function(){
38351 return this.activePanel;
38355 * Returns true if this region is currently visible.
38356 * @return {Boolean}
38358 isVisible : function(){
38359 return this.activePanel ? true : false;
38362 setActivePanel : function(panel){
38363 panel = this.getPanel(panel);
38364 if(this.activePanel && this.activePanel != panel){
38365 this.activePanel.setActiveState(false);
38366 this.activePanel.getEl().setLeftTop(-10000,-10000);
38368 this.activePanel = panel;
38369 panel.setActiveState(true);
38371 panel.setSize(this.box.width, this.box.height);
38373 this.fireEvent("panelactivated", this, panel);
38374 this.fireEvent("invalidated");
38378 * Show the specified panel.
38379 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38380 * @return {Roo.ContentPanel} The shown panel or null
38382 showPanel : function(panel){
38383 panel = this.getPanel(panel);
38385 this.setActivePanel(panel);
38391 * Get the active panel for this region.
38392 * @return {Roo.ContentPanel} The active panel or null
38394 getActivePanel : function(){
38395 return this.activePanel;
38399 * Add the passed ContentPanel(s)
38400 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38401 * @return {Roo.ContentPanel} The panel added (if only one was added)
38403 add : function(panel){
38404 if(arguments.length > 1){
38405 for(var i = 0, len = arguments.length; i < len; i++) {
38406 this.add(arguments[i]);
38410 if(this.hasPanel(panel)){
38411 this.showPanel(panel);
38414 var el = panel.getEl();
38415 if(el.dom.parentNode != this.mgr.el.dom){
38416 this.mgr.el.dom.appendChild(el.dom);
38418 if(panel.setRegion){
38419 panel.setRegion(this);
38421 this.panels.add(panel);
38422 el.setStyle("position", "absolute");
38423 if(!panel.background){
38424 this.setActivePanel(panel);
38425 if(this.config.initialSize && this.panels.getCount()==1){
38426 this.resizeTo(this.config.initialSize);
38429 this.fireEvent("paneladded", this, panel);
38434 * Returns true if the panel is in this region.
38435 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38436 * @return {Boolean}
38438 hasPanel : function(panel){
38439 if(typeof panel == "object"){ // must be panel obj
38440 panel = panel.getId();
38442 return this.getPanel(panel) ? true : false;
38446 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38447 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38448 * @param {Boolean} preservePanel Overrides the config preservePanel option
38449 * @return {Roo.ContentPanel} The panel that was removed
38451 remove : function(panel, preservePanel){
38452 panel = this.getPanel(panel);
38457 this.fireEvent("beforeremove", this, panel, e);
38458 if(e.cancel === true){
38461 var panelId = panel.getId();
38462 this.panels.removeKey(panelId);
38467 * Returns the panel specified or null if it's not in this region.
38468 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38469 * @return {Roo.ContentPanel}
38471 getPanel : function(id){
38472 if(typeof id == "object"){ // must be panel obj
38475 return this.panels.get(id);
38479 * Returns this regions position (north/south/east/west/center).
38482 getPosition: function(){
38483 return this.position;
38487 * Ext JS Library 1.1.1
38488 * Copyright(c) 2006-2007, Ext JS, LLC.
38490 * Originally Released Under LGPL - original licence link has changed is not relivant.
38493 * <script type="text/javascript">
38497 * @class Roo.bootstrap.layout.Region
38498 * @extends Roo.bootstrap.layout.Basic
38499 * This class represents a region in a layout manager.
38501 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38502 * @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})
38503 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38504 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38505 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38506 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38507 * @cfg {String} title The title for the region (overrides panel titles)
38508 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38509 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38510 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38511 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38512 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38513 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38514 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38515 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38516 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38517 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38519 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38520 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38521 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38522 * @cfg {Number} width For East/West panels
38523 * @cfg {Number} height For North/South panels
38524 * @cfg {Boolean} split To show the splitter
38525 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38527 * @cfg {string} cls Extra CSS classes to add to region
38529 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38530 * @cfg {string} region the region that it inhabits..
38533 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38534 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38536 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38537 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38538 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38540 Roo.bootstrap.layout.Region = function(config)
38542 this.applyConfig(config);
38544 var mgr = config.mgr;
38545 var pos = config.region;
38546 config.skipConfig = true;
38547 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38550 this.onRender(mgr.el);
38553 this.visible = true;
38554 this.collapsed = false;
38555 this.unrendered_panels = [];
38558 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38560 position: '', // set by wrapper (eg. north/south etc..)
38561 unrendered_panels : null, // unrendered panels.
38563 tabPosition : false,
38565 mgr: false, // points to 'Border'
38568 createBody : function(){
38569 /** This region's body element
38570 * @type Roo.Element */
38571 this.bodyEl = this.el.createChild({
38573 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38577 onRender: function(ctr, pos)
38579 var dh = Roo.DomHelper;
38580 /** This region's container element
38581 * @type Roo.Element */
38582 this.el = dh.append(ctr.dom, {
38584 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38586 /** This region's title element
38587 * @type Roo.Element */
38589 this.titleEl = dh.append(this.el.dom, {
38591 unselectable: "on",
38592 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38594 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38595 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38599 this.titleEl.enableDisplayMode();
38600 /** This region's title text element
38601 * @type HTMLElement */
38602 this.titleTextEl = this.titleEl.dom.firstChild;
38603 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38605 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38606 this.closeBtn.enableDisplayMode();
38607 this.closeBtn.on("click", this.closeClicked, this);
38608 this.closeBtn.hide();
38610 this.createBody(this.config);
38611 if(this.config.hideWhenEmpty){
38613 this.on("paneladded", this.validateVisibility, this);
38614 this.on("panelremoved", this.validateVisibility, this);
38616 if(this.autoScroll){
38617 this.bodyEl.setStyle("overflow", "auto");
38619 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38621 //if(c.titlebar !== false){
38622 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38623 this.titleEl.hide();
38625 this.titleEl.show();
38626 if(this.config.title){
38627 this.titleTextEl.innerHTML = this.config.title;
38631 if(this.config.collapsed){
38632 this.collapse(true);
38634 if(this.config.hidden){
38638 if (this.unrendered_panels && this.unrendered_panels.length) {
38639 for (var i =0;i< this.unrendered_panels.length; i++) {
38640 this.add(this.unrendered_panels[i]);
38642 this.unrendered_panels = null;
38648 applyConfig : function(c)
38651 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38652 var dh = Roo.DomHelper;
38653 if(c.titlebar !== false){
38654 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38655 this.collapseBtn.on("click", this.collapse, this);
38656 this.collapseBtn.enableDisplayMode();
38658 if(c.showPin === true || this.showPin){
38659 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38660 this.stickBtn.enableDisplayMode();
38661 this.stickBtn.on("click", this.expand, this);
38662 this.stickBtn.hide();
38667 /** This region's collapsed element
38668 * @type Roo.Element */
38671 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38672 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38675 if(c.floatable !== false){
38676 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38677 this.collapsedEl.on("click", this.collapseClick, this);
38680 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38681 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38682 id: "message", unselectable: "on", style:{"float":"left"}});
38683 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38685 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38686 this.expandBtn.on("click", this.expand, this);
38690 if(this.collapseBtn){
38691 this.collapseBtn.setVisible(c.collapsible == true);
38694 this.cmargins = c.cmargins || this.cmargins ||
38695 (this.position == "west" || this.position == "east" ?
38696 {top: 0, left: 2, right:2, bottom: 0} :
38697 {top: 2, left: 0, right:0, bottom: 2});
38699 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38702 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38704 this.autoScroll = c.autoScroll || false;
38709 this.duration = c.duration || .30;
38710 this.slideDuration = c.slideDuration || .45;
38715 * Returns true if this region is currently visible.
38716 * @return {Boolean}
38718 isVisible : function(){
38719 return this.visible;
38723 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38724 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38726 //setCollapsedTitle : function(title){
38727 // title = title || " ";
38728 // if(this.collapsedTitleTextEl){
38729 // this.collapsedTitleTextEl.innerHTML = title;
38733 getBox : function(){
38735 // if(!this.collapsed){
38736 b = this.el.getBox(false, true);
38738 // b = this.collapsedEl.getBox(false, true);
38743 getMargins : function(){
38744 return this.margins;
38745 //return this.collapsed ? this.cmargins : this.margins;
38748 highlight : function(){
38749 this.el.addClass("x-layout-panel-dragover");
38752 unhighlight : function(){
38753 this.el.removeClass("x-layout-panel-dragover");
38756 updateBox : function(box)
38758 if (!this.bodyEl) {
38759 return; // not rendered yet..
38763 if(!this.collapsed){
38764 this.el.dom.style.left = box.x + "px";
38765 this.el.dom.style.top = box.y + "px";
38766 this.updateBody(box.width, box.height);
38768 this.collapsedEl.dom.style.left = box.x + "px";
38769 this.collapsedEl.dom.style.top = box.y + "px";
38770 this.collapsedEl.setSize(box.width, box.height);
38773 this.tabs.autoSizeTabs();
38777 updateBody : function(w, h)
38780 this.el.setWidth(w);
38781 w -= this.el.getBorderWidth("rl");
38782 if(this.config.adjustments){
38783 w += this.config.adjustments[0];
38786 if(h !== null && h > 0){
38787 this.el.setHeight(h);
38788 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38789 h -= this.el.getBorderWidth("tb");
38790 if(this.config.adjustments){
38791 h += this.config.adjustments[1];
38793 this.bodyEl.setHeight(h);
38795 h = this.tabs.syncHeight(h);
38798 if(this.panelSize){
38799 w = w !== null ? w : this.panelSize.width;
38800 h = h !== null ? h : this.panelSize.height;
38802 if(this.activePanel){
38803 var el = this.activePanel.getEl();
38804 w = w !== null ? w : el.getWidth();
38805 h = h !== null ? h : el.getHeight();
38806 this.panelSize = {width: w, height: h};
38807 this.activePanel.setSize(w, h);
38809 if(Roo.isIE && this.tabs){
38810 this.tabs.el.repaint();
38815 * Returns the container element for this region.
38816 * @return {Roo.Element}
38818 getEl : function(){
38823 * Hides this region.
38826 //if(!this.collapsed){
38827 this.el.dom.style.left = "-2000px";
38830 // this.collapsedEl.dom.style.left = "-2000px";
38831 // this.collapsedEl.hide();
38833 this.visible = false;
38834 this.fireEvent("visibilitychange", this, false);
38838 * Shows this region if it was previously hidden.
38841 //if(!this.collapsed){
38844 // this.collapsedEl.show();
38846 this.visible = true;
38847 this.fireEvent("visibilitychange", this, true);
38850 closeClicked : function(){
38851 if(this.activePanel){
38852 this.remove(this.activePanel);
38856 collapseClick : function(e){
38858 e.stopPropagation();
38861 e.stopPropagation();
38867 * Collapses this region.
38868 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38871 collapse : function(skipAnim, skipCheck = false){
38872 if(this.collapsed) {
38876 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38878 this.collapsed = true;
38880 this.split.el.hide();
38882 if(this.config.animate && skipAnim !== true){
38883 this.fireEvent("invalidated", this);
38884 this.animateCollapse();
38886 this.el.setLocation(-20000,-20000);
38888 this.collapsedEl.show();
38889 this.fireEvent("collapsed", this);
38890 this.fireEvent("invalidated", this);
38896 animateCollapse : function(){
38901 * Expands this region if it was previously collapsed.
38902 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38903 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38906 expand : function(e, skipAnim){
38908 e.stopPropagation();
38910 if(!this.collapsed || this.el.hasActiveFx()) {
38914 this.afterSlideIn();
38917 this.collapsed = false;
38918 if(this.config.animate && skipAnim !== true){
38919 this.animateExpand();
38923 this.split.el.show();
38925 this.collapsedEl.setLocation(-2000,-2000);
38926 this.collapsedEl.hide();
38927 this.fireEvent("invalidated", this);
38928 this.fireEvent("expanded", this);
38932 animateExpand : function(){
38936 initTabs : function()
38938 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38940 var ts = new Roo.bootstrap.panel.Tabs({
38941 el: this.bodyEl.dom,
38943 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38944 disableTooltips: this.config.disableTabTips,
38945 toolbar : this.config.toolbar
38948 if(this.config.hideTabs){
38949 ts.stripWrap.setDisplayed(false);
38952 ts.resizeTabs = this.config.resizeTabs === true;
38953 ts.minTabWidth = this.config.minTabWidth || 40;
38954 ts.maxTabWidth = this.config.maxTabWidth || 250;
38955 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38956 ts.monitorResize = false;
38957 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38958 ts.bodyEl.addClass('roo-layout-tabs-body');
38959 this.panels.each(this.initPanelAsTab, this);
38962 initPanelAsTab : function(panel){
38963 var ti = this.tabs.addTab(
38967 this.config.closeOnTab && panel.isClosable(),
38970 if(panel.tabTip !== undefined){
38971 ti.setTooltip(panel.tabTip);
38973 ti.on("activate", function(){
38974 this.setActivePanel(panel);
38977 if(this.config.closeOnTab){
38978 ti.on("beforeclose", function(t, e){
38980 this.remove(panel);
38984 panel.tabItem = ti;
38989 updatePanelTitle : function(panel, title)
38991 if(this.activePanel == panel){
38992 this.updateTitle(title);
38995 var ti = this.tabs.getTab(panel.getEl().id);
38997 if(panel.tabTip !== undefined){
38998 ti.setTooltip(panel.tabTip);
39003 updateTitle : function(title){
39004 if(this.titleTextEl && !this.config.title){
39005 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39009 setActivePanel : function(panel)
39011 panel = this.getPanel(panel);
39012 if(this.activePanel && this.activePanel != panel){
39013 if(this.activePanel.setActiveState(false) === false){
39017 this.activePanel = panel;
39018 panel.setActiveState(true);
39019 if(this.panelSize){
39020 panel.setSize(this.panelSize.width, this.panelSize.height);
39023 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39025 this.updateTitle(panel.getTitle());
39027 this.fireEvent("invalidated", this);
39029 this.fireEvent("panelactivated", this, panel);
39033 * Shows the specified panel.
39034 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39035 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39037 showPanel : function(panel)
39039 panel = this.getPanel(panel);
39042 var tab = this.tabs.getTab(panel.getEl().id);
39043 if(tab.isHidden()){
39044 this.tabs.unhideTab(tab.id);
39048 this.setActivePanel(panel);
39055 * Get the active panel for this region.
39056 * @return {Roo.ContentPanel} The active panel or null
39058 getActivePanel : function(){
39059 return this.activePanel;
39062 validateVisibility : function(){
39063 if(this.panels.getCount() < 1){
39064 this.updateTitle(" ");
39065 this.closeBtn.hide();
39068 if(!this.isVisible()){
39075 * Adds the passed ContentPanel(s) to this region.
39076 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39077 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39079 add : function(panel)
39081 if(arguments.length > 1){
39082 for(var i = 0, len = arguments.length; i < len; i++) {
39083 this.add(arguments[i]);
39088 // if we have not been rendered yet, then we can not really do much of this..
39089 if (!this.bodyEl) {
39090 this.unrendered_panels.push(panel);
39097 if(this.hasPanel(panel)){
39098 this.showPanel(panel);
39101 panel.setRegion(this);
39102 this.panels.add(panel);
39103 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39104 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39105 // and hide them... ???
39106 this.bodyEl.dom.appendChild(panel.getEl().dom);
39107 if(panel.background !== true){
39108 this.setActivePanel(panel);
39110 this.fireEvent("paneladded", this, panel);
39117 this.initPanelAsTab(panel);
39121 if(panel.background !== true){
39122 this.tabs.activate(panel.getEl().id);
39124 this.fireEvent("paneladded", this, panel);
39129 * Hides the tab for the specified panel.
39130 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39132 hidePanel : function(panel){
39133 if(this.tabs && (panel = this.getPanel(panel))){
39134 this.tabs.hideTab(panel.getEl().id);
39139 * Unhides the tab for a previously hidden panel.
39140 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39142 unhidePanel : function(panel){
39143 if(this.tabs && (panel = this.getPanel(panel))){
39144 this.tabs.unhideTab(panel.getEl().id);
39148 clearPanels : function(){
39149 while(this.panels.getCount() > 0){
39150 this.remove(this.panels.first());
39155 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39156 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39157 * @param {Boolean} preservePanel Overrides the config preservePanel option
39158 * @return {Roo.ContentPanel} The panel that was removed
39160 remove : function(panel, preservePanel)
39162 panel = this.getPanel(panel);
39167 this.fireEvent("beforeremove", this, panel, e);
39168 if(e.cancel === true){
39171 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39172 var panelId = panel.getId();
39173 this.panels.removeKey(panelId);
39175 document.body.appendChild(panel.getEl().dom);
39178 this.tabs.removeTab(panel.getEl().id);
39179 }else if (!preservePanel){
39180 this.bodyEl.dom.removeChild(panel.getEl().dom);
39182 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39183 var p = this.panels.first();
39184 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39185 tempEl.appendChild(p.getEl().dom);
39186 this.bodyEl.update("");
39187 this.bodyEl.dom.appendChild(p.getEl().dom);
39189 this.updateTitle(p.getTitle());
39191 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39192 this.setActivePanel(p);
39194 panel.setRegion(null);
39195 if(this.activePanel == panel){
39196 this.activePanel = null;
39198 if(this.config.autoDestroy !== false && preservePanel !== true){
39199 try{panel.destroy();}catch(e){}
39201 this.fireEvent("panelremoved", this, panel);
39206 * Returns the TabPanel component used by this region
39207 * @return {Roo.TabPanel}
39209 getTabs : function(){
39213 createTool : function(parentEl, className){
39214 var btn = Roo.DomHelper.append(parentEl, {
39216 cls: "x-layout-tools-button",
39219 cls: "roo-layout-tools-button-inner " + className,
39223 btn.addClassOnOver("roo-layout-tools-button-over");
39228 * Ext JS Library 1.1.1
39229 * Copyright(c) 2006-2007, Ext JS, LLC.
39231 * Originally Released Under LGPL - original licence link has changed is not relivant.
39234 * <script type="text/javascript">
39240 * @class Roo.SplitLayoutRegion
39241 * @extends Roo.LayoutRegion
39242 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39244 Roo.bootstrap.layout.Split = function(config){
39245 this.cursor = config.cursor;
39246 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39249 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39251 splitTip : "Drag to resize.",
39252 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39253 useSplitTips : false,
39255 applyConfig : function(config){
39256 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39259 onRender : function(ctr,pos) {
39261 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39262 if(!this.config.split){
39267 var splitEl = Roo.DomHelper.append(ctr.dom, {
39269 id: this.el.id + "-split",
39270 cls: "roo-layout-split roo-layout-split-"+this.position,
39273 /** The SplitBar for this region
39274 * @type Roo.SplitBar */
39275 // does not exist yet...
39276 Roo.log([this.position, this.orientation]);
39278 this.split = new Roo.bootstrap.SplitBar({
39279 dragElement : splitEl,
39280 resizingElement: this.el,
39281 orientation : this.orientation
39284 this.split.on("moved", this.onSplitMove, this);
39285 this.split.useShim = this.config.useShim === true;
39286 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39287 if(this.useSplitTips){
39288 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39290 //if(config.collapsible){
39291 // this.split.el.on("dblclick", this.collapse, this);
39294 if(typeof this.config.minSize != "undefined"){
39295 this.split.minSize = this.config.minSize;
39297 if(typeof this.config.maxSize != "undefined"){
39298 this.split.maxSize = this.config.maxSize;
39300 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39301 this.hideSplitter();
39306 getHMaxSize : function(){
39307 var cmax = this.config.maxSize || 10000;
39308 var center = this.mgr.getRegion("center");
39309 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39312 getVMaxSize : function(){
39313 var cmax = this.config.maxSize || 10000;
39314 var center = this.mgr.getRegion("center");
39315 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39318 onSplitMove : function(split, newSize){
39319 this.fireEvent("resized", this, newSize);
39323 * Returns the {@link Roo.SplitBar} for this region.
39324 * @return {Roo.SplitBar}
39326 getSplitBar : function(){
39331 this.hideSplitter();
39332 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39335 hideSplitter : function(){
39337 this.split.el.setLocation(-2000,-2000);
39338 this.split.el.hide();
39344 this.split.el.show();
39346 Roo.bootstrap.layout.Split.superclass.show.call(this);
39349 beforeSlide: function(){
39350 if(Roo.isGecko){// firefox overflow auto bug workaround
39351 this.bodyEl.clip();
39353 this.tabs.bodyEl.clip();
39355 if(this.activePanel){
39356 this.activePanel.getEl().clip();
39358 if(this.activePanel.beforeSlide){
39359 this.activePanel.beforeSlide();
39365 afterSlide : function(){
39366 if(Roo.isGecko){// firefox overflow auto bug workaround
39367 this.bodyEl.unclip();
39369 this.tabs.bodyEl.unclip();
39371 if(this.activePanel){
39372 this.activePanel.getEl().unclip();
39373 if(this.activePanel.afterSlide){
39374 this.activePanel.afterSlide();
39380 initAutoHide : function(){
39381 if(this.autoHide !== false){
39382 if(!this.autoHideHd){
39383 var st = new Roo.util.DelayedTask(this.slideIn, this);
39384 this.autoHideHd = {
39385 "mouseout": function(e){
39386 if(!e.within(this.el, true)){
39390 "mouseover" : function(e){
39396 this.el.on(this.autoHideHd);
39400 clearAutoHide : function(){
39401 if(this.autoHide !== false){
39402 this.el.un("mouseout", this.autoHideHd.mouseout);
39403 this.el.un("mouseover", this.autoHideHd.mouseover);
39407 clearMonitor : function(){
39408 Roo.get(document).un("click", this.slideInIf, this);
39411 // these names are backwards but not changed for compat
39412 slideOut : function(){
39413 if(this.isSlid || this.el.hasActiveFx()){
39416 this.isSlid = true;
39417 if(this.collapseBtn){
39418 this.collapseBtn.hide();
39420 this.closeBtnState = this.closeBtn.getStyle('display');
39421 this.closeBtn.hide();
39423 this.stickBtn.show();
39426 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39427 this.beforeSlide();
39428 this.el.setStyle("z-index", 10001);
39429 this.el.slideIn(this.getSlideAnchor(), {
39430 callback: function(){
39432 this.initAutoHide();
39433 Roo.get(document).on("click", this.slideInIf, this);
39434 this.fireEvent("slideshow", this);
39441 afterSlideIn : function(){
39442 this.clearAutoHide();
39443 this.isSlid = false;
39444 this.clearMonitor();
39445 this.el.setStyle("z-index", "");
39446 if(this.collapseBtn){
39447 this.collapseBtn.show();
39449 this.closeBtn.setStyle('display', this.closeBtnState);
39451 this.stickBtn.hide();
39453 this.fireEvent("slidehide", this);
39456 slideIn : function(cb){
39457 if(!this.isSlid || this.el.hasActiveFx()){
39461 this.isSlid = false;
39462 this.beforeSlide();
39463 this.el.slideOut(this.getSlideAnchor(), {
39464 callback: function(){
39465 this.el.setLeftTop(-10000, -10000);
39467 this.afterSlideIn();
39475 slideInIf : function(e){
39476 if(!e.within(this.el)){
39481 animateCollapse : function(){
39482 this.beforeSlide();
39483 this.el.setStyle("z-index", 20000);
39484 var anchor = this.getSlideAnchor();
39485 this.el.slideOut(anchor, {
39486 callback : function(){
39487 this.el.setStyle("z-index", "");
39488 this.collapsedEl.slideIn(anchor, {duration:.3});
39490 this.el.setLocation(-10000,-10000);
39492 this.fireEvent("collapsed", this);
39499 animateExpand : function(){
39500 this.beforeSlide();
39501 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39502 this.el.setStyle("z-index", 20000);
39503 this.collapsedEl.hide({
39506 this.el.slideIn(this.getSlideAnchor(), {
39507 callback : function(){
39508 this.el.setStyle("z-index", "");
39511 this.split.el.show();
39513 this.fireEvent("invalidated", this);
39514 this.fireEvent("expanded", this);
39542 getAnchor : function(){
39543 return this.anchors[this.position];
39546 getCollapseAnchor : function(){
39547 return this.canchors[this.position];
39550 getSlideAnchor : function(){
39551 return this.sanchors[this.position];
39554 getAlignAdj : function(){
39555 var cm = this.cmargins;
39556 switch(this.position){
39572 getExpandAdj : function(){
39573 var c = this.collapsedEl, cm = this.cmargins;
39574 switch(this.position){
39576 return [-(cm.right+c.getWidth()+cm.left), 0];
39579 return [cm.right+c.getWidth()+cm.left, 0];
39582 return [0, -(cm.top+cm.bottom+c.getHeight())];
39585 return [0, cm.top+cm.bottom+c.getHeight()];
39591 * Ext JS Library 1.1.1
39592 * Copyright(c) 2006-2007, Ext JS, LLC.
39594 * Originally Released Under LGPL - original licence link has changed is not relivant.
39597 * <script type="text/javascript">
39600 * These classes are private internal classes
39602 Roo.bootstrap.layout.Center = function(config){
39603 config.region = "center";
39604 Roo.bootstrap.layout.Region.call(this, config);
39605 this.visible = true;
39606 this.minWidth = config.minWidth || 20;
39607 this.minHeight = config.minHeight || 20;
39610 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39612 // center panel can't be hidden
39616 // center panel can't be hidden
39619 getMinWidth: function(){
39620 return this.minWidth;
39623 getMinHeight: function(){
39624 return this.minHeight;
39638 Roo.bootstrap.layout.North = function(config)
39640 config.region = 'north';
39641 config.cursor = 'n-resize';
39643 Roo.bootstrap.layout.Split.call(this, config);
39647 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39648 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39649 this.split.el.addClass("roo-layout-split-v");
39651 //var size = config.initialSize || config.height;
39652 //if(this.el && typeof size != "undefined"){
39653 // this.el.setHeight(size);
39656 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39658 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39661 onRender : function(ctr, pos)
39663 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39664 var size = this.config.initialSize || this.config.height;
39665 if(this.el && typeof size != "undefined"){
39666 this.el.setHeight(size);
39671 getBox : function(){
39672 if(this.collapsed){
39673 return this.collapsedEl.getBox();
39675 var box = this.el.getBox();
39677 box.height += this.split.el.getHeight();
39682 updateBox : function(box){
39683 if(this.split && !this.collapsed){
39684 box.height -= this.split.el.getHeight();
39685 this.split.el.setLeft(box.x);
39686 this.split.el.setTop(box.y+box.height);
39687 this.split.el.setWidth(box.width);
39689 if(this.collapsed){
39690 this.updateBody(box.width, null);
39692 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39700 Roo.bootstrap.layout.South = function(config){
39701 config.region = 'south';
39702 config.cursor = 's-resize';
39703 Roo.bootstrap.layout.Split.call(this, config);
39705 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39706 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39707 this.split.el.addClass("roo-layout-split-v");
39712 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39713 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39715 onRender : function(ctr, pos)
39717 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39718 var size = this.config.initialSize || this.config.height;
39719 if(this.el && typeof size != "undefined"){
39720 this.el.setHeight(size);
39725 getBox : function(){
39726 if(this.collapsed){
39727 return this.collapsedEl.getBox();
39729 var box = this.el.getBox();
39731 var sh = this.split.el.getHeight();
39738 updateBox : function(box){
39739 if(this.split && !this.collapsed){
39740 var sh = this.split.el.getHeight();
39743 this.split.el.setLeft(box.x);
39744 this.split.el.setTop(box.y-sh);
39745 this.split.el.setWidth(box.width);
39747 if(this.collapsed){
39748 this.updateBody(box.width, null);
39750 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39754 Roo.bootstrap.layout.East = function(config){
39755 config.region = "east";
39756 config.cursor = "e-resize";
39757 Roo.bootstrap.layout.Split.call(this, config);
39759 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39760 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39761 this.split.el.addClass("roo-layout-split-h");
39765 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39766 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39768 onRender : function(ctr, pos)
39770 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39771 var size = this.config.initialSize || this.config.width;
39772 if(this.el && typeof size != "undefined"){
39773 this.el.setWidth(size);
39778 getBox : function(){
39779 if(this.collapsed){
39780 return this.collapsedEl.getBox();
39782 var box = this.el.getBox();
39784 var sw = this.split.el.getWidth();
39791 updateBox : function(box){
39792 if(this.split && !this.collapsed){
39793 var sw = this.split.el.getWidth();
39795 this.split.el.setLeft(box.x);
39796 this.split.el.setTop(box.y);
39797 this.split.el.setHeight(box.height);
39800 if(this.collapsed){
39801 this.updateBody(null, box.height);
39803 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39807 Roo.bootstrap.layout.West = function(config){
39808 config.region = "west";
39809 config.cursor = "w-resize";
39811 Roo.bootstrap.layout.Split.call(this, config);
39813 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39814 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39815 this.split.el.addClass("roo-layout-split-h");
39819 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39820 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39822 onRender: function(ctr, pos)
39824 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39825 var size = this.config.initialSize || this.config.width;
39826 if(typeof size != "undefined"){
39827 this.el.setWidth(size);
39831 getBox : function(){
39832 if(this.collapsed){
39833 return this.collapsedEl.getBox();
39835 var box = this.el.getBox();
39836 if (box.width == 0) {
39837 box.width = this.config.width; // kludge?
39840 box.width += this.split.el.getWidth();
39845 updateBox : function(box){
39846 if(this.split && !this.collapsed){
39847 var sw = this.split.el.getWidth();
39849 this.split.el.setLeft(box.x+box.width);
39850 this.split.el.setTop(box.y);
39851 this.split.el.setHeight(box.height);
39853 if(this.collapsed){
39854 this.updateBody(null, box.height);
39856 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39858 });Roo.namespace("Roo.bootstrap.panel");/*
39860 * Ext JS Library 1.1.1
39861 * Copyright(c) 2006-2007, Ext JS, LLC.
39863 * Originally Released Under LGPL - original licence link has changed is not relivant.
39866 * <script type="text/javascript">
39869 * @class Roo.ContentPanel
39870 * @extends Roo.util.Observable
39871 * A basic ContentPanel element.
39872 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39873 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39874 * @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
39875 * @cfg {Boolean} closable True if the panel can be closed/removed
39876 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39877 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39878 * @cfg {Toolbar} toolbar A toolbar for this panel
39879 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39880 * @cfg {String} title The title for this panel
39881 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39882 * @cfg {String} url Calls {@link #setUrl} with this value
39883 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39884 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39885 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39886 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39887 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39888 * @cfg {Boolean} badges render the badges
39889 * @cfg {String} cls extra classes to use
39890 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39893 * Create a new ContentPanel.
39894 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39895 * @param {String/Object} config A string to set only the title or a config object
39896 * @param {String} content (optional) Set the HTML content for this panel
39897 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39899 Roo.bootstrap.panel.Content = function( config){
39901 this.tpl = config.tpl || false;
39903 var el = config.el;
39904 var content = config.content;
39906 if(config.autoCreate){ // xtype is available if this is called from factory
39909 this.el = Roo.get(el);
39910 if(!this.el && config && config.autoCreate){
39911 if(typeof config.autoCreate == "object"){
39912 if(!config.autoCreate.id){
39913 config.autoCreate.id = config.id||el;
39915 this.el = Roo.DomHelper.append(document.body,
39916 config.autoCreate, true);
39920 cls: (config.cls || '') +
39921 (config.background ? ' bg-' + config.background : '') +
39922 " roo-layout-inactive-content",
39925 if (config.iframe) {
39929 style : 'border: 0px',
39930 src : 'about:blank'
39936 elcfg.html = config.html;
39940 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39941 if (config.iframe) {
39942 this.iframeEl = this.el.select('iframe',true).first();
39947 this.closable = false;
39948 this.loaded = false;
39949 this.active = false;
39952 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39954 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39956 this.wrapEl = this.el; //this.el.wrap();
39958 if (config.toolbar.items) {
39959 ti = config.toolbar.items ;
39960 delete config.toolbar.items ;
39964 this.toolbar.render(this.wrapEl, 'before');
39965 for(var i =0;i < ti.length;i++) {
39966 // Roo.log(['add child', items[i]]);
39967 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39969 this.toolbar.items = nitems;
39970 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39971 delete config.toolbar;
39975 // xtype created footer. - not sure if will work as we normally have to render first..
39976 if (this.footer && !this.footer.el && this.footer.xtype) {
39977 if (!this.wrapEl) {
39978 this.wrapEl = this.el.wrap();
39981 this.footer.container = this.wrapEl.createChild();
39983 this.footer = Roo.factory(this.footer, Roo);
39988 if(typeof config == "string"){
39989 this.title = config;
39991 Roo.apply(this, config);
39995 this.resizeEl = Roo.get(this.resizeEl, true);
39997 this.resizeEl = this.el;
39999 // handle view.xtype
40007 * Fires when this panel is activated.
40008 * @param {Roo.ContentPanel} this
40012 * @event deactivate
40013 * Fires when this panel is activated.
40014 * @param {Roo.ContentPanel} this
40016 "deactivate" : true,
40020 * Fires when this panel is resized if fitToFrame is true.
40021 * @param {Roo.ContentPanel} this
40022 * @param {Number} width The width after any component adjustments
40023 * @param {Number} height The height after any component adjustments
40029 * Fires when this tab is created
40030 * @param {Roo.ContentPanel} this
40041 if(this.autoScroll && !this.iframe){
40042 this.resizeEl.setStyle("overflow", "auto");
40044 // fix randome scrolling
40045 //this.el.on('scroll', function() {
40046 // Roo.log('fix random scolling');
40047 // this.scrollTo('top',0);
40050 content = content || this.content;
40052 this.setContent(content);
40054 if(config && config.url){
40055 this.setUrl(this.url, this.params, this.loadOnce);
40060 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40062 if (this.view && typeof(this.view.xtype) != 'undefined') {
40063 this.view.el = this.el.appendChild(document.createElement("div"));
40064 this.view = Roo.factory(this.view);
40065 this.view.render && this.view.render(false, '');
40069 this.fireEvent('render', this);
40072 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40082 setRegion : function(region){
40083 this.region = region;
40084 this.setActiveClass(region && !this.background);
40088 setActiveClass: function(state)
40091 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40092 this.el.setStyle('position','relative');
40094 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40095 this.el.setStyle('position', 'absolute');
40100 * Returns the toolbar for this Panel if one was configured.
40101 * @return {Roo.Toolbar}
40103 getToolbar : function(){
40104 return this.toolbar;
40107 setActiveState : function(active)
40109 this.active = active;
40110 this.setActiveClass(active);
40112 if(this.fireEvent("deactivate", this) === false){
40117 this.fireEvent("activate", this);
40121 * Updates this panel's element (not for iframe)
40122 * @param {String} content The new content
40123 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40125 setContent : function(content, loadScripts){
40130 this.el.update(content, loadScripts);
40133 ignoreResize : function(w, h){
40134 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40137 this.lastSize = {width: w, height: h};
40142 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40143 * @return {Roo.UpdateManager} The UpdateManager
40145 getUpdateManager : function(){
40149 return this.el.getUpdateManager();
40152 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40153 * Does not work with IFRAME contents
40154 * @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:
40157 url: "your-url.php",
40158 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40159 callback: yourFunction,
40160 scope: yourObject, //(optional scope)
40163 text: "Loading...",
40169 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40170 * 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.
40171 * @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}
40172 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40173 * @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.
40174 * @return {Roo.ContentPanel} this
40182 var um = this.el.getUpdateManager();
40183 um.update.apply(um, arguments);
40189 * 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.
40190 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40191 * @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)
40192 * @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)
40193 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40195 setUrl : function(url, params, loadOnce){
40197 this.iframeEl.dom.src = url;
40201 if(this.refreshDelegate){
40202 this.removeListener("activate", this.refreshDelegate);
40204 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40205 this.on("activate", this.refreshDelegate);
40206 return this.el.getUpdateManager();
40209 _handleRefresh : function(url, params, loadOnce){
40210 if(!loadOnce || !this.loaded){
40211 var updater = this.el.getUpdateManager();
40212 updater.update(url, params, this._setLoaded.createDelegate(this));
40216 _setLoaded : function(){
40217 this.loaded = true;
40221 * Returns this panel's id
40224 getId : function(){
40229 * Returns this panel's element - used by regiosn to add.
40230 * @return {Roo.Element}
40232 getEl : function(){
40233 return this.wrapEl || this.el;
40238 adjustForComponents : function(width, height)
40240 //Roo.log('adjustForComponents ');
40241 if(this.resizeEl != this.el){
40242 width -= this.el.getFrameWidth('lr');
40243 height -= this.el.getFrameWidth('tb');
40246 var te = this.toolbar.getEl();
40247 te.setWidth(width);
40248 height -= te.getHeight();
40251 var te = this.footer.getEl();
40252 te.setWidth(width);
40253 height -= te.getHeight();
40257 if(this.adjustments){
40258 width += this.adjustments[0];
40259 height += this.adjustments[1];
40261 return {"width": width, "height": height};
40264 setSize : function(width, height){
40265 if(this.fitToFrame && !this.ignoreResize(width, height)){
40266 if(this.fitContainer && this.resizeEl != this.el){
40267 this.el.setSize(width, height);
40269 var size = this.adjustForComponents(width, height);
40271 this.iframeEl.setSize(width,height);
40274 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40275 this.fireEvent('resize', this, size.width, size.height);
40282 * Returns this panel's title
40285 getTitle : function(){
40287 if (typeof(this.title) != 'object') {
40292 for (var k in this.title) {
40293 if (!this.title.hasOwnProperty(k)) {
40297 if (k.indexOf('-') >= 0) {
40298 var s = k.split('-');
40299 for (var i = 0; i<s.length; i++) {
40300 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40303 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40310 * Set this panel's title
40311 * @param {String} title
40313 setTitle : function(title){
40314 this.title = title;
40316 this.region.updatePanelTitle(this, title);
40321 * Returns true is this panel was configured to be closable
40322 * @return {Boolean}
40324 isClosable : function(){
40325 return this.closable;
40328 beforeSlide : function(){
40330 this.resizeEl.clip();
40333 afterSlide : function(){
40335 this.resizeEl.unclip();
40339 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40340 * Will fail silently if the {@link #setUrl} method has not been called.
40341 * This does not activate the panel, just updates its content.
40343 refresh : function(){
40344 if(this.refreshDelegate){
40345 this.loaded = false;
40346 this.refreshDelegate();
40351 * Destroys this panel
40353 destroy : function(){
40354 this.el.removeAllListeners();
40355 var tempEl = document.createElement("span");
40356 tempEl.appendChild(this.el.dom);
40357 tempEl.innerHTML = "";
40363 * form - if the content panel contains a form - this is a reference to it.
40364 * @type {Roo.form.Form}
40368 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40369 * This contains a reference to it.
40375 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40385 * @param {Object} cfg Xtype definition of item to add.
40389 getChildContainer: function () {
40390 return this.getEl();
40395 var ret = new Roo.factory(cfg);
40400 if (cfg.xtype.match(/^Form$/)) {
40403 //if (this.footer) {
40404 // el = this.footer.container.insertSibling(false, 'before');
40406 el = this.el.createChild();
40409 this.form = new Roo.form.Form(cfg);
40412 if ( this.form.allItems.length) {
40413 this.form.render(el.dom);
40417 // should only have one of theses..
40418 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40419 // views.. should not be just added - used named prop 'view''
40421 cfg.el = this.el.appendChild(document.createElement("div"));
40424 var ret = new Roo.factory(cfg);
40426 ret.render && ret.render(false, ''); // render blank..
40436 * @class Roo.bootstrap.panel.Grid
40437 * @extends Roo.bootstrap.panel.Content
40439 * Create a new GridPanel.
40440 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40441 * @param {Object} config A the config object
40447 Roo.bootstrap.panel.Grid = function(config)
40451 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40452 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40454 config.el = this.wrapper;
40455 //this.el = this.wrapper;
40457 if (config.container) {
40458 // ctor'ed from a Border/panel.grid
40461 this.wrapper.setStyle("overflow", "hidden");
40462 this.wrapper.addClass('roo-grid-container');
40467 if(config.toolbar){
40468 var tool_el = this.wrapper.createChild();
40469 this.toolbar = Roo.factory(config.toolbar);
40471 if (config.toolbar.items) {
40472 ti = config.toolbar.items ;
40473 delete config.toolbar.items ;
40477 this.toolbar.render(tool_el);
40478 for(var i =0;i < ti.length;i++) {
40479 // Roo.log(['add child', items[i]]);
40480 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40482 this.toolbar.items = nitems;
40484 delete config.toolbar;
40487 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40488 config.grid.scrollBody = true;;
40489 config.grid.monitorWindowResize = false; // turn off autosizing
40490 config.grid.autoHeight = false;
40491 config.grid.autoWidth = false;
40493 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40495 if (config.background) {
40496 // render grid on panel activation (if panel background)
40497 this.on('activate', function(gp) {
40498 if (!gp.grid.rendered) {
40499 gp.grid.render(this.wrapper);
40500 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40505 this.grid.render(this.wrapper);
40506 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40509 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40510 // ??? needed ??? config.el = this.wrapper;
40515 // xtype created footer. - not sure if will work as we normally have to render first..
40516 if (this.footer && !this.footer.el && this.footer.xtype) {
40518 var ctr = this.grid.getView().getFooterPanel(true);
40519 this.footer.dataSource = this.grid.dataSource;
40520 this.footer = Roo.factory(this.footer, Roo);
40521 this.footer.render(ctr);
40531 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40532 getId : function(){
40533 return this.grid.id;
40537 * Returns the grid for this panel
40538 * @return {Roo.bootstrap.Table}
40540 getGrid : function(){
40544 setSize : function(width, height){
40545 if(!this.ignoreResize(width, height)){
40546 var grid = this.grid;
40547 var size = this.adjustForComponents(width, height);
40548 // tfoot is not a footer?
40551 var gridel = grid.getGridEl();
40552 gridel.setSize(size.width, size.height);
40554 var tbd = grid.getGridEl().select('tbody', true).first();
40555 var thd = grid.getGridEl().select('thead',true).first();
40556 var tbf= grid.getGridEl().select('tfoot', true).first();
40559 size.height -= tbf.getHeight();
40562 size.height -= thd.getHeight();
40565 tbd.setSize(size.width, size.height );
40566 // this is for the account management tab -seems to work there.
40567 var thd = grid.getGridEl().select('thead',true).first();
40569 // tbd.setSize(size.width, size.height - thd.getHeight());
40578 beforeSlide : function(){
40579 this.grid.getView().scroller.clip();
40582 afterSlide : function(){
40583 this.grid.getView().scroller.unclip();
40586 destroy : function(){
40587 this.grid.destroy();
40589 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40594 * @class Roo.bootstrap.panel.Nest
40595 * @extends Roo.bootstrap.panel.Content
40597 * Create a new Panel, that can contain a layout.Border.
40600 * @param {Roo.BorderLayout} layout The layout for this panel
40601 * @param {String/Object} config A string to set only the title or a config object
40603 Roo.bootstrap.panel.Nest = function(config)
40605 // construct with only one argument..
40606 /* FIXME - implement nicer consturctors
40607 if (layout.layout) {
40609 layout = config.layout;
40610 delete config.layout;
40612 if (layout.xtype && !layout.getEl) {
40613 // then layout needs constructing..
40614 layout = Roo.factory(layout, Roo);
40618 config.el = config.layout.getEl();
40620 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40622 config.layout.monitorWindowResize = false; // turn off autosizing
40623 this.layout = config.layout;
40624 this.layout.getEl().addClass("roo-layout-nested-layout");
40625 this.layout.parent = this;
40632 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40634 setSize : function(width, height){
40635 if(!this.ignoreResize(width, height)){
40636 var size = this.adjustForComponents(width, height);
40637 var el = this.layout.getEl();
40638 if (size.height < 1) {
40639 el.setWidth(size.width);
40641 el.setSize(size.width, size.height);
40643 var touch = el.dom.offsetWidth;
40644 this.layout.layout();
40645 // ie requires a double layout on the first pass
40646 if(Roo.isIE && !this.initialized){
40647 this.initialized = true;
40648 this.layout.layout();
40653 // activate all subpanels if not currently active..
40655 setActiveState : function(active){
40656 this.active = active;
40657 this.setActiveClass(active);
40660 this.fireEvent("deactivate", this);
40664 this.fireEvent("activate", this);
40665 // not sure if this should happen before or after..
40666 if (!this.layout) {
40667 return; // should not happen..
40670 for (var r in this.layout.regions) {
40671 reg = this.layout.getRegion(r);
40672 if (reg.getActivePanel()) {
40673 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40674 reg.setActivePanel(reg.getActivePanel());
40677 if (!reg.panels.length) {
40680 reg.showPanel(reg.getPanel(0));
40689 * Returns the nested BorderLayout for this panel
40690 * @return {Roo.BorderLayout}
40692 getLayout : function(){
40693 return this.layout;
40697 * Adds a xtype elements to the layout of the nested panel
40701 xtype : 'ContentPanel',
40708 xtype : 'NestedLayoutPanel',
40714 items : [ ... list of content panels or nested layout panels.. ]
40718 * @param {Object} cfg Xtype definition of item to add.
40720 addxtype : function(cfg) {
40721 return this.layout.addxtype(cfg);
40726 * Ext JS Library 1.1.1
40727 * Copyright(c) 2006-2007, Ext JS, LLC.
40729 * Originally Released Under LGPL - original licence link has changed is not relivant.
40732 * <script type="text/javascript">
40735 * @class Roo.TabPanel
40736 * @extends Roo.util.Observable
40737 * A lightweight tab container.
40741 // basic tabs 1, built from existing content
40742 var tabs = new Roo.TabPanel("tabs1");
40743 tabs.addTab("script", "View Script");
40744 tabs.addTab("markup", "View Markup");
40745 tabs.activate("script");
40747 // more advanced tabs, built from javascript
40748 var jtabs = new Roo.TabPanel("jtabs");
40749 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40751 // set up the UpdateManager
40752 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40753 var updater = tab2.getUpdateManager();
40754 updater.setDefaultUrl("ajax1.htm");
40755 tab2.on('activate', updater.refresh, updater, true);
40757 // Use setUrl for Ajax loading
40758 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40759 tab3.setUrl("ajax2.htm", null, true);
40762 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40765 jtabs.activate("jtabs-1");
40768 * Create a new TabPanel.
40769 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40770 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40772 Roo.bootstrap.panel.Tabs = function(config){
40774 * The container element for this TabPanel.
40775 * @type Roo.Element
40777 this.el = Roo.get(config.el);
40780 if(typeof config == "boolean"){
40781 this.tabPosition = config ? "bottom" : "top";
40783 Roo.apply(this, config);
40787 if(this.tabPosition == "bottom"){
40788 // if tabs are at the bottom = create the body first.
40789 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40790 this.el.addClass("roo-tabs-bottom");
40792 // next create the tabs holders
40794 if (this.tabPosition == "west"){
40796 var reg = this.region; // fake it..
40798 if (!reg.mgr.parent) {
40801 reg = reg.mgr.parent.region;
40803 Roo.log("got nest?");
40805 if (reg.mgr.getRegion('west')) {
40806 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40807 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40808 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40809 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40810 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40818 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40819 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40820 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40821 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40826 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40829 // finally - if tabs are at the top, then create the body last..
40830 if(this.tabPosition != "bottom"){
40831 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40832 * @type Roo.Element
40834 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40835 this.el.addClass("roo-tabs-top");
40839 this.bodyEl.setStyle("position", "relative");
40841 this.active = null;
40842 this.activateDelegate = this.activate.createDelegate(this);
40847 * Fires when the active tab changes
40848 * @param {Roo.TabPanel} this
40849 * @param {Roo.TabPanelItem} activePanel The new active tab
40853 * @event beforetabchange
40854 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40855 * @param {Roo.TabPanel} this
40856 * @param {Object} e Set cancel to true on this object to cancel the tab change
40857 * @param {Roo.TabPanelItem} tab The tab being changed to
40859 "beforetabchange" : true
40862 Roo.EventManager.onWindowResize(this.onResize, this);
40863 this.cpad = this.el.getPadding("lr");
40864 this.hiddenCount = 0;
40867 // toolbar on the tabbar support...
40868 if (this.toolbar) {
40869 alert("no toolbar support yet");
40870 this.toolbar = false;
40872 var tcfg = this.toolbar;
40873 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40874 this.toolbar = new Roo.Toolbar(tcfg);
40875 if (Roo.isSafari) {
40876 var tbl = tcfg.container.child('table', true);
40877 tbl.setAttribute('width', '100%');
40885 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40888 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40890 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40892 tabPosition : "top",
40894 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40896 currentTabWidth : 0,
40898 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40902 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40906 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40908 preferredTabWidth : 175,
40910 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40912 resizeTabs : false,
40914 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40916 monitorResize : true,
40918 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40920 toolbar : false, // set by caller..
40922 region : false, /// set by caller
40924 disableTooltips : true, // not used yet...
40927 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40928 * @param {String} id The id of the div to use <b>or create</b>
40929 * @param {String} text The text for the tab
40930 * @param {String} content (optional) Content to put in the TabPanelItem body
40931 * @param {Boolean} closable (optional) True to create a close icon on the tab
40932 * @return {Roo.TabPanelItem} The created TabPanelItem
40934 addTab : function(id, text, content, closable, tpl)
40936 var item = new Roo.bootstrap.panel.TabItem({
40940 closable : closable,
40943 this.addTabItem(item);
40945 item.setContent(content);
40951 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40952 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40953 * @return {Roo.TabPanelItem}
40955 getTab : function(id){
40956 return this.items[id];
40960 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40961 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40963 hideTab : function(id){
40964 var t = this.items[id];
40967 this.hiddenCount++;
40968 this.autoSizeTabs();
40973 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40974 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40976 unhideTab : function(id){
40977 var t = this.items[id];
40979 t.setHidden(false);
40980 this.hiddenCount--;
40981 this.autoSizeTabs();
40986 * Adds an existing {@link Roo.TabPanelItem}.
40987 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40989 addTabItem : function(item)
40991 this.items[item.id] = item;
40992 this.items.push(item);
40993 this.autoSizeTabs();
40994 // if(this.resizeTabs){
40995 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40996 // this.autoSizeTabs();
40998 // item.autoSize();
41003 * Removes a {@link Roo.TabPanelItem}.
41004 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41006 removeTab : function(id){
41007 var items = this.items;
41008 var tab = items[id];
41009 if(!tab) { return; }
41010 var index = items.indexOf(tab);
41011 if(this.active == tab && items.length > 1){
41012 var newTab = this.getNextAvailable(index);
41017 this.stripEl.dom.removeChild(tab.pnode.dom);
41018 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41019 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41021 items.splice(index, 1);
41022 delete this.items[tab.id];
41023 tab.fireEvent("close", tab);
41024 tab.purgeListeners();
41025 this.autoSizeTabs();
41028 getNextAvailable : function(start){
41029 var items = this.items;
41031 // look for a next tab that will slide over to
41032 // replace the one being removed
41033 while(index < items.length){
41034 var item = items[++index];
41035 if(item && !item.isHidden()){
41039 // if one isn't found select the previous tab (on the left)
41042 var item = items[--index];
41043 if(item && !item.isHidden()){
41051 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41052 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41054 disableTab : function(id){
41055 var tab = this.items[id];
41056 if(tab && this.active != tab){
41062 * Enables a {@link Roo.TabPanelItem} that is disabled.
41063 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41065 enableTab : function(id){
41066 var tab = this.items[id];
41071 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41072 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41073 * @return {Roo.TabPanelItem} The TabPanelItem.
41075 activate : function(id)
41077 //Roo.log('activite:' + id);
41079 var tab = this.items[id];
41083 if(tab == this.active || tab.disabled){
41087 this.fireEvent("beforetabchange", this, e, tab);
41088 if(e.cancel !== true && !tab.disabled){
41090 this.active.hide();
41092 this.active = this.items[id];
41093 this.active.show();
41094 this.fireEvent("tabchange", this, this.active);
41100 * Gets the active {@link Roo.TabPanelItem}.
41101 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41103 getActiveTab : function(){
41104 return this.active;
41108 * Updates the tab body element to fit the height of the container element
41109 * for overflow scrolling
41110 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41112 syncHeight : function(targetHeight){
41113 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41114 var bm = this.bodyEl.getMargins();
41115 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41116 this.bodyEl.setHeight(newHeight);
41120 onResize : function(){
41121 if(this.monitorResize){
41122 this.autoSizeTabs();
41127 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41129 beginUpdate : function(){
41130 this.updating = true;
41134 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41136 endUpdate : function(){
41137 this.updating = false;
41138 this.autoSizeTabs();
41142 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41144 autoSizeTabs : function()
41146 var count = this.items.length;
41147 var vcount = count - this.hiddenCount;
41150 this.stripEl.hide();
41152 this.stripEl.show();
41155 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41160 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41161 var availWidth = Math.floor(w / vcount);
41162 var b = this.stripBody;
41163 if(b.getWidth() > w){
41164 var tabs = this.items;
41165 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41166 if(availWidth < this.minTabWidth){
41167 /*if(!this.sleft){ // incomplete scrolling code
41168 this.createScrollButtons();
41171 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41174 if(this.currentTabWidth < this.preferredTabWidth){
41175 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41181 * Returns the number of tabs in this TabPanel.
41184 getCount : function(){
41185 return this.items.length;
41189 * Resizes all the tabs to the passed width
41190 * @param {Number} The new width
41192 setTabWidth : function(width){
41193 this.currentTabWidth = width;
41194 for(var i = 0, len = this.items.length; i < len; i++) {
41195 if(!this.items[i].isHidden()) {
41196 this.items[i].setWidth(width);
41202 * Destroys this TabPanel
41203 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41205 destroy : function(removeEl){
41206 Roo.EventManager.removeResizeListener(this.onResize, this);
41207 for(var i = 0, len = this.items.length; i < len; i++){
41208 this.items[i].purgeListeners();
41210 if(removeEl === true){
41211 this.el.update("");
41216 createStrip : function(container)
41218 var strip = document.createElement("nav");
41219 strip.className = Roo.bootstrap.version == 4 ?
41220 "navbar-light bg-light" :
41221 "navbar navbar-default"; //"x-tabs-wrap";
41222 container.appendChild(strip);
41226 createStripList : function(strip)
41228 // div wrapper for retard IE
41229 // returns the "tr" element.
41230 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41231 //'<div class="x-tabs-strip-wrap">'+
41232 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41233 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41234 return strip.firstChild; //.firstChild.firstChild.firstChild;
41236 createBody : function(container)
41238 var body = document.createElement("div");
41239 Roo.id(body, "tab-body");
41240 //Roo.fly(body).addClass("x-tabs-body");
41241 Roo.fly(body).addClass("tab-content");
41242 container.appendChild(body);
41245 createItemBody :function(bodyEl, id){
41246 var body = Roo.getDom(id);
41248 body = document.createElement("div");
41251 //Roo.fly(body).addClass("x-tabs-item-body");
41252 Roo.fly(body).addClass("tab-pane");
41253 bodyEl.insertBefore(body, bodyEl.firstChild);
41257 createStripElements : function(stripEl, text, closable, tpl)
41259 var td = document.createElement("li"); // was td..
41260 td.className = 'nav-item';
41262 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41265 stripEl.appendChild(td);
41267 td.className = "x-tabs-closable";
41268 if(!this.closeTpl){
41269 this.closeTpl = new Roo.Template(
41270 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41271 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41272 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41275 var el = this.closeTpl.overwrite(td, {"text": text});
41276 var close = el.getElementsByTagName("div")[0];
41277 var inner = el.getElementsByTagName("em")[0];
41278 return {"el": el, "close": close, "inner": inner};
41281 // not sure what this is..
41282 // if(!this.tabTpl){
41283 //this.tabTpl = new Roo.Template(
41284 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41285 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41287 // this.tabTpl = new Roo.Template(
41288 // '<a href="#">' +
41289 // '<span unselectable="on"' +
41290 // (this.disableTooltips ? '' : ' title="{text}"') +
41291 // ' >{text}</span></a>'
41297 var template = tpl || this.tabTpl || false;
41300 template = new Roo.Template(
41301 Roo.bootstrap.version == 4 ?
41303 '<a class="nav-link" href="#" unselectable="on"' +
41304 (this.disableTooltips ? '' : ' title="{text}"') +
41307 '<a class="nav-link" href="#">' +
41308 '<span unselectable="on"' +
41309 (this.disableTooltips ? '' : ' title="{text}"') +
41310 ' >{text}</span></a>'
41315 switch (typeof(template)) {
41319 template = new Roo.Template(template);
41325 var el = template.overwrite(td, {"text": text});
41327 var inner = el.getElementsByTagName("span")[0];
41329 return {"el": el, "inner": inner};
41337 * @class Roo.TabPanelItem
41338 * @extends Roo.util.Observable
41339 * Represents an individual item (tab plus body) in a TabPanel.
41340 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41341 * @param {String} id The id of this TabPanelItem
41342 * @param {String} text The text for the tab of this TabPanelItem
41343 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41345 Roo.bootstrap.panel.TabItem = function(config){
41347 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41348 * @type Roo.TabPanel
41350 this.tabPanel = config.panel;
41352 * The id for this TabPanelItem
41355 this.id = config.id;
41357 this.disabled = false;
41359 this.text = config.text;
41361 this.loaded = false;
41362 this.closable = config.closable;
41365 * The body element for this TabPanelItem.
41366 * @type Roo.Element
41368 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41369 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41370 this.bodyEl.setStyle("display", "block");
41371 this.bodyEl.setStyle("zoom", "1");
41372 //this.hideAction();
41374 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41376 this.el = Roo.get(els.el);
41377 this.inner = Roo.get(els.inner, true);
41378 this.textEl = Roo.bootstrap.version == 4 ?
41379 this.el : Roo.get(this.el.dom.firstChild, true);
41381 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41382 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41385 // this.el.on("mousedown", this.onTabMouseDown, this);
41386 this.el.on("click", this.onTabClick, this);
41388 if(config.closable){
41389 var c = Roo.get(els.close, true);
41390 c.dom.title = this.closeText;
41391 c.addClassOnOver("close-over");
41392 c.on("click", this.closeClick, this);
41398 * Fires when this tab becomes the active tab.
41399 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41400 * @param {Roo.TabPanelItem} this
41404 * @event beforeclose
41405 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41406 * @param {Roo.TabPanelItem} this
41407 * @param {Object} e Set cancel to true on this object to cancel the close.
41409 "beforeclose": true,
41412 * Fires when this tab is closed.
41413 * @param {Roo.TabPanelItem} this
41417 * @event deactivate
41418 * Fires when this tab is no longer the active tab.
41419 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41420 * @param {Roo.TabPanelItem} this
41422 "deactivate" : true
41424 this.hidden = false;
41426 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41429 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41431 purgeListeners : function(){
41432 Roo.util.Observable.prototype.purgeListeners.call(this);
41433 this.el.removeAllListeners();
41436 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41439 this.status_node.addClass("active");
41442 this.tabPanel.stripWrap.repaint();
41444 this.fireEvent("activate", this.tabPanel, this);
41448 * Returns true if this tab is the active tab.
41449 * @return {Boolean}
41451 isActive : function(){
41452 return this.tabPanel.getActiveTab() == this;
41456 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41459 this.status_node.removeClass("active");
41461 this.fireEvent("deactivate", this.tabPanel, this);
41464 hideAction : function(){
41465 this.bodyEl.hide();
41466 this.bodyEl.setStyle("position", "absolute");
41467 this.bodyEl.setLeft("-20000px");
41468 this.bodyEl.setTop("-20000px");
41471 showAction : function(){
41472 this.bodyEl.setStyle("position", "relative");
41473 this.bodyEl.setTop("");
41474 this.bodyEl.setLeft("");
41475 this.bodyEl.show();
41479 * Set the tooltip for the tab.
41480 * @param {String} tooltip The tab's tooltip
41482 setTooltip : function(text){
41483 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41484 this.textEl.dom.qtip = text;
41485 this.textEl.dom.removeAttribute('title');
41487 this.textEl.dom.title = text;
41491 onTabClick : function(e){
41492 e.preventDefault();
41493 this.tabPanel.activate(this.id);
41496 onTabMouseDown : function(e){
41497 e.preventDefault();
41498 this.tabPanel.activate(this.id);
41501 getWidth : function(){
41502 return this.inner.getWidth();
41505 setWidth : function(width){
41506 var iwidth = width - this.linode.getPadding("lr");
41507 this.inner.setWidth(iwidth);
41508 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41509 this.linode.setWidth(width);
41513 * Show or hide the tab
41514 * @param {Boolean} hidden True to hide or false to show.
41516 setHidden : function(hidden){
41517 this.hidden = hidden;
41518 this.linode.setStyle("display", hidden ? "none" : "");
41522 * Returns true if this tab is "hidden"
41523 * @return {Boolean}
41525 isHidden : function(){
41526 return this.hidden;
41530 * Returns the text for this tab
41533 getText : function(){
41537 autoSize : function(){
41538 //this.el.beginMeasure();
41539 this.textEl.setWidth(1);
41541 * #2804 [new] Tabs in Roojs
41542 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41544 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41545 //this.el.endMeasure();
41549 * Sets the text for the tab (Note: this also sets the tooltip text)
41550 * @param {String} text The tab's text and tooltip
41552 setText : function(text){
41554 this.textEl.update(text);
41555 this.setTooltip(text);
41556 //if(!this.tabPanel.resizeTabs){
41557 // this.autoSize();
41561 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41563 activate : function(){
41564 this.tabPanel.activate(this.id);
41568 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41570 disable : function(){
41571 if(this.tabPanel.active != this){
41572 this.disabled = true;
41573 this.status_node.addClass("disabled");
41578 * Enables this TabPanelItem if it was previously disabled.
41580 enable : function(){
41581 this.disabled = false;
41582 this.status_node.removeClass("disabled");
41586 * Sets the content for this TabPanelItem.
41587 * @param {String} content The content
41588 * @param {Boolean} loadScripts true to look for and load scripts
41590 setContent : function(content, loadScripts){
41591 this.bodyEl.update(content, loadScripts);
41595 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41596 * @return {Roo.UpdateManager} The UpdateManager
41598 getUpdateManager : function(){
41599 return this.bodyEl.getUpdateManager();
41603 * Set a URL to be used to load the content for this TabPanelItem.
41604 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41605 * @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)
41606 * @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)
41607 * @return {Roo.UpdateManager} The UpdateManager
41609 setUrl : function(url, params, loadOnce){
41610 if(this.refreshDelegate){
41611 this.un('activate', this.refreshDelegate);
41613 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41614 this.on("activate", this.refreshDelegate);
41615 return this.bodyEl.getUpdateManager();
41619 _handleRefresh : function(url, params, loadOnce){
41620 if(!loadOnce || !this.loaded){
41621 var updater = this.bodyEl.getUpdateManager();
41622 updater.update(url, params, this._setLoaded.createDelegate(this));
41627 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41628 * Will fail silently if the setUrl method has not been called.
41629 * This does not activate the panel, just updates its content.
41631 refresh : function(){
41632 if(this.refreshDelegate){
41633 this.loaded = false;
41634 this.refreshDelegate();
41639 _setLoaded : function(){
41640 this.loaded = true;
41644 closeClick : function(e){
41647 this.fireEvent("beforeclose", this, o);
41648 if(o.cancel !== true){
41649 this.tabPanel.removeTab(this.id);
41653 * The text displayed in the tooltip for the close icon.
41656 closeText : "Close this tab"
41659 * This script refer to:
41660 * Title: International Telephone Input
41661 * Author: Jack O'Connor
41662 * Code version: v12.1.12
41663 * Availability: https://github.com/jackocnr/intl-tel-input.git
41666 Roo.bootstrap.PhoneInputData = function() {
41669 "Afghanistan (افغانستان)",
41674 "Albania (Shqipëri)",
41679 "Algeria (الجزائر)",
41704 "Antigua and Barbuda",
41714 "Armenia (Հայաստան)",
41730 "Austria (Österreich)",
41735 "Azerbaijan (Azərbaycan)",
41745 "Bahrain (البحرين)",
41750 "Bangladesh (বাংলাদেশ)",
41760 "Belarus (Беларусь)",
41765 "Belgium (België)",
41795 "Bosnia and Herzegovina (Босна и Херцеговина)",
41810 "British Indian Ocean Territory",
41815 "British Virgin Islands",
41825 "Bulgaria (България)",
41835 "Burundi (Uburundi)",
41840 "Cambodia (កម្ពុជា)",
41845 "Cameroon (Cameroun)",
41854 ["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"]
41857 "Cape Verde (Kabu Verdi)",
41862 "Caribbean Netherlands",
41873 "Central African Republic (République centrafricaine)",
41893 "Christmas Island",
41899 "Cocos (Keeling) Islands",
41910 "Comoros (جزر القمر)",
41915 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41920 "Congo (Republic) (Congo-Brazzaville)",
41940 "Croatia (Hrvatska)",
41961 "Czech Republic (Česká republika)",
41966 "Denmark (Danmark)",
41981 "Dominican Republic (República Dominicana)",
41985 ["809", "829", "849"]
42003 "Equatorial Guinea (Guinea Ecuatorial)",
42023 "Falkland Islands (Islas Malvinas)",
42028 "Faroe Islands (Føroyar)",
42049 "French Guiana (Guyane française)",
42054 "French Polynesia (Polynésie française)",
42069 "Georgia (საქართველო)",
42074 "Germany (Deutschland)",
42094 "Greenland (Kalaallit Nunaat)",
42131 "Guinea-Bissau (Guiné Bissau)",
42156 "Hungary (Magyarország)",
42161 "Iceland (Ísland)",
42181 "Iraq (العراق)",
42197 "Israel (ישראל)",
42224 "Jordan (الأردن)",
42229 "Kazakhstan (Казахстан)",
42250 "Kuwait (الكويت)",
42255 "Kyrgyzstan (Кыргызстан)",
42265 "Latvia (Latvija)",
42270 "Lebanon (لبنان)",
42285 "Libya (ليبيا)",
42295 "Lithuania (Lietuva)",
42310 "Macedonia (FYROM) (Македонија)",
42315 "Madagascar (Madagasikara)",
42345 "Marshall Islands",
42355 "Mauritania (موريتانيا)",
42360 "Mauritius (Moris)",
42381 "Moldova (Republica Moldova)",
42391 "Mongolia (Монгол)",
42396 "Montenegro (Crna Gora)",
42406 "Morocco (المغرب)",
42412 "Mozambique (Moçambique)",
42417 "Myanmar (Burma) (မြန်မာ)",
42422 "Namibia (Namibië)",
42437 "Netherlands (Nederland)",
42442 "New Caledonia (Nouvelle-Calédonie)",
42477 "North Korea (조선 민주주의 인민 공화국)",
42482 "Northern Mariana Islands",
42498 "Pakistan (پاکستان)",
42508 "Palestine (فلسطين)",
42518 "Papua New Guinea",
42560 "Réunion (La Réunion)",
42566 "Romania (România)",
42582 "Saint Barthélemy",
42593 "Saint Kitts and Nevis",
42603 "Saint Martin (Saint-Martin (partie française))",
42609 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42614 "Saint Vincent and the Grenadines",
42629 "São Tomé and Príncipe (São Tomé e Príncipe)",
42634 "Saudi Arabia (المملكة العربية السعودية)",
42639 "Senegal (Sénégal)",
42669 "Slovakia (Slovensko)",
42674 "Slovenia (Slovenija)",
42684 "Somalia (Soomaaliya)",
42694 "South Korea (대한민국)",
42699 "South Sudan (جنوب السودان)",
42709 "Sri Lanka (ශ්රී ලංකාව)",
42714 "Sudan (السودان)",
42724 "Svalbard and Jan Mayen",
42735 "Sweden (Sverige)",
42740 "Switzerland (Schweiz)",
42745 "Syria (سوريا)",
42790 "Trinidad and Tobago",
42795 "Tunisia (تونس)",
42800 "Turkey (Türkiye)",
42810 "Turks and Caicos Islands",
42820 "U.S. Virgin Islands",
42830 "Ukraine (Україна)",
42835 "United Arab Emirates (الإمارات العربية المتحدة)",
42857 "Uzbekistan (Oʻzbekiston)",
42867 "Vatican City (Città del Vaticano)",
42878 "Vietnam (Việt Nam)",
42883 "Wallis and Futuna (Wallis-et-Futuna)",
42888 "Western Sahara (الصحراء الغربية)",
42894 "Yemen (اليمن)",
42918 * This script refer to:
42919 * Title: International Telephone Input
42920 * Author: Jack O'Connor
42921 * Code version: v12.1.12
42922 * Availability: https://github.com/jackocnr/intl-tel-input.git
42926 * @class Roo.bootstrap.PhoneInput
42927 * @extends Roo.bootstrap.TriggerField
42928 * An input with International dial-code selection
42930 * @cfg {String} defaultDialCode default '+852'
42931 * @cfg {Array} preferedCountries default []
42934 * Create a new PhoneInput.
42935 * @param {Object} config Configuration options
42938 Roo.bootstrap.PhoneInput = function(config) {
42939 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42942 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42944 listWidth: undefined,
42946 selectedClass: 'active',
42948 invalidClass : "has-warning",
42950 validClass: 'has-success',
42952 allowed: '0123456789',
42957 * @cfg {String} defaultDialCode The default dial code when initializing the input
42959 defaultDialCode: '+852',
42962 * @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
42964 preferedCountries: false,
42966 getAutoCreate : function()
42968 var data = Roo.bootstrap.PhoneInputData();
42969 var align = this.labelAlign || this.parentLabelAlign();
42972 this.allCountries = [];
42973 this.dialCodeMapping = [];
42975 for (var i = 0; i < data.length; i++) {
42977 this.allCountries[i] = {
42981 priority: c[3] || 0,
42982 areaCodes: c[4] || null
42984 this.dialCodeMapping[c[2]] = {
42987 priority: c[3] || 0,
42988 areaCodes: c[4] || null
43000 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43001 maxlength: this.max_length,
43002 cls : 'form-control tel-input',
43003 autocomplete: 'new-password'
43006 var hiddenInput = {
43009 cls: 'hidden-tel-input'
43013 hiddenInput.name = this.name;
43016 if (this.disabled) {
43017 input.disabled = true;
43020 var flag_container = {
43037 cls: this.hasFeedback ? 'has-feedback' : '',
43043 cls: 'dial-code-holder',
43050 cls: 'roo-select2-container input-group',
43057 if (this.fieldLabel.length) {
43060 tooltip: 'This field is required'
43066 cls: 'control-label',
43072 html: this.fieldLabel
43075 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43081 if(this.indicatorpos == 'right') {
43082 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43089 if(align == 'left') {
43097 if(this.labelWidth > 12){
43098 label.style = "width: " + this.labelWidth + 'px';
43100 if(this.labelWidth < 13 && this.labelmd == 0){
43101 this.labelmd = this.labelWidth;
43103 if(this.labellg > 0){
43104 label.cls += ' col-lg-' + this.labellg;
43105 input.cls += ' col-lg-' + (12 - this.labellg);
43107 if(this.labelmd > 0){
43108 label.cls += ' col-md-' + this.labelmd;
43109 container.cls += ' col-md-' + (12 - this.labelmd);
43111 if(this.labelsm > 0){
43112 label.cls += ' col-sm-' + this.labelsm;
43113 container.cls += ' col-sm-' + (12 - this.labelsm);
43115 if(this.labelxs > 0){
43116 label.cls += ' col-xs-' + this.labelxs;
43117 container.cls += ' col-xs-' + (12 - this.labelxs);
43127 var settings = this;
43129 ['xs','sm','md','lg'].map(function(size){
43130 if (settings[size]) {
43131 cfg.cls += ' col-' + size + '-' + settings[size];
43135 this.store = new Roo.data.Store({
43136 proxy : new Roo.data.MemoryProxy({}),
43137 reader : new Roo.data.JsonReader({
43148 'name' : 'dialCode',
43152 'name' : 'priority',
43156 'name' : 'areaCodes',
43163 if(!this.preferedCountries) {
43164 this.preferedCountries = [
43171 var p = this.preferedCountries.reverse();
43174 for (var i = 0; i < p.length; i++) {
43175 for (var j = 0; j < this.allCountries.length; j++) {
43176 if(this.allCountries[j].iso2 == p[i]) {
43177 var t = this.allCountries[j];
43178 this.allCountries.splice(j,1);
43179 this.allCountries.unshift(t);
43185 this.store.proxy.data = {
43187 data: this.allCountries
43193 initEvents : function()
43196 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43198 this.indicator = this.indicatorEl();
43199 this.flag = this.flagEl();
43200 this.dialCodeHolder = this.dialCodeHolderEl();
43202 this.trigger = this.el.select('div.flag-box',true).first();
43203 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43208 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43209 _this.list.setWidth(lw);
43212 this.list.on('mouseover', this.onViewOver, this);
43213 this.list.on('mousemove', this.onViewMove, this);
43214 this.inputEl().on("keyup", this.onKeyUp, this);
43215 this.inputEl().on("keypress", this.onKeyPress, this);
43217 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43219 this.view = new Roo.View(this.list, this.tpl, {
43220 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43223 this.view.on('click', this.onViewClick, this);
43224 this.setValue(this.defaultDialCode);
43227 onTriggerClick : function(e)
43229 Roo.log('trigger click');
43234 if(this.isExpanded()){
43236 this.hasFocus = false;
43238 this.store.load({});
43239 this.hasFocus = true;
43244 isExpanded : function()
43246 return this.list.isVisible();
43249 collapse : function()
43251 if(!this.isExpanded()){
43255 Roo.get(document).un('mousedown', this.collapseIf, this);
43256 Roo.get(document).un('mousewheel', this.collapseIf, this);
43257 this.fireEvent('collapse', this);
43261 expand : function()
43265 if(this.isExpanded() || !this.hasFocus){
43269 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43270 this.list.setWidth(lw);
43273 this.restrictHeight();
43275 Roo.get(document).on('mousedown', this.collapseIf, this);
43276 Roo.get(document).on('mousewheel', this.collapseIf, this);
43278 this.fireEvent('expand', this);
43281 restrictHeight : function()
43283 this.list.alignTo(this.inputEl(), this.listAlign);
43284 this.list.alignTo(this.inputEl(), this.listAlign);
43287 onViewOver : function(e, t)
43289 if(this.inKeyMode){
43292 var item = this.view.findItemFromChild(t);
43295 var index = this.view.indexOf(item);
43296 this.select(index, false);
43301 onViewClick : function(view, doFocus, el, e)
43303 var index = this.view.getSelectedIndexes()[0];
43305 var r = this.store.getAt(index);
43308 this.onSelect(r, index);
43310 if(doFocus !== false && !this.blockFocus){
43311 this.inputEl().focus();
43315 onViewMove : function(e, t)
43317 this.inKeyMode = false;
43320 select : function(index, scrollIntoView)
43322 this.selectedIndex = index;
43323 this.view.select(index);
43324 if(scrollIntoView !== false){
43325 var el = this.view.getNode(index);
43327 this.list.scrollChildIntoView(el, false);
43332 createList : function()
43334 this.list = Roo.get(document.body).createChild({
43336 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43337 style: 'display:none'
43340 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43343 collapseIf : function(e)
43345 var in_combo = e.within(this.el);
43346 var in_list = e.within(this.list);
43347 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43349 if (in_combo || in_list || is_list) {
43355 onSelect : function(record, index)
43357 if(this.fireEvent('beforeselect', this, record, index) !== false){
43359 this.setFlagClass(record.data.iso2);
43360 this.setDialCode(record.data.dialCode);
43361 this.hasFocus = false;
43363 this.fireEvent('select', this, record, index);
43367 flagEl : function()
43369 var flag = this.el.select('div.flag',true).first();
43376 dialCodeHolderEl : function()
43378 var d = this.el.select('input.dial-code-holder',true).first();
43385 setDialCode : function(v)
43387 this.dialCodeHolder.dom.value = '+'+v;
43390 setFlagClass : function(n)
43392 this.flag.dom.className = 'flag '+n;
43395 getValue : function()
43397 var v = this.inputEl().getValue();
43398 if(this.dialCodeHolder) {
43399 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43404 setValue : function(v)
43406 var d = this.getDialCode(v);
43408 //invalid dial code
43409 if(v.length == 0 || !d || d.length == 0) {
43411 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43412 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43418 this.setFlagClass(this.dialCodeMapping[d].iso2);
43419 this.setDialCode(d);
43420 this.inputEl().dom.value = v.replace('+'+d,'');
43421 this.hiddenEl().dom.value = this.getValue();
43426 getDialCode : function(v)
43430 if (v.length == 0) {
43431 return this.dialCodeHolder.dom.value;
43435 if (v.charAt(0) != "+") {
43438 var numericChars = "";
43439 for (var i = 1; i < v.length; i++) {
43440 var c = v.charAt(i);
43443 if (this.dialCodeMapping[numericChars]) {
43444 dialCode = v.substr(1, i);
43446 if (numericChars.length == 4) {
43456 this.setValue(this.defaultDialCode);
43460 hiddenEl : function()
43462 return this.el.select('input.hidden-tel-input',true).first();
43465 // after setting val
43466 onKeyUp : function(e){
43467 this.setValue(this.getValue());
43470 onKeyPress : function(e){
43471 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43478 * @class Roo.bootstrap.MoneyField
43479 * @extends Roo.bootstrap.ComboBox
43480 * Bootstrap MoneyField class
43483 * Create a new MoneyField.
43484 * @param {Object} config Configuration options
43487 Roo.bootstrap.MoneyField = function(config) {
43489 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43493 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43496 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43498 allowDecimals : true,
43500 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43502 decimalSeparator : ".",
43504 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43506 decimalPrecision : 0,
43508 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43510 allowNegative : true,
43512 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43516 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43518 minValue : Number.NEGATIVE_INFINITY,
43520 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43522 maxValue : Number.MAX_VALUE,
43524 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43526 minText : "The minimum value for this field is {0}",
43528 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43530 maxText : "The maximum value for this field is {0}",
43532 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43533 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43535 nanText : "{0} is not a valid number",
43537 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43541 * @cfg {String} defaults currency of the MoneyField
43542 * value should be in lkey
43544 defaultCurrency : false,
43546 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43548 thousandsDelimiter : false,
43550 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43561 getAutoCreate : function()
43563 var align = this.labelAlign || this.parentLabelAlign();
43575 cls : 'form-control roo-money-amount-input',
43576 autocomplete: 'new-password'
43579 var hiddenInput = {
43583 cls: 'hidden-number-input'
43586 if(this.max_length) {
43587 input.maxlength = this.max_length;
43591 hiddenInput.name = this.name;
43594 if (this.disabled) {
43595 input.disabled = true;
43598 var clg = 12 - this.inputlg;
43599 var cmd = 12 - this.inputmd;
43600 var csm = 12 - this.inputsm;
43601 var cxs = 12 - this.inputxs;
43605 cls : 'row roo-money-field',
43609 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43613 cls: 'roo-select2-container input-group',
43617 cls : 'form-control roo-money-currency-input',
43618 autocomplete: 'new-password',
43620 name : this.currencyName
43624 cls : 'input-group-addon',
43638 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43642 cls: this.hasFeedback ? 'has-feedback' : '',
43653 if (this.fieldLabel.length) {
43656 tooltip: 'This field is required'
43662 cls: 'control-label',
43668 html: this.fieldLabel
43671 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43677 if(this.indicatorpos == 'right') {
43678 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43685 if(align == 'left') {
43693 if(this.labelWidth > 12){
43694 label.style = "width: " + this.labelWidth + 'px';
43696 if(this.labelWidth < 13 && this.labelmd == 0){
43697 this.labelmd = this.labelWidth;
43699 if(this.labellg > 0){
43700 label.cls += ' col-lg-' + this.labellg;
43701 input.cls += ' col-lg-' + (12 - this.labellg);
43703 if(this.labelmd > 0){
43704 label.cls += ' col-md-' + this.labelmd;
43705 container.cls += ' col-md-' + (12 - this.labelmd);
43707 if(this.labelsm > 0){
43708 label.cls += ' col-sm-' + this.labelsm;
43709 container.cls += ' col-sm-' + (12 - this.labelsm);
43711 if(this.labelxs > 0){
43712 label.cls += ' col-xs-' + this.labelxs;
43713 container.cls += ' col-xs-' + (12 - this.labelxs);
43724 var settings = this;
43726 ['xs','sm','md','lg'].map(function(size){
43727 if (settings[size]) {
43728 cfg.cls += ' col-' + size + '-' + settings[size];
43735 initEvents : function()
43737 this.indicator = this.indicatorEl();
43739 this.initCurrencyEvent();
43741 this.initNumberEvent();
43744 initCurrencyEvent : function()
43747 throw "can not find store for combo";
43750 this.store = Roo.factory(this.store, Roo.data);
43751 this.store.parent = this;
43755 this.triggerEl = this.el.select('.input-group-addon', true).first();
43757 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43762 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43763 _this.list.setWidth(lw);
43766 this.list.on('mouseover', this.onViewOver, this);
43767 this.list.on('mousemove', this.onViewMove, this);
43768 this.list.on('scroll', this.onViewScroll, this);
43771 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43774 this.view = new Roo.View(this.list, this.tpl, {
43775 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43778 this.view.on('click', this.onViewClick, this);
43780 this.store.on('beforeload', this.onBeforeLoad, this);
43781 this.store.on('load', this.onLoad, this);
43782 this.store.on('loadexception', this.onLoadException, this);
43784 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43785 "up" : function(e){
43786 this.inKeyMode = true;
43790 "down" : function(e){
43791 if(!this.isExpanded()){
43792 this.onTriggerClick();
43794 this.inKeyMode = true;
43799 "enter" : function(e){
43802 if(this.fireEvent("specialkey", this, e)){
43803 this.onViewClick(false);
43809 "esc" : function(e){
43813 "tab" : function(e){
43816 if(this.fireEvent("specialkey", this, e)){
43817 this.onViewClick(false);
43825 doRelay : function(foo, bar, hname){
43826 if(hname == 'down' || this.scope.isExpanded()){
43827 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43835 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43839 initNumberEvent : function(e)
43841 this.inputEl().on("keydown" , this.fireKey, this);
43842 this.inputEl().on("focus", this.onFocus, this);
43843 this.inputEl().on("blur", this.onBlur, this);
43845 this.inputEl().relayEvent('keyup', this);
43847 if(this.indicator){
43848 this.indicator.addClass('invisible');
43851 this.originalValue = this.getValue();
43853 if(this.validationEvent == 'keyup'){
43854 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43855 this.inputEl().on('keyup', this.filterValidation, this);
43857 else if(this.validationEvent !== false){
43858 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43861 if(this.selectOnFocus){
43862 this.on("focus", this.preFocus, this);
43865 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43866 this.inputEl().on("keypress", this.filterKeys, this);
43868 this.inputEl().relayEvent('keypress', this);
43871 var allowed = "0123456789";
43873 if(this.allowDecimals){
43874 allowed += this.decimalSeparator;
43877 if(this.allowNegative){
43881 if(this.thousandsDelimiter) {
43885 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43887 var keyPress = function(e){
43889 var k = e.getKey();
43891 var c = e.getCharCode();
43894 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43895 allowed.indexOf(String.fromCharCode(c)) === -1
43901 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43905 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43910 this.inputEl().on("keypress", keyPress, this);
43914 onTriggerClick : function(e)
43921 this.loadNext = false;
43923 if(this.isExpanded()){
43928 this.hasFocus = true;
43930 if(this.triggerAction == 'all') {
43931 this.doQuery(this.allQuery, true);
43935 this.doQuery(this.getRawValue());
43938 getCurrency : function()
43940 var v = this.currencyEl().getValue();
43945 restrictHeight : function()
43947 this.list.alignTo(this.currencyEl(), this.listAlign);
43948 this.list.alignTo(this.currencyEl(), this.listAlign);
43951 onViewClick : function(view, doFocus, el, e)
43953 var index = this.view.getSelectedIndexes()[0];
43955 var r = this.store.getAt(index);
43958 this.onSelect(r, index);
43962 onSelect : function(record, index){
43964 if(this.fireEvent('beforeselect', this, record, index) !== false){
43966 this.setFromCurrencyData(index > -1 ? record.data : false);
43970 this.fireEvent('select', this, record, index);
43974 setFromCurrencyData : function(o)
43978 this.lastCurrency = o;
43980 if (this.currencyField) {
43981 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43983 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43986 this.lastSelectionText = currency;
43988 //setting default currency
43989 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43990 this.setCurrency(this.defaultCurrency);
43994 this.setCurrency(currency);
43997 setFromData : function(o)
44001 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44003 this.setFromCurrencyData(c);
44008 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44010 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44013 this.setValue(value);
44017 setCurrency : function(v)
44019 this.currencyValue = v;
44022 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44027 setValue : function(v)
44029 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44035 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44037 this.inputEl().dom.value = (v == '') ? '' :
44038 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44040 if(!this.allowZero && v === '0') {
44041 this.hiddenEl().dom.value = '';
44042 this.inputEl().dom.value = '';
44049 getRawValue : function()
44051 var v = this.inputEl().getValue();
44056 getValue : function()
44058 return this.fixPrecision(this.parseValue(this.getRawValue()));
44061 parseValue : function(value)
44063 if(this.thousandsDelimiter) {
44065 r = new RegExp(",", "g");
44066 value = value.replace(r, "");
44069 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44070 return isNaN(value) ? '' : value;
44074 fixPrecision : function(value)
44076 if(this.thousandsDelimiter) {
44078 r = new RegExp(",", "g");
44079 value = value.replace(r, "");
44082 var nan = isNaN(value);
44084 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44085 return nan ? '' : value;
44087 return parseFloat(value).toFixed(this.decimalPrecision);
44090 decimalPrecisionFcn : function(v)
44092 return Math.floor(v);
44095 validateValue : function(value)
44097 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44101 var num = this.parseValue(value);
44104 this.markInvalid(String.format(this.nanText, value));
44108 if(num < this.minValue){
44109 this.markInvalid(String.format(this.minText, this.minValue));
44113 if(num > this.maxValue){
44114 this.markInvalid(String.format(this.maxText, this.maxValue));
44121 validate : function()
44123 if(this.disabled || this.allowBlank){
44128 var currency = this.getCurrency();
44130 if(this.validateValue(this.getRawValue()) && currency.length){
44135 this.markInvalid();
44139 getName: function()
44144 beforeBlur : function()
44150 var v = this.parseValue(this.getRawValue());
44157 onBlur : function()
44161 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44162 //this.el.removeClass(this.focusClass);
44165 this.hasFocus = false;
44167 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44171 var v = this.getValue();
44173 if(String(v) !== String(this.startValue)){
44174 this.fireEvent('change', this, v, this.startValue);
44177 this.fireEvent("blur", this);
44180 inputEl : function()
44182 return this.el.select('.roo-money-amount-input', true).first();
44185 currencyEl : function()
44187 return this.el.select('.roo-money-currency-input', true).first();
44190 hiddenEl : function()
44192 return this.el.select('input.hidden-number-input',true).first();
44196 * @class Roo.bootstrap.BezierSignature
44197 * @extends Roo.bootstrap.Component
44198 * Bootstrap BezierSignature class
44199 * This script refer to:
44200 * Title: Signature Pad
44202 * Availability: https://github.com/szimek/signature_pad
44205 * Create a new BezierSignature
44206 * @param {Object} config The config object
44209 Roo.bootstrap.BezierSignature = function(config){
44210 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44216 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44223 mouse_btn_down: true,
44226 * @cfg {int} canvas height
44228 canvas_height: '200px',
44231 * @cfg {float|function} Radius of a single dot.
44236 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44241 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44246 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44251 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44256 * @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.
44258 bg_color: 'rgba(0, 0, 0, 0)',
44261 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44263 dot_color: 'black',
44266 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44268 velocity_filter_weight: 0.7,
44271 * @cfg {function} Callback when stroke begin.
44276 * @cfg {function} Callback when stroke end.
44280 getAutoCreate : function()
44282 var cls = 'roo-signature column';
44285 cls += ' ' + this.cls;
44295 for(var i = 0; i < col_sizes.length; i++) {
44296 if(this[col_sizes[i]]) {
44297 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44307 cls: 'roo-signature-body',
44311 cls: 'roo-signature-body-canvas',
44312 height: this.canvas_height,
44313 width: this.canvas_width
44320 style: 'display: none'
44328 initEvents: function()
44330 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44332 var canvas = this.canvasEl();
44334 // mouse && touch event swapping...
44335 canvas.dom.style.touchAction = 'none';
44336 canvas.dom.style.msTouchAction = 'none';
44338 this.mouse_btn_down = false;
44339 canvas.on('mousedown', this._handleMouseDown, this);
44340 canvas.on('mousemove', this._handleMouseMove, this);
44341 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44343 if (window.PointerEvent) {
44344 canvas.on('pointerdown', this._handleMouseDown, this);
44345 canvas.on('pointermove', this._handleMouseMove, this);
44346 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44349 if ('ontouchstart' in window) {
44350 canvas.on('touchstart', this._handleTouchStart, this);
44351 canvas.on('touchmove', this._handleTouchMove, this);
44352 canvas.on('touchend', this._handleTouchEnd, this);
44355 Roo.EventManager.onWindowResize(this.resize, this, true);
44357 // file input event
44358 this.fileEl().on('change', this.uploadImage, this);
44365 resize: function(){
44367 var canvas = this.canvasEl().dom;
44368 var ctx = this.canvasElCtx();
44369 var img_data = false;
44371 if(canvas.width > 0) {
44372 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44374 // setting canvas width will clean img data
44377 var style = window.getComputedStyle ?
44378 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44380 var padding_left = parseInt(style.paddingLeft) || 0;
44381 var padding_right = parseInt(style.paddingRight) || 0;
44383 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44386 ctx.putImageData(img_data, 0, 0);
44390 _handleMouseDown: function(e)
44392 if (e.browserEvent.which === 1) {
44393 this.mouse_btn_down = true;
44394 this.strokeBegin(e);
44398 _handleMouseMove: function (e)
44400 if (this.mouse_btn_down) {
44401 this.strokeMoveUpdate(e);
44405 _handleMouseUp: function (e)
44407 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44408 this.mouse_btn_down = false;
44413 _handleTouchStart: function (e) {
44415 e.preventDefault();
44416 if (e.browserEvent.targetTouches.length === 1) {
44417 // var touch = e.browserEvent.changedTouches[0];
44418 // this.strokeBegin(touch);
44420 this.strokeBegin(e); // assume e catching the correct xy...
44424 _handleTouchMove: function (e) {
44425 e.preventDefault();
44426 // var touch = event.targetTouches[0];
44427 // _this._strokeMoveUpdate(touch);
44428 this.strokeMoveUpdate(e);
44431 _handleTouchEnd: function (e) {
44432 var wasCanvasTouched = e.target === this.canvasEl().dom;
44433 if (wasCanvasTouched) {
44434 e.preventDefault();
44435 // var touch = event.changedTouches[0];
44436 // _this._strokeEnd(touch);
44441 reset: function () {
44442 this._lastPoints = [];
44443 this._lastVelocity = 0;
44444 this._lastWidth = (this.min_width + this.max_width) / 2;
44445 this.canvasElCtx().fillStyle = this.dot_color;
44448 strokeMoveUpdate: function(e)
44450 this.strokeUpdate(e);
44452 if (this.throttle) {
44453 this.throttleStroke(this.strokeUpdate, this.throttle);
44456 this.strokeUpdate(e);
44460 strokeBegin: function(e)
44462 var newPointGroup = {
44463 color: this.dot_color,
44467 if (typeof this.onBegin === 'function') {
44471 this.curve_data.push(newPointGroup);
44473 this.strokeUpdate(e);
44476 strokeUpdate: function(e)
44478 var rect = this.canvasEl().dom.getBoundingClientRect();
44479 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44480 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44481 var lastPoints = lastPointGroup.points;
44482 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44483 var isLastPointTooClose = lastPoint
44484 ? point.distanceTo(lastPoint) <= this.min_distance
44486 var color = lastPointGroup.color;
44487 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44488 var curve = this.addPoint(point);
44490 this.drawDot({color: color, point: point});
44493 this.drawCurve({color: color, curve: curve});
44503 strokeEnd: function(e)
44505 this.strokeUpdate(e);
44506 if (typeof this.onEnd === 'function') {
44511 addPoint: function (point) {
44512 var _lastPoints = this._lastPoints;
44513 _lastPoints.push(point);
44514 if (_lastPoints.length > 2) {
44515 if (_lastPoints.length === 3) {
44516 _lastPoints.unshift(_lastPoints[0]);
44518 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44519 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44520 _lastPoints.shift();
44526 calculateCurveWidths: function (startPoint, endPoint) {
44527 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44528 (1 - this.velocity_filter_weight) * this._lastVelocity;
44530 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44533 start: this._lastWidth
44536 this._lastVelocity = velocity;
44537 this._lastWidth = newWidth;
44541 drawDot: function (_a) {
44542 var color = _a.color, point = _a.point;
44543 var ctx = this.canvasElCtx();
44544 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44546 this.drawCurveSegment(point.x, point.y, width);
44548 ctx.fillStyle = color;
44552 drawCurve: function (_a) {
44553 var color = _a.color, curve = _a.curve;
44554 var ctx = this.canvasElCtx();
44555 var widthDelta = curve.endWidth - curve.startWidth;
44556 var drawSteps = Math.floor(curve.length()) * 2;
44558 ctx.fillStyle = color;
44559 for (var i = 0; i < drawSteps; i += 1) {
44560 var t = i / drawSteps;
44566 var x = uuu * curve.startPoint.x;
44567 x += 3 * uu * t * curve.control1.x;
44568 x += 3 * u * tt * curve.control2.x;
44569 x += ttt * curve.endPoint.x;
44570 var y = uuu * curve.startPoint.y;
44571 y += 3 * uu * t * curve.control1.y;
44572 y += 3 * u * tt * curve.control2.y;
44573 y += ttt * curve.endPoint.y;
44574 var width = curve.startWidth + ttt * widthDelta;
44575 this.drawCurveSegment(x, y, width);
44581 drawCurveSegment: function (x, y, width) {
44582 var ctx = this.canvasElCtx();
44584 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44585 this.is_empty = false;
44590 var ctx = this.canvasElCtx();
44591 var canvas = this.canvasEl().dom;
44592 ctx.fillStyle = this.bg_color;
44593 ctx.clearRect(0, 0, canvas.width, canvas.height);
44594 ctx.fillRect(0, 0, canvas.width, canvas.height);
44595 this.curve_data = [];
44597 this.is_empty = true;
44602 return this.el.select('input',true).first();
44605 canvasEl: function()
44607 return this.el.select('canvas',true).first();
44610 canvasElCtx: function()
44612 return this.el.select('canvas',true).first().dom.getContext('2d');
44615 getImage: function(type)
44617 if(this.is_empty) {
44622 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44625 drawFromImage: function(img_src)
44627 var img = new Image();
44629 img.onload = function(){
44630 this.canvasElCtx().drawImage(img, 0, 0);
44635 this.is_empty = false;
44638 selectImage: function()
44640 this.fileEl().dom.click();
44643 uploadImage: function(e)
44645 var reader = new FileReader();
44647 reader.onload = function(e){
44648 var img = new Image();
44649 img.onload = function(){
44651 this.canvasElCtx().drawImage(img, 0, 0);
44653 img.src = e.target.result;
44656 reader.readAsDataURL(e.target.files[0]);
44659 // Bezier Point Constructor
44660 Point: (function () {
44661 function Point(x, y, time) {
44664 this.time = time || Date.now();
44666 Point.prototype.distanceTo = function (start) {
44667 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44669 Point.prototype.equals = function (other) {
44670 return this.x === other.x && this.y === other.y && this.time === other.time;
44672 Point.prototype.velocityFrom = function (start) {
44673 return this.time !== start.time
44674 ? this.distanceTo(start) / (this.time - start.time)
44681 // Bezier Constructor
44682 Bezier: (function () {
44683 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44684 this.startPoint = startPoint;
44685 this.control2 = control2;
44686 this.control1 = control1;
44687 this.endPoint = endPoint;
44688 this.startWidth = startWidth;
44689 this.endWidth = endWidth;
44691 Bezier.fromPoints = function (points, widths, scope) {
44692 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44693 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44694 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44696 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44697 var dx1 = s1.x - s2.x;
44698 var dy1 = s1.y - s2.y;
44699 var dx2 = s2.x - s3.x;
44700 var dy2 = s2.y - s3.y;
44701 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44702 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44703 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44704 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44705 var dxm = m1.x - m2.x;
44706 var dym = m1.y - m2.y;
44707 var k = l2 / (l1 + l2);
44708 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44709 var tx = s2.x - cm.x;
44710 var ty = s2.y - cm.y;
44712 c1: new scope.Point(m1.x + tx, m1.y + ty),
44713 c2: new scope.Point(m2.x + tx, m2.y + ty)
44716 Bezier.prototype.length = function () {
44721 for (var i = 0; i <= steps; i += 1) {
44723 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44724 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44726 var xdiff = cx - px;
44727 var ydiff = cy - py;
44728 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44735 Bezier.prototype.point = function (t, start, c1, c2, end) {
44736 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44737 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44738 + (3.0 * c2 * (1.0 - t) * t * t)
44739 + (end * t * t * t);
44744 throttleStroke: function(fn, wait) {
44745 if (wait === void 0) { wait = 250; }
44747 var timeout = null;
44751 var later = function () {
44752 previous = Date.now();
44754 result = fn.apply(storedContext, storedArgs);
44756 storedContext = null;
44760 return function wrapper() {
44762 for (var _i = 0; _i < arguments.length; _i++) {
44763 args[_i] = arguments[_i];
44765 var now = Date.now();
44766 var remaining = wait - (now - previous);
44767 storedContext = this;
44769 if (remaining <= 0 || remaining > wait) {
44771 clearTimeout(timeout);
44775 result = fn.apply(storedContext, storedArgs);
44777 storedContext = null;
44781 else if (!timeout) {
44782 timeout = window.setTimeout(later, remaining);