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 this.fireEvent('uploaded', this, this.selectorEl.dom.files );
2985 * addCard - add an Attachment to the uploader
2986 * @param data - the data about the image to upload
2990 title : "Title of file",
2991 is_uploaded : false,
2992 src : "http://.....",
2993 srcfile : { the File upload object },
2994 mimetype : file.type,
2997 .. any other data...
3022 * @class Roo.bootstrap.Img
3023 * @extends Roo.bootstrap.Component
3024 * Bootstrap Img class
3025 * @cfg {Boolean} imgResponsive false | true
3026 * @cfg {String} border rounded | circle | thumbnail
3027 * @cfg {String} src image source
3028 * @cfg {String} alt image alternative text
3029 * @cfg {String} href a tag href
3030 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3031 * @cfg {String} xsUrl xs image source
3032 * @cfg {String} smUrl sm image source
3033 * @cfg {String} mdUrl md image source
3034 * @cfg {String} lgUrl lg image source
3037 * Create a new Input
3038 * @param {Object} config The config object
3041 Roo.bootstrap.Img = function(config){
3042 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3048 * The img click event for the img.
3049 * @param {Roo.EventObject} e
3055 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3057 imgResponsive: true,
3067 getAutoCreate : function()
3069 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3070 return this.createSingleImg();
3075 cls: 'roo-image-responsive-group',
3080 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3082 if(!_this[size + 'Url']){
3088 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3089 html: _this.html || cfg.html,
3090 src: _this[size + 'Url']
3093 img.cls += ' roo-image-responsive-' + size;
3095 var s = ['xs', 'sm', 'md', 'lg'];
3097 s.splice(s.indexOf(size), 1);
3099 Roo.each(s, function(ss){
3100 img.cls += ' hidden-' + ss;
3103 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3104 cfg.cls += ' img-' + _this.border;
3108 cfg.alt = _this.alt;
3121 a.target = _this.target;
3125 cfg.cn.push((_this.href) ? a : img);
3132 createSingleImg : function()
3136 cls: (this.imgResponsive) ? 'img-responsive' : '',
3138 src : 'about:blank' // just incase src get's set to undefined?!?
3141 cfg.html = this.html || cfg.html;
3143 cfg.src = this.src || cfg.src;
3145 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3146 cfg.cls += ' img-' + this.border;
3163 a.target = this.target;
3168 return (this.href) ? a : cfg;
3171 initEvents: function()
3174 this.el.on('click', this.onClick, this);
3179 onClick : function(e)
3181 Roo.log('img onclick');
3182 this.fireEvent('click', this, e);
3185 * Sets the url of the image - used to update it
3186 * @param {String} url the url of the image
3189 setSrc : function(url)
3193 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3194 this.el.dom.src = url;
3198 this.el.select('img', true).first().dom.src = url;
3214 * @class Roo.bootstrap.Link
3215 * @extends Roo.bootstrap.Component
3216 * Bootstrap Link Class
3217 * @cfg {String} alt image alternative text
3218 * @cfg {String} href a tag href
3219 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3220 * @cfg {String} html the content of the link.
3221 * @cfg {String} anchor name for the anchor link
3222 * @cfg {String} fa - favicon
3224 * @cfg {Boolean} preventDefault (true | false) default false
3228 * Create a new Input
3229 * @param {Object} config The config object
3232 Roo.bootstrap.Link = function(config){
3233 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3239 * The img click event for the img.
3240 * @param {Roo.EventObject} e
3246 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3250 preventDefault: false,
3256 getAutoCreate : function()
3258 var html = this.html || '';
3260 if (this.fa !== false) {
3261 html = '<i class="fa fa-' + this.fa + '"></i>';
3266 // anchor's do not require html/href...
3267 if (this.anchor === false) {
3269 cfg.href = this.href || '#';
3271 cfg.name = this.anchor;
3272 if (this.html !== false || this.fa !== false) {
3275 if (this.href !== false) {
3276 cfg.href = this.href;
3280 if(this.alt !== false){
3285 if(this.target !== false) {
3286 cfg.target = this.target;
3292 initEvents: function() {
3294 if(!this.href || this.preventDefault){
3295 this.el.on('click', this.onClick, this);
3299 onClick : function(e)
3301 if(this.preventDefault){
3304 //Roo.log('img onclick');
3305 this.fireEvent('click', this, e);
3318 * @class Roo.bootstrap.Header
3319 * @extends Roo.bootstrap.Component
3320 * Bootstrap Header class
3321 * @cfg {String} html content of header
3322 * @cfg {Number} level (1|2|3|4|5|6) default 1
3325 * Create a new Header
3326 * @param {Object} config The config object
3330 Roo.bootstrap.Header = function(config){
3331 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3334 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3342 getAutoCreate : function(){
3347 tag: 'h' + (1 *this.level),
3348 html: this.html || ''
3360 * Ext JS Library 1.1.1
3361 * Copyright(c) 2006-2007, Ext JS, LLC.
3363 * Originally Released Under LGPL - original licence link has changed is not relivant.
3366 * <script type="text/javascript">
3370 * @class Roo.bootstrap.MenuMgr
3371 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3374 Roo.bootstrap.MenuMgr = function(){
3375 var menus, active, groups = {}, attached = false, lastShow = new Date();
3377 // private - called when first menu is created
3380 active = new Roo.util.MixedCollection();
3381 Roo.get(document).addKeyListener(27, function(){
3382 if(active.length > 0){
3390 if(active && active.length > 0){
3391 var c = active.clone();
3401 if(active.length < 1){
3402 Roo.get(document).un("mouseup", onMouseDown);
3410 var last = active.last();
3411 lastShow = new Date();
3414 Roo.get(document).on("mouseup", onMouseDown);
3419 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3420 m.parentMenu.activeChild = m;
3421 }else if(last && last.isVisible()){
3422 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3427 function onBeforeHide(m){
3429 m.activeChild.hide();
3431 if(m.autoHideTimer){
3432 clearTimeout(m.autoHideTimer);
3433 delete m.autoHideTimer;
3438 function onBeforeShow(m){
3439 var pm = m.parentMenu;
3440 if(!pm && !m.allowOtherMenus){
3442 }else if(pm && pm.activeChild && active != m){
3443 pm.activeChild.hide();
3447 // private this should really trigger on mouseup..
3448 function onMouseDown(e){
3449 Roo.log("on Mouse Up");
3451 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3452 Roo.log("MenuManager hideAll");
3461 function onBeforeCheck(mi, state){
3463 var g = groups[mi.group];
3464 for(var i = 0, l = g.length; i < l; i++){
3466 g[i].setChecked(false);
3475 * Hides all menus that are currently visible
3477 hideAll : function(){
3482 register : function(menu){
3486 menus[menu.id] = menu;
3487 menu.on("beforehide", onBeforeHide);
3488 menu.on("hide", onHide);
3489 menu.on("beforeshow", onBeforeShow);
3490 menu.on("show", onShow);
3492 if(g && menu.events["checkchange"]){
3496 groups[g].push(menu);
3497 menu.on("checkchange", onCheck);
3502 * Returns a {@link Roo.menu.Menu} object
3503 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3504 * be used to generate and return a new Menu instance.
3506 get : function(menu){
3507 if(typeof menu == "string"){ // menu id
3509 }else if(menu.events){ // menu instance
3512 /*else if(typeof menu.length == 'number'){ // array of menu items?
3513 return new Roo.bootstrap.Menu({items:menu});
3514 }else{ // otherwise, must be a config
3515 return new Roo.bootstrap.Menu(menu);
3522 unregister : function(menu){
3523 delete menus[menu.id];
3524 menu.un("beforehide", onBeforeHide);
3525 menu.un("hide", onHide);
3526 menu.un("beforeshow", onBeforeShow);
3527 menu.un("show", onShow);
3529 if(g && menu.events["checkchange"]){
3530 groups[g].remove(menu);
3531 menu.un("checkchange", onCheck);
3536 registerCheckable : function(menuItem){
3537 var g = menuItem.group;
3542 groups[g].push(menuItem);
3543 menuItem.on("beforecheckchange", onBeforeCheck);
3548 unregisterCheckable : function(menuItem){
3549 var g = menuItem.group;
3551 groups[g].remove(menuItem);
3552 menuItem.un("beforecheckchange", onBeforeCheck);
3564 * @class Roo.bootstrap.Menu
3565 * @extends Roo.bootstrap.Component
3566 * Bootstrap Menu class - container for MenuItems
3567 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3568 * @cfg {bool} hidden if the menu should be hidden when rendered.
3569 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3570 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3574 * @param {Object} config The config object
3578 Roo.bootstrap.Menu = function(config){
3579 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3580 if (this.registerMenu && this.type != 'treeview') {
3581 Roo.bootstrap.MenuMgr.register(this);
3588 * Fires before this menu is displayed (return false to block)
3589 * @param {Roo.menu.Menu} this
3594 * Fires before this menu is hidden (return false to block)
3595 * @param {Roo.menu.Menu} this
3600 * Fires after this menu is displayed
3601 * @param {Roo.menu.Menu} this
3606 * Fires after this menu is hidden
3607 * @param {Roo.menu.Menu} this
3612 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3613 * @param {Roo.menu.Menu} this
3614 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3615 * @param {Roo.EventObject} e
3620 * Fires when the mouse is hovering over this menu
3621 * @param {Roo.menu.Menu} this
3622 * @param {Roo.EventObject} e
3623 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3628 * Fires when the mouse exits this menu
3629 * @param {Roo.menu.Menu} this
3630 * @param {Roo.EventObject} e
3631 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3636 * Fires when a menu item contained in this menu is clicked
3637 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3638 * @param {Roo.EventObject} e
3642 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3645 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3649 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3652 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3654 registerMenu : true,
3656 menuItems :false, // stores the menu items..
3666 getChildContainer : function() {
3670 getAutoCreate : function(){
3672 //if (['right'].indexOf(this.align)!==-1) {
3673 // cfg.cn[1].cls += ' pull-right'
3679 cls : 'dropdown-menu' ,
3680 style : 'z-index:1000'
3684 if (this.type === 'submenu') {
3685 cfg.cls = 'submenu active';
3687 if (this.type === 'treeview') {
3688 cfg.cls = 'treeview-menu';
3693 initEvents : function() {
3695 // Roo.log("ADD event");
3696 // Roo.log(this.triggerEl.dom);
3698 this.triggerEl.on('click', this.onTriggerClick, this);
3700 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3703 if (this.triggerEl.hasClass('nav-item')) {
3704 // dropdown toggle on the 'a' in BS4?
3705 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3707 this.triggerEl.addClass('dropdown-toggle');
3710 this.el.on('touchstart' , this.onTouch, this);
3712 this.el.on('click' , this.onClick, this);
3714 this.el.on("mouseover", this.onMouseOver, this);
3715 this.el.on("mouseout", this.onMouseOut, this);
3719 findTargetItem : function(e)
3721 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3725 //Roo.log(t); Roo.log(t.id);
3727 //Roo.log(this.menuitems);
3728 return this.menuitems.get(t.id);
3730 //return this.items.get(t.menuItemId);
3736 onTouch : function(e)
3738 Roo.log("menu.onTouch");
3739 //e.stopEvent(); this make the user popdown broken
3743 onClick : function(e)
3745 Roo.log("menu.onClick");
3747 var t = this.findTargetItem(e);
3748 if(!t || t.isContainer){
3753 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3754 if(t == this.activeItem && t.shouldDeactivate(e)){
3755 this.activeItem.deactivate();
3756 delete this.activeItem;
3760 this.setActiveItem(t, true);
3768 Roo.log('pass click event');
3772 this.fireEvent("click", this, t, e);
3776 if(!t.href.length || t.href == '#'){
3777 (function() { _this.hide(); }).defer(100);
3782 onMouseOver : function(e){
3783 var t = this.findTargetItem(e);
3786 // if(t.canActivate && !t.disabled){
3787 // this.setActiveItem(t, true);
3791 this.fireEvent("mouseover", this, e, t);
3793 isVisible : function(){
3794 return !this.hidden;
3796 onMouseOut : function(e){
3797 var t = this.findTargetItem(e);
3800 // if(t == this.activeItem && t.shouldDeactivate(e)){
3801 // this.activeItem.deactivate();
3802 // delete this.activeItem;
3805 this.fireEvent("mouseout", this, e, t);
3810 * Displays this menu relative to another element
3811 * @param {String/HTMLElement/Roo.Element} element The element to align to
3812 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3813 * the element (defaults to this.defaultAlign)
3814 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3816 show : function(el, pos, parentMenu)
3818 if (false === this.fireEvent("beforeshow", this)) {
3819 Roo.log("show canceled");
3822 this.parentMenu = parentMenu;
3827 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3830 * Displays this menu at a specific xy position
3831 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3832 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3834 showAt : function(xy, parentMenu, /* private: */_e){
3835 this.parentMenu = parentMenu;
3840 this.fireEvent("beforeshow", this);
3841 //xy = this.el.adjustForConstraints(xy);
3845 this.hideMenuItems();
3846 this.hidden = false;
3847 this.triggerEl.addClass('open');
3848 this.el.addClass('show');
3850 // reassign x when hitting right
3851 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3852 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3855 // reassign y when hitting bottom
3856 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3857 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3860 // but the list may align on trigger left or trigger top... should it be a properity?
3862 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3867 this.fireEvent("show", this);
3873 this.doFocus.defer(50, this);
3877 doFocus : function(){
3879 this.focusEl.focus();
3884 * Hides this menu and optionally all parent menus
3885 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3887 hide : function(deep)
3889 if (false === this.fireEvent("beforehide", this)) {
3890 Roo.log("hide canceled");
3893 this.hideMenuItems();
3894 if(this.el && this.isVisible()){
3896 if(this.activeItem){
3897 this.activeItem.deactivate();
3898 this.activeItem = null;
3900 this.triggerEl.removeClass('open');;
3901 this.el.removeClass('show');
3903 this.fireEvent("hide", this);
3905 if(deep === true && this.parentMenu){
3906 this.parentMenu.hide(true);
3910 onTriggerClick : function(e)
3912 Roo.log('trigger click');
3914 var target = e.getTarget();
3916 Roo.log(target.nodeName.toLowerCase());
3918 if(target.nodeName.toLowerCase() === 'i'){
3924 onTriggerPress : function(e)
3926 Roo.log('trigger press');
3927 //Roo.log(e.getTarget());
3928 // Roo.log(this.triggerEl.dom);
3930 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3931 var pel = Roo.get(e.getTarget());
3932 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3933 Roo.log('is treeview or dropdown?');
3937 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3941 if (this.isVisible()) {
3946 this.show(this.triggerEl, '?', false);
3949 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3956 hideMenuItems : function()
3958 Roo.log("hide Menu Items");
3963 this.el.select('.open',true).each(function(aa) {
3965 aa.removeClass('open');
3969 addxtypeChild : function (tree, cntr) {
3970 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3972 this.menuitems.add(comp);
3984 this.getEl().dom.innerHTML = '';
3985 this.menuitems.clear();
3999 * @class Roo.bootstrap.MenuItem
4000 * @extends Roo.bootstrap.Component
4001 * Bootstrap MenuItem class
4002 * @cfg {String} html the menu label
4003 * @cfg {String} href the link
4004 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4005 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4006 * @cfg {Boolean} active used on sidebars to highlight active itesm
4007 * @cfg {String} fa favicon to show on left of menu item.
4008 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4012 * Create a new MenuItem
4013 * @param {Object} config The config object
4017 Roo.bootstrap.MenuItem = function(config){
4018 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4023 * The raw click event for the entire grid.
4024 * @param {Roo.bootstrap.MenuItem} this
4025 * @param {Roo.EventObject} e
4031 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4035 preventDefault: false,
4036 isContainer : false,
4040 getAutoCreate : function(){
4042 if(this.isContainer){
4045 cls: 'dropdown-menu-item '
4055 cls : 'dropdown-item',
4060 if (this.fa !== false) {
4063 cls : 'fa fa-' + this.fa
4072 cls: 'dropdown-menu-item',
4075 if (this.parent().type == 'treeview') {
4076 cfg.cls = 'treeview-menu';
4079 cfg.cls += ' active';
4084 anc.href = this.href || cfg.cn[0].href ;
4085 ctag.html = this.html || cfg.cn[0].html ;
4089 initEvents: function()
4091 if (this.parent().type == 'treeview') {
4092 this.el.select('a').on('click', this.onClick, this);
4096 this.menu.parentType = this.xtype;
4097 this.menu.triggerEl = this.el;
4098 this.menu = this.addxtype(Roo.apply({}, this.menu));
4102 onClick : function(e)
4104 Roo.log('item on click ');
4106 if(this.preventDefault){
4109 //this.parent().hideMenuItems();
4111 this.fireEvent('click', this, e);
4130 * @class Roo.bootstrap.MenuSeparator
4131 * @extends Roo.bootstrap.Component
4132 * Bootstrap MenuSeparator class
4135 * Create a new MenuItem
4136 * @param {Object} config The config object
4140 Roo.bootstrap.MenuSeparator = function(config){
4141 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4144 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4146 getAutoCreate : function(){
4165 * @class Roo.bootstrap.Modal
4166 * @extends Roo.bootstrap.Component
4167 * Bootstrap Modal class
4168 * @cfg {String} title Title of dialog
4169 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4170 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4171 * @cfg {Boolean} specificTitle default false
4172 * @cfg {Array} buttons Array of buttons or standard button set..
4173 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4174 * @cfg {Boolean} animate default true
4175 * @cfg {Boolean} allow_close default true
4176 * @cfg {Boolean} fitwindow default false
4177 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4178 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4179 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4180 * @cfg {String} size (sm|lg|xl) default empty
4181 * @cfg {Number} max_width set the max width of modal
4182 * @cfg {Boolean} editableTitle can the title be edited
4187 * Create a new Modal Dialog
4188 * @param {Object} config The config object
4191 Roo.bootstrap.Modal = function(config){
4192 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4197 * The raw btnclick event for the button
4198 * @param {Roo.EventObject} e
4203 * Fire when dialog resize
4204 * @param {Roo.bootstrap.Modal} this
4205 * @param {Roo.EventObject} e
4209 * @event titlechanged
4210 * Fire when the editable title has been changed
4211 * @param {Roo.bootstrap.Modal} this
4212 * @param {Roo.EventObject} value
4214 "titlechanged" : true
4217 this.buttons = this.buttons || [];
4220 this.tmpl = Roo.factory(this.tmpl);
4225 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4227 title : 'test dialog',
4237 specificTitle: false,
4239 buttonPosition: 'right',
4261 editableTitle : false,
4263 onRender : function(ct, position)
4265 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4268 var cfg = Roo.apply({}, this.getAutoCreate());
4271 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4273 //if (!cfg.name.length) {
4277 cfg.cls += ' ' + this.cls;
4280 cfg.style = this.style;
4282 this.el = Roo.get(document.body).createChild(cfg, position);
4284 //var type = this.el.dom.type;
4287 if(this.tabIndex !== undefined){
4288 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4291 this.dialogEl = this.el.select('.modal-dialog',true).first();
4292 this.bodyEl = this.el.select('.modal-body',true).first();
4293 this.closeEl = this.el.select('.modal-header .close', true).first();
4294 this.headerEl = this.el.select('.modal-header',true).first();
4295 this.titleEl = this.el.select('.modal-title',true).first();
4296 this.footerEl = this.el.select('.modal-footer',true).first();
4298 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4300 //this.el.addClass("x-dlg-modal");
4302 if (this.buttons.length) {
4303 Roo.each(this.buttons, function(bb) {
4304 var b = Roo.apply({}, bb);
4305 b.xns = b.xns || Roo.bootstrap;
4306 b.xtype = b.xtype || 'Button';
4307 if (typeof(b.listeners) == 'undefined') {
4308 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4311 var btn = Roo.factory(b);
4313 btn.render(this.getButtonContainer());
4317 // render the children.
4320 if(typeof(this.items) != 'undefined'){
4321 var items = this.items;
4324 for(var i =0;i < items.length;i++) {
4325 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4329 this.items = nitems;
4331 // where are these used - they used to be body/close/footer
4335 //this.el.addClass([this.fieldClass, this.cls]);
4339 getAutoCreate : function()
4341 // we will default to modal-body-overflow - might need to remove or make optional later.
4343 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4344 html : this.html || ''
4349 cls : 'modal-title',
4353 if(this.specificTitle){ // WTF is this?
4358 if (this.allow_close && Roo.bootstrap.version == 3) {
4368 if (this.editableTitle) {
4370 cls: 'form-control roo-editable-title d-none',
4376 if (this.allow_close && Roo.bootstrap.version == 4) {
4386 if(this.size.length){
4387 size = 'modal-' + this.size;
4390 var footer = Roo.bootstrap.version == 3 ?
4392 cls : 'modal-footer',
4396 cls: 'btn-' + this.buttonPosition
4401 { // BS4 uses mr-auto on left buttons....
4402 cls : 'modal-footer'
4413 cls: "modal-dialog " + size,
4416 cls : "modal-content",
4419 cls : 'modal-header',
4434 modal.cls += ' fade';
4440 getChildContainer : function() {
4445 getButtonContainer : function() {
4447 return Roo.bootstrap.version == 4 ?
4448 this.el.select('.modal-footer',true).first()
4449 : this.el.select('.modal-footer div',true).first();
4452 initEvents : function()
4454 if (this.allow_close) {
4455 this.closeEl.on('click', this.hide, this);
4457 Roo.EventManager.onWindowResize(this.resize, this, true);
4458 if (this.editableTitle) {
4459 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4460 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4461 this.headerEditEl.on('keyup', function(e) {
4462 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4463 this.toggleHeaderInput(false)
4466 this.headerEditEl.on('blur', function(e) {
4467 this.toggleHeaderInput(false)
4476 this.maskEl.setSize(
4477 Roo.lib.Dom.getViewWidth(true),
4478 Roo.lib.Dom.getViewHeight(true)
4481 if (this.fitwindow) {
4483 this.dialogEl.setStyle( { 'max-width' : '100%' });
4485 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4486 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4491 if(this.max_width !== 0) {
4493 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4496 this.setSize(w, this.height);
4500 if(this.max_height) {
4501 this.setSize(w,Math.min(
4503 Roo.lib.Dom.getViewportHeight(true) - 60
4509 if(!this.fit_content) {
4510 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4514 this.setSize(w, Math.min(
4516 this.headerEl.getHeight() +
4517 this.footerEl.getHeight() +
4518 this.getChildHeight(this.bodyEl.dom.childNodes),
4519 Roo.lib.Dom.getViewportHeight(true) - 60)
4525 setSize : function(w,h)
4536 if (!this.rendered) {
4539 this.toggleHeaderInput(false);
4540 //this.el.setStyle('display', 'block');
4541 this.el.removeClass('hideing');
4542 this.el.dom.style.display='block';
4544 Roo.get(document.body).addClass('modal-open');
4546 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4549 this.el.addClass('show');
4550 this.el.addClass('in');
4553 this.el.addClass('show');
4554 this.el.addClass('in');
4557 // not sure how we can show data in here..
4559 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4562 Roo.get(document.body).addClass("x-body-masked");
4564 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4565 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4566 this.maskEl.dom.style.display = 'block';
4567 this.maskEl.addClass('show');
4572 this.fireEvent('show', this);
4574 // set zindex here - otherwise it appears to be ignored...
4575 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4578 this.items.forEach( function(e) {
4579 e.layout ? e.layout() : false;
4587 if(this.fireEvent("beforehide", this) !== false){
4589 this.maskEl.removeClass('show');
4591 this.maskEl.dom.style.display = '';
4592 Roo.get(document.body).removeClass("x-body-masked");
4593 this.el.removeClass('in');
4594 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4596 if(this.animate){ // why
4597 this.el.addClass('hideing');
4598 this.el.removeClass('show');
4600 if (!this.el.hasClass('hideing')) {
4601 return; // it's been shown again...
4604 this.el.dom.style.display='';
4606 Roo.get(document.body).removeClass('modal-open');
4607 this.el.removeClass('hideing');
4611 this.el.removeClass('show');
4612 this.el.dom.style.display='';
4613 Roo.get(document.body).removeClass('modal-open');
4616 this.fireEvent('hide', this);
4619 isVisible : function()
4622 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4626 addButton : function(str, cb)
4630 var b = Roo.apply({}, { html : str } );
4631 b.xns = b.xns || Roo.bootstrap;
4632 b.xtype = b.xtype || 'Button';
4633 if (typeof(b.listeners) == 'undefined') {
4634 b.listeners = { click : cb.createDelegate(this) };
4637 var btn = Roo.factory(b);
4639 btn.render(this.getButtonContainer());
4645 setDefaultButton : function(btn)
4647 //this.el.select('.modal-footer').()
4650 resizeTo: function(w,h)
4652 this.dialogEl.setWidth(w);
4654 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4656 this.bodyEl.setHeight(h - diff);
4658 this.fireEvent('resize', this);
4661 setContentSize : function(w, h)
4665 onButtonClick: function(btn,e)
4668 this.fireEvent('btnclick', btn.name, e);
4671 * Set the title of the Dialog
4672 * @param {String} str new Title
4674 setTitle: function(str) {
4675 this.titleEl.dom.innerHTML = str;
4679 * Set the body of the Dialog
4680 * @param {String} str new Title
4682 setBody: function(str) {
4683 this.bodyEl.dom.innerHTML = str;
4686 * Set the body of the Dialog using the template
4687 * @param {Obj} data - apply this data to the template and replace the body contents.
4689 applyBody: function(obj)
4692 Roo.log("Error - using apply Body without a template");
4695 this.tmpl.overwrite(this.bodyEl, obj);
4698 getChildHeight : function(child_nodes)
4702 child_nodes.length == 0
4707 var child_height = 0;
4709 for(var i = 0; i < child_nodes.length; i++) {
4712 * for modal with tabs...
4713 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4715 var layout_childs = child_nodes[i].childNodes;
4717 for(var j = 0; j < layout_childs.length; j++) {
4719 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4721 var layout_body_childs = layout_childs[j].childNodes;
4723 for(var k = 0; k < layout_body_childs.length; k++) {
4725 if(layout_body_childs[k].classList.contains('navbar')) {
4726 child_height += layout_body_childs[k].offsetHeight;
4730 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4732 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4734 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4736 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4737 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4752 child_height += child_nodes[i].offsetHeight;
4753 // Roo.log(child_nodes[i].offsetHeight);
4756 return child_height;
4758 toggleHeaderInput : function(is_edit)
4760 if (!this.editableTitle) {
4761 return; // not editable.
4763 if (is_edit && this.is_header_editing) {
4764 return; // already editing..
4768 this.headerEditEl.dom.value = this.title;
4769 this.headerEditEl.removeClass('d-none');
4770 this.headerEditEl.dom.focus();
4771 this.titleEl.addClass('d-none');
4773 this.is_header_editing = true;
4776 // flip back to not editing.
4777 this.title = this.headerEditEl.dom.value;
4778 this.headerEditEl.addClass('d-none');
4779 this.titleEl.removeClass('d-none');
4780 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4781 this.is_header_editing = false;
4782 this.fireEvent('titlechanged', this, this.title);
4791 Roo.apply(Roo.bootstrap.Modal, {
4793 * Button config that displays a single OK button
4802 * Button config that displays Yes and No buttons
4818 * Button config that displays OK and Cancel buttons
4833 * Button config that displays Yes, No and Cancel buttons
4858 * messagebox - can be used as a replace
4862 * @class Roo.MessageBox
4863 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4867 Roo.Msg.alert('Status', 'Changes saved successfully.');
4869 // Prompt for user data:
4870 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4872 // process text value...
4876 // Show a dialog using config options:
4878 title:'Save Changes?',
4879 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4880 buttons: Roo.Msg.YESNOCANCEL,
4887 Roo.bootstrap.MessageBox = function(){
4888 var dlg, opt, mask, waitTimer;
4889 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4890 var buttons, activeTextEl, bwidth;
4894 var handleButton = function(button){
4896 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4900 var handleHide = function(){
4902 dlg.el.removeClass(opt.cls);
4905 // Roo.TaskMgr.stop(waitTimer);
4906 // waitTimer = null;
4911 var updateButtons = function(b){
4914 buttons["ok"].hide();
4915 buttons["cancel"].hide();
4916 buttons["yes"].hide();
4917 buttons["no"].hide();
4918 dlg.footerEl.hide();
4922 dlg.footerEl.show();
4923 for(var k in buttons){
4924 if(typeof buttons[k] != "function"){
4927 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4928 width += buttons[k].el.getWidth()+15;
4938 var handleEsc = function(d, k, e){
4939 if(opt && opt.closable !== false){
4949 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4950 * @return {Roo.BasicDialog} The BasicDialog element
4952 getDialog : function(){
4954 dlg = new Roo.bootstrap.Modal( {
4957 //constraintoviewport:false,
4959 //collapsible : false,
4964 //buttonAlign:"center",
4965 closeClick : function(){
4966 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4969 handleButton("cancel");
4974 dlg.on("hide", handleHide);
4976 //dlg.addKeyListener(27, handleEsc);
4978 this.buttons = buttons;
4979 var bt = this.buttonText;
4980 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4981 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4982 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4983 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4985 bodyEl = dlg.bodyEl.createChild({
4987 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4988 '<textarea class="roo-mb-textarea"></textarea>' +
4989 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4991 msgEl = bodyEl.dom.firstChild;
4992 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4993 textboxEl.enableDisplayMode();
4994 textboxEl.addKeyListener([10,13], function(){
4995 if(dlg.isVisible() && opt && opt.buttons){
4998 }else if(opt.buttons.yes){
4999 handleButton("yes");
5003 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5004 textareaEl.enableDisplayMode();
5005 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5006 progressEl.enableDisplayMode();
5008 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5009 var pf = progressEl.dom.firstChild;
5011 pp = Roo.get(pf.firstChild);
5012 pp.setHeight(pf.offsetHeight);
5020 * Updates the message box body text
5021 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5022 * the XHTML-compliant non-breaking space character '&#160;')
5023 * @return {Roo.MessageBox} This message box
5025 updateText : function(text)
5027 if(!dlg.isVisible() && !opt.width){
5028 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5029 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5031 msgEl.innerHTML = text || ' ';
5033 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5034 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5036 Math.min(opt.width || cw , this.maxWidth),
5037 Math.max(opt.minWidth || this.minWidth, bwidth)
5040 activeTextEl.setWidth(w);
5042 if(dlg.isVisible()){
5043 dlg.fixedcenter = false;
5045 // to big, make it scroll. = But as usual stupid IE does not support
5048 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5049 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5050 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5052 bodyEl.dom.style.height = '';
5053 bodyEl.dom.style.overflowY = '';
5056 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5058 bodyEl.dom.style.overflowX = '';
5061 dlg.setContentSize(w, bodyEl.getHeight());
5062 if(dlg.isVisible()){
5063 dlg.fixedcenter = true;
5069 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5070 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5071 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5072 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5073 * @return {Roo.MessageBox} This message box
5075 updateProgress : function(value, text){
5077 this.updateText(text);
5080 if (pp) { // weird bug on my firefox - for some reason this is not defined
5081 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5082 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5088 * Returns true if the message box is currently displayed
5089 * @return {Boolean} True if the message box is visible, else false
5091 isVisible : function(){
5092 return dlg && dlg.isVisible();
5096 * Hides the message box if it is displayed
5099 if(this.isVisible()){
5105 * Displays a new message box, or reinitializes an existing message box, based on the config options
5106 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5107 * The following config object properties are supported:
5109 Property Type Description
5110 ---------- --------------- ------------------------------------------------------------------------------------
5111 animEl String/Element An id or Element from which the message box should animate as it opens and
5112 closes (defaults to undefined)
5113 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5114 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5115 closable Boolean False to hide the top-right close button (defaults to true). Note that
5116 progress and wait dialogs will ignore this property and always hide the
5117 close button as they can only be closed programmatically.
5118 cls String A custom CSS class to apply to the message box element
5119 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5120 displayed (defaults to 75)
5121 fn Function A callback function to execute after closing the dialog. The arguments to the
5122 function will be btn (the name of the button that was clicked, if applicable,
5123 e.g. "ok"), and text (the value of the active text field, if applicable).
5124 Progress and wait dialogs will ignore this option since they do not respond to
5125 user actions and can only be closed programmatically, so any required function
5126 should be called by the same code after it closes the dialog.
5127 icon String A CSS class that provides a background image to be used as an icon for
5128 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5129 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5130 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5131 modal Boolean False to allow user interaction with the page while the message box is
5132 displayed (defaults to true)
5133 msg String A string that will replace the existing message box body text (defaults
5134 to the XHTML-compliant non-breaking space character ' ')
5135 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5136 progress Boolean True to display a progress bar (defaults to false)
5137 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5138 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5139 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5140 title String The title text
5141 value String The string value to set into the active textbox element if displayed
5142 wait Boolean True to display a progress bar (defaults to false)
5143 width Number The width of the dialog in pixels
5150 msg: 'Please enter your address:',
5152 buttons: Roo.MessageBox.OKCANCEL,
5155 animEl: 'addAddressBtn'
5158 * @param {Object} config Configuration options
5159 * @return {Roo.MessageBox} This message box
5161 show : function(options)
5164 // this causes nightmares if you show one dialog after another
5165 // especially on callbacks..
5167 if(this.isVisible()){
5170 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5171 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5172 Roo.log("New Dialog Message:" + options.msg )
5173 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5174 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5177 var d = this.getDialog();
5179 d.setTitle(opt.title || " ");
5180 d.closeEl.setDisplayed(opt.closable !== false);
5181 activeTextEl = textboxEl;
5182 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5187 textareaEl.setHeight(typeof opt.multiline == "number" ?
5188 opt.multiline : this.defaultTextHeight);
5189 activeTextEl = textareaEl;
5198 progressEl.setDisplayed(opt.progress === true);
5200 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5202 this.updateProgress(0);
5203 activeTextEl.dom.value = opt.value || "";
5205 dlg.setDefaultButton(activeTextEl);
5207 var bs = opt.buttons;
5211 }else if(bs && bs.yes){
5212 db = buttons["yes"];
5214 dlg.setDefaultButton(db);
5216 bwidth = updateButtons(opt.buttons);
5217 this.updateText(opt.msg);
5219 d.el.addClass(opt.cls);
5221 d.proxyDrag = opt.proxyDrag === true;
5222 d.modal = opt.modal !== false;
5223 d.mask = opt.modal !== false ? mask : false;
5225 // force it to the end of the z-index stack so it gets a cursor in FF
5226 document.body.appendChild(dlg.el.dom);
5227 d.animateTarget = null;
5228 d.show(options.animEl);
5234 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5235 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5236 * and closing the message box when the process is complete.
5237 * @param {String} title The title bar text
5238 * @param {String} msg The message box body text
5239 * @return {Roo.MessageBox} This message box
5241 progress : function(title, msg){
5248 minWidth: this.minProgressWidth,
5255 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5256 * If a callback function is passed it will be called after the user clicks the button, and the
5257 * id of the button that was clicked will be passed as the only parameter to the callback
5258 * (could also be the top-right close button).
5259 * @param {String} title The title bar text
5260 * @param {String} msg The message box body text
5261 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5262 * @param {Object} scope (optional) The scope of the callback function
5263 * @return {Roo.MessageBox} This message box
5265 alert : function(title, msg, fn, scope)
5280 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5281 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5282 * You are responsible for closing the message box when the process is complete.
5283 * @param {String} msg The message box body text
5284 * @param {String} title (optional) The title bar text
5285 * @return {Roo.MessageBox} This message box
5287 wait : function(msg, title){
5298 waitTimer = Roo.TaskMgr.start({
5300 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5308 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5309 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5310 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5311 * @param {String} title The title bar text
5312 * @param {String} msg The message box body text
5313 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5314 * @param {Object} scope (optional) The scope of the callback function
5315 * @return {Roo.MessageBox} This message box
5317 confirm : function(title, msg, fn, scope){
5321 buttons: this.YESNO,
5330 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5331 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5332 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5333 * (could also be the top-right close button) and the text that was entered will be passed as the two
5334 * parameters to the callback.
5335 * @param {String} title The title bar text
5336 * @param {String} msg The message box body text
5337 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5338 * @param {Object} scope (optional) The scope of the callback function
5339 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5340 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5341 * @return {Roo.MessageBox} This message box
5343 prompt : function(title, msg, fn, scope, multiline){
5347 buttons: this.OKCANCEL,
5352 multiline: multiline,
5359 * Button config that displays a single OK button
5364 * Button config that displays Yes and No buttons
5367 YESNO : {yes:true, no:true},
5369 * Button config that displays OK and Cancel buttons
5372 OKCANCEL : {ok:true, cancel:true},
5374 * Button config that displays Yes, No and Cancel buttons
5377 YESNOCANCEL : {yes:true, no:true, cancel:true},
5380 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5383 defaultTextHeight : 75,
5385 * The maximum width in pixels of the message box (defaults to 600)
5390 * The minimum width in pixels of the message box (defaults to 100)
5395 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5396 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5399 minProgressWidth : 250,
5401 * An object containing the default button text strings that can be overriden for localized language support.
5402 * Supported properties are: ok, cancel, yes and no.
5403 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5416 * Shorthand for {@link Roo.MessageBox}
5418 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5419 Roo.Msg = Roo.Msg || Roo.MessageBox;
5428 * @class Roo.bootstrap.Navbar
5429 * @extends Roo.bootstrap.Component
5430 * Bootstrap Navbar class
5433 * Create a new Navbar
5434 * @param {Object} config The config object
5438 Roo.bootstrap.Navbar = function(config){
5439 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5443 * @event beforetoggle
5444 * Fire before toggle the menu
5445 * @param {Roo.EventObject} e
5447 "beforetoggle" : true
5451 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5460 getAutoCreate : function(){
5463 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5467 initEvents :function ()
5469 //Roo.log(this.el.select('.navbar-toggle',true));
5470 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5477 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5479 var size = this.el.getSize();
5480 this.maskEl.setSize(size.width, size.height);
5481 this.maskEl.enableDisplayMode("block");
5490 getChildContainer : function()
5492 if (this.el && this.el.select('.collapse').getCount()) {
5493 return this.el.select('.collapse',true).first();
5508 onToggle : function()
5511 if(this.fireEvent('beforetoggle', this) === false){
5514 var ce = this.el.select('.navbar-collapse',true).first();
5516 if (!ce.hasClass('show')) {
5526 * Expand the navbar pulldown
5528 expand : function ()
5531 var ce = this.el.select('.navbar-collapse',true).first();
5532 if (ce.hasClass('collapsing')) {
5535 ce.dom.style.height = '';
5537 ce.addClass('in'); // old...
5538 ce.removeClass('collapse');
5539 ce.addClass('show');
5540 var h = ce.getHeight();
5542 ce.removeClass('show');
5543 // at this point we should be able to see it..
5544 ce.addClass('collapsing');
5546 ce.setHeight(0); // resize it ...
5547 ce.on('transitionend', function() {
5548 //Roo.log('done transition');
5549 ce.removeClass('collapsing');
5550 ce.addClass('show');
5551 ce.removeClass('collapse');
5553 ce.dom.style.height = '';
5554 }, this, { single: true} );
5556 ce.dom.scrollTop = 0;
5559 * Collapse the navbar pulldown
5561 collapse : function()
5563 var ce = this.el.select('.navbar-collapse',true).first();
5565 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5566 // it's collapsed or collapsing..
5569 ce.removeClass('in'); // old...
5570 ce.setHeight(ce.getHeight());
5571 ce.removeClass('show');
5572 ce.addClass('collapsing');
5574 ce.on('transitionend', function() {
5575 ce.dom.style.height = '';
5576 ce.removeClass('collapsing');
5577 ce.addClass('collapse');
5578 }, this, { single: true} );
5598 * @class Roo.bootstrap.NavSimplebar
5599 * @extends Roo.bootstrap.Navbar
5600 * Bootstrap Sidebar class
5602 * @cfg {Boolean} inverse is inverted color
5604 * @cfg {String} type (nav | pills | tabs)
5605 * @cfg {Boolean} arrangement stacked | justified
5606 * @cfg {String} align (left | right) alignment
5608 * @cfg {Boolean} main (true|false) main nav bar? default false
5609 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5611 * @cfg {String} tag (header|footer|nav|div) default is nav
5613 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5617 * Create a new Sidebar
5618 * @param {Object} config The config object
5622 Roo.bootstrap.NavSimplebar = function(config){
5623 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5626 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5642 getAutoCreate : function(){
5646 tag : this.tag || 'div',
5647 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5649 if (['light','white'].indexOf(this.weight) > -1) {
5650 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5652 cfg.cls += ' bg-' + this.weight;
5655 cfg.cls += ' navbar-inverse';
5659 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5661 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5670 cls: 'nav nav-' + this.xtype,
5676 this.type = this.type || 'nav';
5677 if (['tabs','pills'].indexOf(this.type) != -1) {
5678 cfg.cn[0].cls += ' nav-' + this.type
5682 if (this.type!=='nav') {
5683 Roo.log('nav type must be nav/tabs/pills')
5685 cfg.cn[0].cls += ' navbar-nav'
5691 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5692 cfg.cn[0].cls += ' nav-' + this.arrangement;
5696 if (this.align === 'right') {
5697 cfg.cn[0].cls += ' navbar-right';
5722 * navbar-expand-md fixed-top
5726 * @class Roo.bootstrap.NavHeaderbar
5727 * @extends Roo.bootstrap.NavSimplebar
5728 * Bootstrap Sidebar class
5730 * @cfg {String} brand what is brand
5731 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5732 * @cfg {String} brand_href href of the brand
5733 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5734 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5735 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5736 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5739 * Create a new Sidebar
5740 * @param {Object} config The config object
5744 Roo.bootstrap.NavHeaderbar = function(config){
5745 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5749 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5756 desktopCenter : false,
5759 getAutoCreate : function(){
5762 tag: this.nav || 'nav',
5763 cls: 'navbar navbar-expand-md',
5769 if (this.desktopCenter) {
5770 cn.push({cls : 'container', cn : []});
5778 cls: 'navbar-toggle navbar-toggler',
5779 'data-toggle': 'collapse',
5784 html: 'Toggle navigation'
5788 cls: 'icon-bar navbar-toggler-icon'
5801 cn.push( Roo.bootstrap.version == 4 ? btn : {
5803 cls: 'navbar-header',
5812 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5816 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5818 if (['light','white'].indexOf(this.weight) > -1) {
5819 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5821 cfg.cls += ' bg-' + this.weight;
5824 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5825 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5827 // tag can override this..
5829 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5832 if (this.brand !== '') {
5833 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5834 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5836 href: this.brand_href ? this.brand_href : '#',
5837 cls: 'navbar-brand',
5845 cfg.cls += ' main-nav';
5853 getHeaderChildContainer : function()
5855 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5856 return this.el.select('.navbar-header',true).first();
5859 return this.getChildContainer();
5862 getChildContainer : function()
5865 return this.el.select('.roo-navbar-collapse',true).first();
5870 initEvents : function()
5872 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5874 if (this.autohide) {
5879 Roo.get(document).on('scroll',function(e) {
5880 var ns = Roo.get(document).getScroll().top;
5881 var os = prevScroll;
5885 ft.removeClass('slideDown');
5886 ft.addClass('slideUp');
5889 ft.removeClass('slideUp');
5890 ft.addClass('slideDown');
5911 * @class Roo.bootstrap.NavSidebar
5912 * @extends Roo.bootstrap.Navbar
5913 * Bootstrap Sidebar class
5916 * Create a new Sidebar
5917 * @param {Object} config The config object
5921 Roo.bootstrap.NavSidebar = function(config){
5922 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5925 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5927 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5929 getAutoCreate : function(){
5934 cls: 'sidebar sidebar-nav'
5956 * @class Roo.bootstrap.NavGroup
5957 * @extends Roo.bootstrap.Component
5958 * Bootstrap NavGroup class
5959 * @cfg {String} align (left|right)
5960 * @cfg {Boolean} inverse
5961 * @cfg {String} type (nav|pills|tab) default nav
5962 * @cfg {String} navId - reference Id for navbar.
5963 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5966 * Create a new nav group
5967 * @param {Object} config The config object
5970 Roo.bootstrap.NavGroup = function(config){
5971 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5974 Roo.bootstrap.NavGroup.register(this);
5978 * Fires when the active item changes
5979 * @param {Roo.bootstrap.NavGroup} this
5980 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5981 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5988 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6000 getAutoCreate : function()
6002 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6008 if (Roo.bootstrap.version == 4) {
6009 if (['tabs','pills'].indexOf(this.type) != -1) {
6010 cfg.cls += ' nav-' + this.type;
6012 // trying to remove so header bar can right align top?
6013 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6014 // do not use on header bar...
6015 cfg.cls += ' navbar-nav';
6020 if (['tabs','pills'].indexOf(this.type) != -1) {
6021 cfg.cls += ' nav-' + this.type
6023 if (this.type !== 'nav') {
6024 Roo.log('nav type must be nav/tabs/pills')
6026 cfg.cls += ' navbar-nav'
6030 if (this.parent() && this.parent().sidebar) {
6033 cls: 'dashboard-menu sidebar-menu'
6039 if (this.form === true) {
6042 cls: 'navbar-form form-inline'
6044 //nav navbar-right ml-md-auto
6045 if (this.align === 'right') {
6046 cfg.cls += ' navbar-right ml-md-auto';
6048 cfg.cls += ' navbar-left';
6052 if (this.align === 'right') {
6053 cfg.cls += ' navbar-right ml-md-auto';
6055 cfg.cls += ' mr-auto';
6059 cfg.cls += ' navbar-inverse';
6067 * sets the active Navigation item
6068 * @param {Roo.bootstrap.NavItem} the new current navitem
6070 setActiveItem : function(item)
6073 Roo.each(this.navItems, function(v){
6078 v.setActive(false, true);
6085 item.setActive(true, true);
6086 this.fireEvent('changed', this, item, prev);
6091 * gets the active Navigation item
6092 * @return {Roo.bootstrap.NavItem} the current navitem
6094 getActive : function()
6098 Roo.each(this.navItems, function(v){
6109 indexOfNav : function()
6113 Roo.each(this.navItems, function(v,i){
6124 * adds a Navigation item
6125 * @param {Roo.bootstrap.NavItem} the navitem to add
6127 addItem : function(cfg)
6129 if (this.form && Roo.bootstrap.version == 4) {
6132 var cn = new Roo.bootstrap.NavItem(cfg);
6134 cn.parentId = this.id;
6135 cn.onRender(this.el, null);
6139 * register a Navigation item
6140 * @param {Roo.bootstrap.NavItem} the navitem to add
6142 register : function(item)
6144 this.navItems.push( item);
6145 item.navId = this.navId;
6150 * clear all the Navigation item
6153 clearAll : function()
6156 this.el.dom.innerHTML = '';
6159 getNavItem: function(tabId)
6162 Roo.each(this.navItems, function(e) {
6163 if (e.tabId == tabId) {
6173 setActiveNext : function()
6175 var i = this.indexOfNav(this.getActive());
6176 if (i > this.navItems.length) {
6179 this.setActiveItem(this.navItems[i+1]);
6181 setActivePrev : function()
6183 var i = this.indexOfNav(this.getActive());
6187 this.setActiveItem(this.navItems[i-1]);
6189 clearWasActive : function(except) {
6190 Roo.each(this.navItems, function(e) {
6191 if (e.tabId != except.tabId && e.was_active) {
6192 e.was_active = false;
6199 getWasActive : function ()
6202 Roo.each(this.navItems, function(e) {
6217 Roo.apply(Roo.bootstrap.NavGroup, {
6221 * register a Navigation Group
6222 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6224 register : function(navgrp)
6226 this.groups[navgrp.navId] = navgrp;
6230 * fetch a Navigation Group based on the navigation ID
6231 * @param {string} the navgroup to add
6232 * @returns {Roo.bootstrap.NavGroup} the navgroup
6234 get: function(navId) {
6235 if (typeof(this.groups[navId]) == 'undefined') {
6237 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6239 return this.groups[navId] ;
6254 * @class Roo.bootstrap.NavItem
6255 * @extends Roo.bootstrap.Component
6256 * Bootstrap Navbar.NavItem class
6257 * @cfg {String} href link to
6258 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6259 * @cfg {Boolean} button_outline show and outlined button
6260 * @cfg {String} html content of button
6261 * @cfg {String} badge text inside badge
6262 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6263 * @cfg {String} glyphicon DEPRICATED - use fa
6264 * @cfg {String} icon DEPRICATED - use fa
6265 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6266 * @cfg {Boolean} active Is item active
6267 * @cfg {Boolean} disabled Is item disabled
6268 * @cfg {String} linkcls Link Class
6269 * @cfg {Boolean} preventDefault (true | false) default false
6270 * @cfg {String} tabId the tab that this item activates.
6271 * @cfg {String} tagtype (a|span) render as a href or span?
6272 * @cfg {Boolean} animateRef (true|false) link to element default false
6275 * Create a new Navbar Item
6276 * @param {Object} config The config object
6278 Roo.bootstrap.NavItem = function(config){
6279 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6284 * The raw click event for the entire grid.
6285 * @param {Roo.EventObject} e
6290 * Fires when the active item active state changes
6291 * @param {Roo.bootstrap.NavItem} this
6292 * @param {boolean} state the new state
6298 * Fires when scroll to element
6299 * @param {Roo.bootstrap.NavItem} this
6300 * @param {Object} options
6301 * @param {Roo.EventObject} e
6309 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6318 preventDefault : false,
6326 button_outline : false,
6330 getAutoCreate : function(){
6337 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6340 cfg.cls += ' active' ;
6342 if (this.disabled) {
6343 cfg.cls += ' disabled';
6347 if (this.button_weight.length) {
6348 cfg.tag = this.href ? 'a' : 'button';
6349 cfg.html = this.html || '';
6350 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6352 cfg.href = this.href;
6355 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6357 cfg.cls += " nav-html";
6360 // menu .. should add dropdown-menu class - so no need for carat..
6362 if (this.badge !== '') {
6364 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6369 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6373 href : this.href || "#",
6374 html: this.html || '',
6378 if (this.tagtype == 'a') {
6379 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6383 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6384 } else if (this.fa) {
6385 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6386 } else if(this.glyphicon) {
6387 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6389 cfg.cn[0].cls += " nav-html";
6393 cfg.cn[0].html += " <span class='caret'></span>";
6397 if (this.badge !== '') {
6398 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6406 onRender : function(ct, position)
6408 // Roo.log("Call onRender: " + this.xtype);
6409 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6413 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6414 this.navLink = this.el.select('.nav-link',true).first();
6415 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6420 initEvents: function()
6422 if (typeof (this.menu) != 'undefined') {
6423 this.menu.parentType = this.xtype;
6424 this.menu.triggerEl = this.el;
6425 this.menu = this.addxtype(Roo.apply({}, this.menu));
6428 this.el.on('click', this.onClick, this);
6430 //if(this.tagtype == 'span'){
6431 // this.el.select('span',true).on('click', this.onClick, this);
6434 // at this point parent should be available..
6435 this.parent().register(this);
6438 onClick : function(e)
6440 if (e.getTarget('.dropdown-menu-item')) {
6441 // did you click on a menu itemm.... - then don't trigger onclick..
6446 this.preventDefault ||
6449 Roo.log("NavItem - prevent Default?");
6453 if (this.disabled) {
6457 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6458 if (tg && tg.transition) {
6459 Roo.log("waiting for the transitionend");
6465 //Roo.log("fire event clicked");
6466 if(this.fireEvent('click', this, e) === false){
6470 if(this.tagtype == 'span'){
6474 //Roo.log(this.href);
6475 var ael = this.el.select('a',true).first();
6478 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6479 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6480 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6481 return; // ignore... - it's a 'hash' to another page.
6483 Roo.log("NavItem - prevent Default?");
6485 this.scrollToElement(e);
6489 var p = this.parent();
6491 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6492 if (typeof(p.setActiveItem) !== 'undefined') {
6493 p.setActiveItem(this);
6497 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6498 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6499 // remove the collapsed menu expand...
6500 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6504 isActive: function () {
6507 setActive : function(state, fire, is_was_active)
6509 if (this.active && !state && this.navId) {
6510 this.was_active = true;
6511 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6513 nv.clearWasActive(this);
6517 this.active = state;
6520 this.el.removeClass('active');
6521 this.navLink ? this.navLink.removeClass('active') : false;
6522 } else if (!this.el.hasClass('active')) {
6524 this.el.addClass('active');
6525 if (Roo.bootstrap.version == 4 && this.navLink ) {
6526 this.navLink.addClass('active');
6531 this.fireEvent('changed', this, state);
6534 // show a panel if it's registered and related..
6536 if (!this.navId || !this.tabId || !state || is_was_active) {
6540 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6544 var pan = tg.getPanelByName(this.tabId);
6548 // if we can not flip to new panel - go back to old nav highlight..
6549 if (false == tg.showPanel(pan)) {
6550 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6552 var onav = nv.getWasActive();
6554 onav.setActive(true, false, true);
6563 // this should not be here...
6564 setDisabled : function(state)
6566 this.disabled = state;
6568 this.el.removeClass('disabled');
6569 } else if (!this.el.hasClass('disabled')) {
6570 this.el.addClass('disabled');
6576 * Fetch the element to display the tooltip on.
6577 * @return {Roo.Element} defaults to this.el
6579 tooltipEl : function()
6581 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6584 scrollToElement : function(e)
6586 var c = document.body;
6589 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6591 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6592 c = document.documentElement;
6595 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6601 var o = target.calcOffsetsTo(c);
6608 this.fireEvent('scrollto', this, options, e);
6610 Roo.get(c).scrollTo('top', options.value, true);
6615 * Set the HTML (text content) of the item
6616 * @param {string} html content for the nav item
6618 setHtml : function(html)
6621 this.htmlEl.dom.innerHTML = html;
6633 * <span> icon </span>
6634 * <span> text </span>
6635 * <span>badge </span>
6639 * @class Roo.bootstrap.NavSidebarItem
6640 * @extends Roo.bootstrap.NavItem
6641 * Bootstrap Navbar.NavSidebarItem class
6642 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6643 * {Boolean} open is the menu open
6644 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6645 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6646 * {String} buttonSize (sm|md|lg)the extra classes for the button
6647 * {Boolean} showArrow show arrow next to the text (default true)
6649 * Create a new Navbar Button
6650 * @param {Object} config The config object
6652 Roo.bootstrap.NavSidebarItem = function(config){
6653 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6658 * The raw click event for the entire grid.
6659 * @param {Roo.EventObject} e
6664 * Fires when the active item active state changes
6665 * @param {Roo.bootstrap.NavSidebarItem} this
6666 * @param {boolean} state the new state
6674 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6676 badgeWeight : 'default',
6682 buttonWeight : 'default',
6688 getAutoCreate : function(){
6693 href : this.href || '#',
6699 if(this.buttonView){
6702 href : this.href || '#',
6703 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6716 cfg.cls += ' active';
6719 if (this.disabled) {
6720 cfg.cls += ' disabled';
6723 cfg.cls += ' open x-open';
6726 if (this.glyphicon || this.icon) {
6727 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6728 a.cn.push({ tag : 'i', cls : c }) ;
6731 if(!this.buttonView){
6734 html : this.html || ''
6741 if (this.badge !== '') {
6742 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6748 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6751 a.cls += ' dropdown-toggle treeview' ;
6757 initEvents : function()
6759 if (typeof (this.menu) != 'undefined') {
6760 this.menu.parentType = this.xtype;
6761 this.menu.triggerEl = this.el;
6762 this.menu = this.addxtype(Roo.apply({}, this.menu));
6765 this.el.on('click', this.onClick, this);
6767 if(this.badge !== ''){
6768 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6773 onClick : function(e)
6780 if(this.preventDefault){
6784 this.fireEvent('click', this, e);
6787 disable : function()
6789 this.setDisabled(true);
6794 this.setDisabled(false);
6797 setDisabled : function(state)
6799 if(this.disabled == state){
6803 this.disabled = state;
6806 this.el.addClass('disabled');
6810 this.el.removeClass('disabled');
6815 setActive : function(state)
6817 if(this.active == state){
6821 this.active = state;
6824 this.el.addClass('active');
6828 this.el.removeClass('active');
6833 isActive: function ()
6838 setBadge : function(str)
6844 this.badgeEl.dom.innerHTML = str;
6859 Roo.namespace('Roo.bootstrap.breadcrumb');
6863 * @class Roo.bootstrap.breadcrumb.Nav
6864 * @extends Roo.bootstrap.Component
6865 * Bootstrap Breadcrumb Nav Class
6867 * @children Roo.bootstrap.breadcrumb.Item
6870 * Create a new breadcrumb.Nav
6871 * @param {Object} config The config object
6875 Roo.bootstrap.breadcrumb.Nav = function(config){
6876 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6881 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6883 getAutoCreate : function()
6900 initEvents: function()
6902 this.olEl = this.el.select('ol',true).first();
6904 getChildContainer : function()
6920 * @class Roo.bootstrap.breadcrumb.Nav
6921 * @extends Roo.bootstrap.Component
6922 * Bootstrap Breadcrumb Nav Class
6924 * @children Roo.bootstrap.breadcrumb.Component
6925 * @cfg {String} html the content of the link.
6926 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6927 * @cfg {Boolean} active is it active
6931 * Create a new breadcrumb.Nav
6932 * @param {Object} config The config object
6935 Roo.bootstrap.breadcrumb.Item = function(config){
6936 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6941 * The img click event for the img.
6942 * @param {Roo.EventObject} e
6949 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6954 getAutoCreate : function()
6959 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6961 if (this.href !== false) {
6968 cfg.html = this.html;
6974 initEvents: function()
6977 this.el.select('a', true).first().on('click',this.onClick, this)
6981 onClick : function(e)
6984 this.fireEvent('click',this, e);
6997 * @class Roo.bootstrap.Row
6998 * @extends Roo.bootstrap.Component
6999 * Bootstrap Row class (contains columns...)
7003 * @param {Object} config The config object
7006 Roo.bootstrap.Row = function(config){
7007 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7010 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7012 getAutoCreate : function(){
7031 * @class Roo.bootstrap.Pagination
7032 * @extends Roo.bootstrap.Component
7033 * Bootstrap Pagination class
7034 * @cfg {String} size xs | sm | md | lg
7035 * @cfg {Boolean} inverse false | true
7038 * Create a new Pagination
7039 * @param {Object} config The config object
7042 Roo.bootstrap.Pagination = function(config){
7043 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7046 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7052 getAutoCreate : function(){
7058 cfg.cls += ' inverse';
7064 cfg.cls += " " + this.cls;
7082 * @class Roo.bootstrap.PaginationItem
7083 * @extends Roo.bootstrap.Component
7084 * Bootstrap PaginationItem class
7085 * @cfg {String} html text
7086 * @cfg {String} href the link
7087 * @cfg {Boolean} preventDefault (true | false) default true
7088 * @cfg {Boolean} active (true | false) default false
7089 * @cfg {Boolean} disabled default false
7093 * Create a new PaginationItem
7094 * @param {Object} config The config object
7098 Roo.bootstrap.PaginationItem = function(config){
7099 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7104 * The raw click event for the entire grid.
7105 * @param {Roo.EventObject} e
7111 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7115 preventDefault: true,
7120 getAutoCreate : function(){
7126 href : this.href ? this.href : '#',
7127 html : this.html ? this.html : ''
7137 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7141 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7147 initEvents: function() {
7149 this.el.on('click', this.onClick, this);
7152 onClick : function(e)
7154 Roo.log('PaginationItem on click ');
7155 if(this.preventDefault){
7163 this.fireEvent('click', this, e);
7179 * @class Roo.bootstrap.Slider
7180 * @extends Roo.bootstrap.Component
7181 * Bootstrap Slider class
7184 * Create a new Slider
7185 * @param {Object} config The config object
7188 Roo.bootstrap.Slider = function(config){
7189 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7192 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7194 getAutoCreate : function(){
7198 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7202 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7214 * Ext JS Library 1.1.1
7215 * Copyright(c) 2006-2007, Ext JS, LLC.
7217 * Originally Released Under LGPL - original licence link has changed is not relivant.
7220 * <script type="text/javascript">
7225 * @class Roo.grid.ColumnModel
7226 * @extends Roo.util.Observable
7227 * This is the default implementation of a ColumnModel used by the Grid. It defines
7228 * the columns in the grid.
7231 var colModel = new Roo.grid.ColumnModel([
7232 {header: "Ticker", width: 60, sortable: true, locked: true},
7233 {header: "Company Name", width: 150, sortable: true},
7234 {header: "Market Cap.", width: 100, sortable: true},
7235 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7236 {header: "Employees", width: 100, sortable: true, resizable: false}
7241 * The config options listed for this class are options which may appear in each
7242 * individual column definition.
7243 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7245 * @param {Object} config An Array of column config objects. See this class's
7246 * config objects for details.
7248 Roo.grid.ColumnModel = function(config){
7250 * The config passed into the constructor
7252 this.config = config;
7255 // if no id, create one
7256 // if the column does not have a dataIndex mapping,
7257 // map it to the order it is in the config
7258 for(var i = 0, len = config.length; i < len; i++){
7260 if(typeof c.dataIndex == "undefined"){
7263 if(typeof c.renderer == "string"){
7264 c.renderer = Roo.util.Format[c.renderer];
7266 if(typeof c.id == "undefined"){
7269 if(c.editor && c.editor.xtype){
7270 c.editor = Roo.factory(c.editor, Roo.grid);
7272 if(c.editor && c.editor.isFormField){
7273 c.editor = new Roo.grid.GridEditor(c.editor);
7275 this.lookup[c.id] = c;
7279 * The width of columns which have no width specified (defaults to 100)
7282 this.defaultWidth = 100;
7285 * Default sortable of columns which have no sortable specified (defaults to false)
7288 this.defaultSortable = false;
7292 * @event widthchange
7293 * Fires when the width of a column changes.
7294 * @param {ColumnModel} this
7295 * @param {Number} columnIndex The column index
7296 * @param {Number} newWidth The new width
7298 "widthchange": true,
7300 * @event headerchange
7301 * Fires when the text of a header changes.
7302 * @param {ColumnModel} this
7303 * @param {Number} columnIndex The column index
7304 * @param {Number} newText The new header text
7306 "headerchange": true,
7308 * @event hiddenchange
7309 * Fires when a column is hidden or "unhidden".
7310 * @param {ColumnModel} this
7311 * @param {Number} columnIndex The column index
7312 * @param {Boolean} hidden true if hidden, false otherwise
7314 "hiddenchange": true,
7316 * @event columnmoved
7317 * Fires when a column is moved.
7318 * @param {ColumnModel} this
7319 * @param {Number} oldIndex
7320 * @param {Number} newIndex
7322 "columnmoved" : true,
7324 * @event columlockchange
7325 * Fires when a column's locked state is changed
7326 * @param {ColumnModel} this
7327 * @param {Number} colIndex
7328 * @param {Boolean} locked true if locked
7330 "columnlockchange" : true
7332 Roo.grid.ColumnModel.superclass.constructor.call(this);
7334 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7336 * @cfg {String} header The header text to display in the Grid view.
7339 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7340 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7341 * specified, the column's index is used as an index into the Record's data Array.
7344 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7345 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7348 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7349 * Defaults to the value of the {@link #defaultSortable} property.
7350 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7353 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7356 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7359 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7362 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7365 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7366 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7367 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7368 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7371 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7374 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7377 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7380 * @cfg {String} cursor (Optional)
7383 * @cfg {String} tooltip (Optional)
7386 * @cfg {Number} xs (Optional)
7389 * @cfg {Number} sm (Optional)
7392 * @cfg {Number} md (Optional)
7395 * @cfg {Number} lg (Optional)
7398 * Returns the id of the column at the specified index.
7399 * @param {Number} index The column index
7400 * @return {String} the id
7402 getColumnId : function(index){
7403 return this.config[index].id;
7407 * Returns the column for a specified id.
7408 * @param {String} id The column id
7409 * @return {Object} the column
7411 getColumnById : function(id){
7412 return this.lookup[id];
7417 * Returns the column for a specified dataIndex.
7418 * @param {String} dataIndex The column dataIndex
7419 * @return {Object|Boolean} the column or false if not found
7421 getColumnByDataIndex: function(dataIndex){
7422 var index = this.findColumnIndex(dataIndex);
7423 return index > -1 ? this.config[index] : false;
7427 * Returns the index for a specified column id.
7428 * @param {String} id The column id
7429 * @return {Number} the index, or -1 if not found
7431 getIndexById : function(id){
7432 for(var i = 0, len = this.config.length; i < len; i++){
7433 if(this.config[i].id == id){
7441 * Returns the index for a specified column dataIndex.
7442 * @param {String} dataIndex The column dataIndex
7443 * @return {Number} the index, or -1 if not found
7446 findColumnIndex : function(dataIndex){
7447 for(var i = 0, len = this.config.length; i < len; i++){
7448 if(this.config[i].dataIndex == dataIndex){
7456 moveColumn : function(oldIndex, newIndex){
7457 var c = this.config[oldIndex];
7458 this.config.splice(oldIndex, 1);
7459 this.config.splice(newIndex, 0, c);
7460 this.dataMap = null;
7461 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7464 isLocked : function(colIndex){
7465 return this.config[colIndex].locked === true;
7468 setLocked : function(colIndex, value, suppressEvent){
7469 if(this.isLocked(colIndex) == value){
7472 this.config[colIndex].locked = value;
7474 this.fireEvent("columnlockchange", this, colIndex, value);
7478 getTotalLockedWidth : function(){
7480 for(var i = 0; i < this.config.length; i++){
7481 if(this.isLocked(i) && !this.isHidden(i)){
7482 this.totalWidth += this.getColumnWidth(i);
7488 getLockedCount : function(){
7489 for(var i = 0, len = this.config.length; i < len; i++){
7490 if(!this.isLocked(i)){
7495 return this.config.length;
7499 * Returns the number of columns.
7502 getColumnCount : function(visibleOnly){
7503 if(visibleOnly === true){
7505 for(var i = 0, len = this.config.length; i < len; i++){
7506 if(!this.isHidden(i)){
7512 return this.config.length;
7516 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7517 * @param {Function} fn
7518 * @param {Object} scope (optional)
7519 * @return {Array} result
7521 getColumnsBy : function(fn, scope){
7523 for(var i = 0, len = this.config.length; i < len; i++){
7524 var c = this.config[i];
7525 if(fn.call(scope||this, c, i) === true){
7533 * Returns true if the specified column is sortable.
7534 * @param {Number} col The column index
7537 isSortable : function(col){
7538 if(typeof this.config[col].sortable == "undefined"){
7539 return this.defaultSortable;
7541 return this.config[col].sortable;
7545 * Returns the rendering (formatting) function defined for the column.
7546 * @param {Number} col The column index.
7547 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7549 getRenderer : function(col){
7550 if(!this.config[col].renderer){
7551 return Roo.grid.ColumnModel.defaultRenderer;
7553 return this.config[col].renderer;
7557 * Sets the rendering (formatting) function for a column.
7558 * @param {Number} col The column index
7559 * @param {Function} fn The function to use to process the cell's raw data
7560 * to return HTML markup for the grid view. The render function is called with
7561 * the following parameters:<ul>
7562 * <li>Data value.</li>
7563 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7564 * <li>css A CSS style string to apply to the table cell.</li>
7565 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7566 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7567 * <li>Row index</li>
7568 * <li>Column index</li>
7569 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7571 setRenderer : function(col, fn){
7572 this.config[col].renderer = fn;
7576 * Returns the width for the specified column.
7577 * @param {Number} col The column index
7580 getColumnWidth : function(col){
7581 return this.config[col].width * 1 || this.defaultWidth;
7585 * Sets the width for a column.
7586 * @param {Number} col The column index
7587 * @param {Number} width The new width
7589 setColumnWidth : function(col, width, suppressEvent){
7590 this.config[col].width = width;
7591 this.totalWidth = null;
7593 this.fireEvent("widthchange", this, col, width);
7598 * Returns the total width of all columns.
7599 * @param {Boolean} includeHidden True to include hidden column widths
7602 getTotalWidth : function(includeHidden){
7603 if(!this.totalWidth){
7604 this.totalWidth = 0;
7605 for(var i = 0, len = this.config.length; i < len; i++){
7606 if(includeHidden || !this.isHidden(i)){
7607 this.totalWidth += this.getColumnWidth(i);
7611 return this.totalWidth;
7615 * Returns the header for the specified column.
7616 * @param {Number} col The column index
7619 getColumnHeader : function(col){
7620 return this.config[col].header;
7624 * Sets the header for a column.
7625 * @param {Number} col The column index
7626 * @param {String} header The new header
7628 setColumnHeader : function(col, header){
7629 this.config[col].header = header;
7630 this.fireEvent("headerchange", this, col, header);
7634 * Returns the tooltip for the specified column.
7635 * @param {Number} col The column index
7638 getColumnTooltip : function(col){
7639 return this.config[col].tooltip;
7642 * Sets the tooltip for a column.
7643 * @param {Number} col The column index
7644 * @param {String} tooltip The new tooltip
7646 setColumnTooltip : function(col, tooltip){
7647 this.config[col].tooltip = tooltip;
7651 * Returns the dataIndex for the specified column.
7652 * @param {Number} col The column index
7655 getDataIndex : function(col){
7656 return this.config[col].dataIndex;
7660 * Sets the dataIndex for a column.
7661 * @param {Number} col The column index
7662 * @param {Number} dataIndex The new dataIndex
7664 setDataIndex : function(col, dataIndex){
7665 this.config[col].dataIndex = dataIndex;
7671 * Returns true if the cell is editable.
7672 * @param {Number} colIndex The column index
7673 * @param {Number} rowIndex The row index - this is nto actually used..?
7676 isCellEditable : function(colIndex, rowIndex){
7677 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7681 * Returns the editor defined for the cell/column.
7682 * return false or null to disable editing.
7683 * @param {Number} colIndex The column index
7684 * @param {Number} rowIndex The row index
7687 getCellEditor : function(colIndex, rowIndex){
7688 return this.config[colIndex].editor;
7692 * Sets if a column is editable.
7693 * @param {Number} col The column index
7694 * @param {Boolean} editable True if the column is editable
7696 setEditable : function(col, editable){
7697 this.config[col].editable = editable;
7702 * Returns true if the column is hidden.
7703 * @param {Number} colIndex The column index
7706 isHidden : function(colIndex){
7707 return this.config[colIndex].hidden;
7712 * Returns true if the column width cannot be changed
7714 isFixed : function(colIndex){
7715 return this.config[colIndex].fixed;
7719 * Returns true if the column can be resized
7722 isResizable : function(colIndex){
7723 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7726 * Sets if a column is hidden.
7727 * @param {Number} colIndex The column index
7728 * @param {Boolean} hidden True if the column is hidden
7730 setHidden : function(colIndex, hidden){
7731 this.config[colIndex].hidden = hidden;
7732 this.totalWidth = null;
7733 this.fireEvent("hiddenchange", this, colIndex, hidden);
7737 * Sets the editor for a column.
7738 * @param {Number} col The column index
7739 * @param {Object} editor The editor object
7741 setEditor : function(col, editor){
7742 this.config[col].editor = editor;
7746 Roo.grid.ColumnModel.defaultRenderer = function(value)
7748 if(typeof value == "object") {
7751 if(typeof value == "string" && value.length < 1){
7755 return String.format("{0}", value);
7758 // Alias for backwards compatibility
7759 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7762 * Ext JS Library 1.1.1
7763 * Copyright(c) 2006-2007, Ext JS, LLC.
7765 * Originally Released Under LGPL - original licence link has changed is not relivant.
7768 * <script type="text/javascript">
7772 * @class Roo.LoadMask
7773 * A simple utility class for generically masking elements while loading data. If the element being masked has
7774 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7775 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7776 * element's UpdateManager load indicator and will be destroyed after the initial load.
7778 * Create a new LoadMask
7779 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7780 * @param {Object} config The config object
7782 Roo.LoadMask = function(el, config){
7783 this.el = Roo.get(el);
7784 Roo.apply(this, config);
7786 this.store.on('beforeload', this.onBeforeLoad, this);
7787 this.store.on('load', this.onLoad, this);
7788 this.store.on('loadexception', this.onLoadException, this);
7789 this.removeMask = false;
7791 var um = this.el.getUpdateManager();
7792 um.showLoadIndicator = false; // disable the default indicator
7793 um.on('beforeupdate', this.onBeforeLoad, this);
7794 um.on('update', this.onLoad, this);
7795 um.on('failure', this.onLoad, this);
7796 this.removeMask = true;
7800 Roo.LoadMask.prototype = {
7802 * @cfg {Boolean} removeMask
7803 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7804 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7808 * The text to display in a centered loading message box (defaults to 'Loading...')
7812 * @cfg {String} msgCls
7813 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7815 msgCls : 'x-mask-loading',
7818 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7824 * Disables the mask to prevent it from being displayed
7826 disable : function(){
7827 this.disabled = true;
7831 * Enables the mask so that it can be displayed
7833 enable : function(){
7834 this.disabled = false;
7837 onLoadException : function()
7841 if (typeof(arguments[3]) != 'undefined') {
7842 Roo.MessageBox.alert("Error loading",arguments[3]);
7846 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7847 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7854 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7859 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7863 onBeforeLoad : function(){
7865 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7870 destroy : function(){
7872 this.store.un('beforeload', this.onBeforeLoad, this);
7873 this.store.un('load', this.onLoad, this);
7874 this.store.un('loadexception', this.onLoadException, this);
7876 var um = this.el.getUpdateManager();
7877 um.un('beforeupdate', this.onBeforeLoad, this);
7878 um.un('update', this.onLoad, this);
7879 um.un('failure', this.onLoad, this);
7890 * @class Roo.bootstrap.Table
7891 * @extends Roo.bootstrap.Component
7892 * Bootstrap Table class
7893 * @cfg {String} cls table class
7894 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7895 * @cfg {String} bgcolor Specifies the background color for a table
7896 * @cfg {Number} border Specifies whether the table cells should have borders or not
7897 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7898 * @cfg {Number} cellspacing Specifies the space between cells
7899 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7900 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7901 * @cfg {String} sortable Specifies that the table should be sortable
7902 * @cfg {String} summary Specifies a summary of the content of a table
7903 * @cfg {Number} width Specifies the width of a table
7904 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7906 * @cfg {boolean} striped Should the rows be alternative striped
7907 * @cfg {boolean} bordered Add borders to the table
7908 * @cfg {boolean} hover Add hover highlighting
7909 * @cfg {boolean} condensed Format condensed
7910 * @cfg {boolean} responsive Format condensed
7911 * @cfg {Boolean} loadMask (true|false) default false
7912 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7913 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7914 * @cfg {Boolean} rowSelection (true|false) default false
7915 * @cfg {Boolean} cellSelection (true|false) default false
7916 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7917 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7918 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7919 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7923 * Create a new Table
7924 * @param {Object} config The config object
7927 Roo.bootstrap.Table = function(config){
7928 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7933 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7934 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7935 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7936 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7938 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7940 this.sm.grid = this;
7941 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7942 this.sm = this.selModel;
7943 this.sm.xmodule = this.xmodule || false;
7946 if (this.cm && typeof(this.cm.config) == 'undefined') {
7947 this.colModel = new Roo.grid.ColumnModel(this.cm);
7948 this.cm = this.colModel;
7949 this.cm.xmodule = this.xmodule || false;
7952 this.store= Roo.factory(this.store, Roo.data);
7953 this.ds = this.store;
7954 this.ds.xmodule = this.xmodule || false;
7957 if (this.footer && this.store) {
7958 this.footer.dataSource = this.ds;
7959 this.footer = Roo.factory(this.footer);
7966 * Fires when a cell is clicked
7967 * @param {Roo.bootstrap.Table} this
7968 * @param {Roo.Element} el
7969 * @param {Number} rowIndex
7970 * @param {Number} columnIndex
7971 * @param {Roo.EventObject} e
7975 * @event celldblclick
7976 * Fires when a cell is double clicked
7977 * @param {Roo.bootstrap.Table} this
7978 * @param {Roo.Element} el
7979 * @param {Number} rowIndex
7980 * @param {Number} columnIndex
7981 * @param {Roo.EventObject} e
7983 "celldblclick" : true,
7986 * Fires when a row is clicked
7987 * @param {Roo.bootstrap.Table} this
7988 * @param {Roo.Element} el
7989 * @param {Number} rowIndex
7990 * @param {Roo.EventObject} e
7994 * @event rowdblclick
7995 * Fires when a row is double clicked
7996 * @param {Roo.bootstrap.Table} this
7997 * @param {Roo.Element} el
7998 * @param {Number} rowIndex
7999 * @param {Roo.EventObject} e
8001 "rowdblclick" : true,
8004 * Fires when a mouseover occur
8005 * @param {Roo.bootstrap.Table} this
8006 * @param {Roo.Element} el
8007 * @param {Number} rowIndex
8008 * @param {Number} columnIndex
8009 * @param {Roo.EventObject} e
8014 * Fires when a mouseout occur
8015 * @param {Roo.bootstrap.Table} this
8016 * @param {Roo.Element} el
8017 * @param {Number} rowIndex
8018 * @param {Number} columnIndex
8019 * @param {Roo.EventObject} e
8024 * Fires when a row is rendered, so you can change add a style to it.
8025 * @param {Roo.bootstrap.Table} this
8026 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8030 * @event rowsrendered
8031 * Fires when all the rows have been rendered
8032 * @param {Roo.bootstrap.Table} this
8034 'rowsrendered' : true,
8036 * @event contextmenu
8037 * The raw contextmenu event for the entire grid.
8038 * @param {Roo.EventObject} e
8040 "contextmenu" : true,
8042 * @event rowcontextmenu
8043 * Fires when a row is right clicked
8044 * @param {Roo.bootstrap.Table} this
8045 * @param {Number} rowIndex
8046 * @param {Roo.EventObject} e
8048 "rowcontextmenu" : true,
8050 * @event cellcontextmenu
8051 * Fires when a cell is right clicked
8052 * @param {Roo.bootstrap.Table} this
8053 * @param {Number} rowIndex
8054 * @param {Number} cellIndex
8055 * @param {Roo.EventObject} e
8057 "cellcontextmenu" : true,
8059 * @event headercontextmenu
8060 * Fires when a header is right clicked
8061 * @param {Roo.bootstrap.Table} this
8062 * @param {Number} columnIndex
8063 * @param {Roo.EventObject} e
8065 "headercontextmenu" : true
8069 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8095 rowSelection : false,
8096 cellSelection : false,
8099 // Roo.Element - the tbody
8101 // Roo.Element - thead element
8104 container: false, // used by gridpanel...
8110 auto_hide_footer : false,
8112 getAutoCreate : function()
8114 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8121 if (this.scrollBody) {
8122 cfg.cls += ' table-body-fixed';
8125 cfg.cls += ' table-striped';
8129 cfg.cls += ' table-hover';
8131 if (this.bordered) {
8132 cfg.cls += ' table-bordered';
8134 if (this.condensed) {
8135 cfg.cls += ' table-condensed';
8137 if (this.responsive) {
8138 cfg.cls += ' table-responsive';
8142 cfg.cls+= ' ' +this.cls;
8145 // this lot should be simplifed...
8158 ].forEach(function(k) {
8166 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8169 if(this.store || this.cm){
8170 if(this.headerShow){
8171 cfg.cn.push(this.renderHeader());
8174 cfg.cn.push(this.renderBody());
8176 if(this.footerShow){
8177 cfg.cn.push(this.renderFooter());
8179 // where does this come from?
8180 //cfg.cls+= ' TableGrid';
8183 return { cn : [ cfg ] };
8186 initEvents : function()
8188 if(!this.store || !this.cm){
8191 if (this.selModel) {
8192 this.selModel.initEvents();
8196 //Roo.log('initEvents with ds!!!!');
8198 this.mainBody = this.el.select('tbody', true).first();
8199 this.mainHead = this.el.select('thead', true).first();
8200 this.mainFoot = this.el.select('tfoot', true).first();
8206 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8207 e.on('click', _this.sort, _this);
8210 this.mainBody.on("click", this.onClick, this);
8211 this.mainBody.on("dblclick", this.onDblClick, this);
8213 // why is this done????? = it breaks dialogs??
8214 //this.parent().el.setStyle('position', 'relative');
8218 this.footer.parentId = this.id;
8219 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8222 this.el.select('tfoot tr td').first().addClass('hide');
8227 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8230 this.store.on('load', this.onLoad, this);
8231 this.store.on('beforeload', this.onBeforeLoad, this);
8232 this.store.on('update', this.onUpdate, this);
8233 this.store.on('add', this.onAdd, this);
8234 this.store.on("clear", this.clear, this);
8236 this.el.on("contextmenu", this.onContextMenu, this);
8238 this.mainBody.on('scroll', this.onBodyScroll, this);
8240 this.cm.on("headerchange", this.onHeaderChange, this);
8242 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8246 onContextMenu : function(e, t)
8248 this.processEvent("contextmenu", e);
8251 processEvent : function(name, e)
8253 if (name != 'touchstart' ) {
8254 this.fireEvent(name, e);
8257 var t = e.getTarget();
8259 var cell = Roo.get(t);
8265 if(cell.findParent('tfoot', false, true)){
8269 if(cell.findParent('thead', false, true)){
8271 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8272 cell = Roo.get(t).findParent('th', false, true);
8274 Roo.log("failed to find th in thead?");
8275 Roo.log(e.getTarget());
8280 var cellIndex = cell.dom.cellIndex;
8282 var ename = name == 'touchstart' ? 'click' : name;
8283 this.fireEvent("header" + ename, this, cellIndex, e);
8288 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8289 cell = Roo.get(t).findParent('td', false, true);
8291 Roo.log("failed to find th in tbody?");
8292 Roo.log(e.getTarget());
8297 var row = cell.findParent('tr', false, true);
8298 var cellIndex = cell.dom.cellIndex;
8299 var rowIndex = row.dom.rowIndex - 1;
8303 this.fireEvent("row" + name, this, rowIndex, e);
8307 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8313 onMouseover : function(e, el)
8315 var cell = Roo.get(el);
8321 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8322 cell = cell.findParent('td', false, true);
8325 var row = cell.findParent('tr', false, true);
8326 var cellIndex = cell.dom.cellIndex;
8327 var rowIndex = row.dom.rowIndex - 1; // start from 0
8329 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8333 onMouseout : function(e, el)
8335 var cell = Roo.get(el);
8341 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8342 cell = cell.findParent('td', false, true);
8345 var row = cell.findParent('tr', false, true);
8346 var cellIndex = cell.dom.cellIndex;
8347 var rowIndex = row.dom.rowIndex - 1; // start from 0
8349 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8353 onClick : function(e, el)
8355 var cell = Roo.get(el);
8357 if(!cell || (!this.cellSelection && !this.rowSelection)){
8361 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8362 cell = cell.findParent('td', false, true);
8365 if(!cell || typeof(cell) == 'undefined'){
8369 var row = cell.findParent('tr', false, true);
8371 if(!row || typeof(row) == 'undefined'){
8375 var cellIndex = cell.dom.cellIndex;
8376 var rowIndex = this.getRowIndex(row);
8378 // why??? - should these not be based on SelectionModel?
8379 if(this.cellSelection){
8380 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8383 if(this.rowSelection){
8384 this.fireEvent('rowclick', this, row, rowIndex, e);
8390 onDblClick : function(e,el)
8392 var cell = Roo.get(el);
8394 if(!cell || (!this.cellSelection && !this.rowSelection)){
8398 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8399 cell = cell.findParent('td', false, true);
8402 if(!cell || typeof(cell) == 'undefined'){
8406 var row = cell.findParent('tr', false, true);
8408 if(!row || typeof(row) == 'undefined'){
8412 var cellIndex = cell.dom.cellIndex;
8413 var rowIndex = this.getRowIndex(row);
8415 if(this.cellSelection){
8416 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8419 if(this.rowSelection){
8420 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8424 sort : function(e,el)
8426 var col = Roo.get(el);
8428 if(!col.hasClass('sortable')){
8432 var sort = col.attr('sort');
8435 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8439 this.store.sortInfo = {field : sort, direction : dir};
8442 Roo.log("calling footer first");
8443 this.footer.onClick('first');
8446 this.store.load({ params : { start : 0 } });
8450 renderHeader : function()
8458 this.totalWidth = 0;
8460 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8462 var config = cm.config[i];
8466 cls : 'x-hcol-' + i,
8468 html: cm.getColumnHeader(i)
8473 if(typeof(config.sortable) != 'undefined' && config.sortable){
8475 c.html = '<i class="glyphicon"></i>' + c.html;
8478 // could use BS4 hidden-..-down
8480 if(typeof(config.lgHeader) != 'undefined'){
8481 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8484 if(typeof(config.mdHeader) != 'undefined'){
8485 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8488 if(typeof(config.smHeader) != 'undefined'){
8489 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8492 if(typeof(config.xsHeader) != 'undefined'){
8493 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8500 if(typeof(config.tooltip) != 'undefined'){
8501 c.tooltip = config.tooltip;
8504 if(typeof(config.colspan) != 'undefined'){
8505 c.colspan = config.colspan;
8508 if(typeof(config.hidden) != 'undefined' && config.hidden){
8509 c.style += ' display:none;';
8512 if(typeof(config.dataIndex) != 'undefined'){
8513 c.sort = config.dataIndex;
8518 if(typeof(config.align) != 'undefined' && config.align.length){
8519 c.style += ' text-align:' + config.align + ';';
8522 if(typeof(config.width) != 'undefined'){
8523 c.style += ' width:' + config.width + 'px;';
8524 this.totalWidth += config.width;
8526 this.totalWidth += 100; // assume minimum of 100 per column?
8529 if(typeof(config.cls) != 'undefined'){
8530 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8533 ['xs','sm','md','lg'].map(function(size){
8535 if(typeof(config[size]) == 'undefined'){
8539 if (!config[size]) { // 0 = hidden
8540 // BS 4 '0' is treated as hide that column and below.
8541 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8545 c.cls += ' col-' + size + '-' + config[size] + (
8546 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8558 renderBody : function()
8568 colspan : this.cm.getColumnCount()
8578 renderFooter : function()
8588 colspan : this.cm.getColumnCount()
8602 // Roo.log('ds onload');
8607 var ds = this.store;
8609 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8610 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8611 if (_this.store.sortInfo) {
8613 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8614 e.select('i', true).addClass(['glyphicon-arrow-up']);
8617 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8618 e.select('i', true).addClass(['glyphicon-arrow-down']);
8623 var tbody = this.mainBody;
8625 if(ds.getCount() > 0){
8626 ds.data.each(function(d,rowIndex){
8627 var row = this.renderRow(cm, ds, rowIndex);
8629 tbody.createChild(row);
8633 if(row.cellObjects.length){
8634 Roo.each(row.cellObjects, function(r){
8635 _this.renderCellObject(r);
8642 var tfoot = this.el.select('tfoot', true).first();
8644 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8646 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8648 var total = this.ds.getTotalCount();
8650 if(this.footer.pageSize < total){
8651 this.mainFoot.show();
8655 Roo.each(this.el.select('tbody td', true).elements, function(e){
8656 e.on('mouseover', _this.onMouseover, _this);
8659 Roo.each(this.el.select('tbody td', true).elements, function(e){
8660 e.on('mouseout', _this.onMouseout, _this);
8662 this.fireEvent('rowsrendered', this);
8668 onUpdate : function(ds,record)
8670 this.refreshRow(record);
8674 onRemove : function(ds, record, index, isUpdate){
8675 if(isUpdate !== true){
8676 this.fireEvent("beforerowremoved", this, index, record);
8678 var bt = this.mainBody.dom;
8680 var rows = this.el.select('tbody > tr', true).elements;
8682 if(typeof(rows[index]) != 'undefined'){
8683 bt.removeChild(rows[index].dom);
8686 // if(bt.rows[index]){
8687 // bt.removeChild(bt.rows[index]);
8690 if(isUpdate !== true){
8691 //this.stripeRows(index);
8692 //this.syncRowHeights(index, index);
8694 this.fireEvent("rowremoved", this, index, record);
8698 onAdd : function(ds, records, rowIndex)
8700 //Roo.log('on Add called');
8701 // - note this does not handle multiple adding very well..
8702 var bt = this.mainBody.dom;
8703 for (var i =0 ; i < records.length;i++) {
8704 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8705 //Roo.log(records[i]);
8706 //Roo.log(this.store.getAt(rowIndex+i));
8707 this.insertRow(this.store, rowIndex + i, false);
8714 refreshRow : function(record){
8715 var ds = this.store, index;
8716 if(typeof record == 'number'){
8718 record = ds.getAt(index);
8720 index = ds.indexOf(record);
8722 return; // should not happen - but seems to
8725 this.insertRow(ds, index, true);
8727 this.onRemove(ds, record, index+1, true);
8729 //this.syncRowHeights(index, index);
8731 this.fireEvent("rowupdated", this, index, record);
8734 insertRow : function(dm, rowIndex, isUpdate){
8737 this.fireEvent("beforerowsinserted", this, rowIndex);
8739 //var s = this.getScrollState();
8740 var row = this.renderRow(this.cm, this.store, rowIndex);
8741 // insert before rowIndex..
8742 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8746 if(row.cellObjects.length){
8747 Roo.each(row.cellObjects, function(r){
8748 _this.renderCellObject(r);
8753 this.fireEvent("rowsinserted", this, rowIndex);
8754 //this.syncRowHeights(firstRow, lastRow);
8755 //this.stripeRows(firstRow);
8762 getRowDom : function(rowIndex)
8764 var rows = this.el.select('tbody > tr', true).elements;
8766 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8769 // returns the object tree for a tr..
8772 renderRow : function(cm, ds, rowIndex)
8774 var d = ds.getAt(rowIndex);
8778 cls : 'x-row-' + rowIndex,
8782 var cellObjects = [];
8784 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8785 var config = cm.config[i];
8787 var renderer = cm.getRenderer(i);
8791 if(typeof(renderer) !== 'undefined'){
8792 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8794 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8795 // and are rendered into the cells after the row is rendered - using the id for the element.
8797 if(typeof(value) === 'object'){
8807 rowIndex : rowIndex,
8812 this.fireEvent('rowclass', this, rowcfg);
8816 cls : rowcfg.rowClass + ' x-col-' + i,
8818 html: (typeof(value) === 'object') ? '' : value
8825 if(typeof(config.colspan) != 'undefined'){
8826 td.colspan = config.colspan;
8829 if(typeof(config.hidden) != 'undefined' && config.hidden){
8830 td.style += ' display:none;';
8833 if(typeof(config.align) != 'undefined' && config.align.length){
8834 td.style += ' text-align:' + config.align + ';';
8836 if(typeof(config.valign) != 'undefined' && config.valign.length){
8837 td.style += ' vertical-align:' + config.valign + ';';
8840 if(typeof(config.width) != 'undefined'){
8841 td.style += ' width:' + config.width + 'px;';
8844 if(typeof(config.cursor) != 'undefined'){
8845 td.style += ' cursor:' + config.cursor + ';';
8848 if(typeof(config.cls) != 'undefined'){
8849 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8852 ['xs','sm','md','lg'].map(function(size){
8854 if(typeof(config[size]) == 'undefined'){
8860 if (!config[size]) { // 0 = hidden
8861 // BS 4 '0' is treated as hide that column and below.
8862 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8866 td.cls += ' col-' + size + '-' + config[size] + (
8867 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8877 row.cellObjects = cellObjects;
8885 onBeforeLoad : function()
8894 this.el.select('tbody', true).first().dom.innerHTML = '';
8897 * Show or hide a row.
8898 * @param {Number} rowIndex to show or hide
8899 * @param {Boolean} state hide
8901 setRowVisibility : function(rowIndex, state)
8903 var bt = this.mainBody.dom;
8905 var rows = this.el.select('tbody > tr', true).elements;
8907 if(typeof(rows[rowIndex]) == 'undefined'){
8910 rows[rowIndex].dom.style.display = state ? '' : 'none';
8914 getSelectionModel : function(){
8916 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8918 return this.selModel;
8921 * Render the Roo.bootstrap object from renderder
8923 renderCellObject : function(r)
8927 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8929 var t = r.cfg.render(r.container);
8932 Roo.each(r.cfg.cn, function(c){
8934 container: t.getChildContainer(),
8937 _this.renderCellObject(child);
8942 getRowIndex : function(row)
8946 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8957 * Returns the grid's underlying element = used by panel.Grid
8958 * @return {Element} The element
8960 getGridEl : function(){
8964 * Forces a resize - used by panel.Grid
8965 * @return {Element} The element
8967 autoSize : function()
8969 //var ctr = Roo.get(this.container.dom.parentElement);
8970 var ctr = Roo.get(this.el.dom);
8972 var thd = this.getGridEl().select('thead',true).first();
8973 var tbd = this.getGridEl().select('tbody', true).first();
8974 var tfd = this.getGridEl().select('tfoot', true).first();
8976 var cw = ctr.getWidth();
8977 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8981 tbd.setWidth(ctr.getWidth());
8982 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8983 // this needs fixing for various usage - currently only hydra job advers I think..
8985 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8987 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8990 cw = Math.max(cw, this.totalWidth);
8991 this.getGridEl().select('tbody tr',true).setWidth(cw);
8993 // resize 'expandable coloumn?
8995 return; // we doe not have a view in this design..
8998 onBodyScroll: function()
9000 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9002 this.mainHead.setStyle({
9003 'position' : 'relative',
9004 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9010 var scrollHeight = this.mainBody.dom.scrollHeight;
9012 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9014 var height = this.mainBody.getHeight();
9016 if(scrollHeight - height == scrollTop) {
9018 var total = this.ds.getTotalCount();
9020 if(this.footer.cursor + this.footer.pageSize < total){
9022 this.footer.ds.load({
9024 start : this.footer.cursor + this.footer.pageSize,
9025 limit : this.footer.pageSize
9035 onHeaderChange : function()
9037 var header = this.renderHeader();
9038 var table = this.el.select('table', true).first();
9040 this.mainHead.remove();
9041 this.mainHead = table.createChild(header, this.mainBody, false);
9044 onHiddenChange : function(colModel, colIndex, hidden)
9046 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9047 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9049 this.CSS.updateRule(thSelector, "display", "");
9050 this.CSS.updateRule(tdSelector, "display", "");
9053 this.CSS.updateRule(thSelector, "display", "none");
9054 this.CSS.updateRule(tdSelector, "display", "none");
9057 this.onHeaderChange();
9061 setColumnWidth: function(col_index, width)
9063 // width = "md-2 xs-2..."
9064 if(!this.colModel.config[col_index]) {
9068 var w = width.split(" ");
9070 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9072 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9075 for(var j = 0; j < w.length; j++) {
9081 var size_cls = w[j].split("-");
9083 if(!Number.isInteger(size_cls[1] * 1)) {
9087 if(!this.colModel.config[col_index][size_cls[0]]) {
9091 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9095 h_row[0].classList.replace(
9096 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9097 "col-"+size_cls[0]+"-"+size_cls[1]
9100 for(var i = 0; i < rows.length; i++) {
9102 var size_cls = w[j].split("-");
9104 if(!Number.isInteger(size_cls[1] * 1)) {
9108 if(!this.colModel.config[col_index][size_cls[0]]) {
9112 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9116 rows[i].classList.replace(
9117 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9118 "col-"+size_cls[0]+"-"+size_cls[1]
9122 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9137 * @class Roo.bootstrap.TableCell
9138 * @extends Roo.bootstrap.Component
9139 * Bootstrap TableCell class
9140 * @cfg {String} html cell contain text
9141 * @cfg {String} cls cell class
9142 * @cfg {String} tag cell tag (td|th) default td
9143 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9144 * @cfg {String} align Aligns the content in a cell
9145 * @cfg {String} axis Categorizes cells
9146 * @cfg {String} bgcolor Specifies the background color of a cell
9147 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9148 * @cfg {Number} colspan Specifies the number of columns a cell should span
9149 * @cfg {String} headers Specifies one or more header cells a cell is related to
9150 * @cfg {Number} height Sets the height of a cell
9151 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9152 * @cfg {Number} rowspan Sets the number of rows a cell should span
9153 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9154 * @cfg {String} valign Vertical aligns the content in a cell
9155 * @cfg {Number} width Specifies the width of a cell
9158 * Create a new TableCell
9159 * @param {Object} config The config object
9162 Roo.bootstrap.TableCell = function(config){
9163 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9166 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9186 getAutoCreate : function(){
9187 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9207 cfg.align=this.align
9213 cfg.bgcolor=this.bgcolor
9216 cfg.charoff=this.charoff
9219 cfg.colspan=this.colspan
9222 cfg.headers=this.headers
9225 cfg.height=this.height
9228 cfg.nowrap=this.nowrap
9231 cfg.rowspan=this.rowspan
9234 cfg.scope=this.scope
9237 cfg.valign=this.valign
9240 cfg.width=this.width
9259 * @class Roo.bootstrap.TableRow
9260 * @extends Roo.bootstrap.Component
9261 * Bootstrap TableRow class
9262 * @cfg {String} cls row class
9263 * @cfg {String} align Aligns the content in a table row
9264 * @cfg {String} bgcolor Specifies a background color for a table row
9265 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9266 * @cfg {String} valign Vertical aligns the content in a table row
9269 * Create a new TableRow
9270 * @param {Object} config The config object
9273 Roo.bootstrap.TableRow = function(config){
9274 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9277 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9285 getAutoCreate : function(){
9286 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9296 cfg.align = this.align;
9299 cfg.bgcolor = this.bgcolor;
9302 cfg.charoff = this.charoff;
9305 cfg.valign = this.valign;
9323 * @class Roo.bootstrap.TableBody
9324 * @extends Roo.bootstrap.Component
9325 * Bootstrap TableBody class
9326 * @cfg {String} cls element class
9327 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9328 * @cfg {String} align Aligns the content inside the element
9329 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9330 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9333 * Create a new TableBody
9334 * @param {Object} config The config object
9337 Roo.bootstrap.TableBody = function(config){
9338 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9341 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9349 getAutoCreate : function(){
9350 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9364 cfg.align = this.align;
9367 cfg.charoff = this.charoff;
9370 cfg.valign = this.valign;
9377 // initEvents : function()
9384 // this.store = Roo.factory(this.store, Roo.data);
9385 // this.store.on('load', this.onLoad, this);
9387 // this.store.load();
9391 // onLoad: function ()
9393 // this.fireEvent('load', this);
9403 * Ext JS Library 1.1.1
9404 * Copyright(c) 2006-2007, Ext JS, LLC.
9406 * Originally Released Under LGPL - original licence link has changed is not relivant.
9409 * <script type="text/javascript">
9412 // as we use this in bootstrap.
9413 Roo.namespace('Roo.form');
9415 * @class Roo.form.Action
9416 * Internal Class used to handle form actions
9418 * @param {Roo.form.BasicForm} el The form element or its id
9419 * @param {Object} config Configuration options
9424 // define the action interface
9425 Roo.form.Action = function(form, options){
9427 this.options = options || {};
9430 * Client Validation Failed
9433 Roo.form.Action.CLIENT_INVALID = 'client';
9435 * Server Validation Failed
9438 Roo.form.Action.SERVER_INVALID = 'server';
9440 * Connect to Server Failed
9443 Roo.form.Action.CONNECT_FAILURE = 'connect';
9445 * Reading Data from Server Failed
9448 Roo.form.Action.LOAD_FAILURE = 'load';
9450 Roo.form.Action.prototype = {
9452 failureType : undefined,
9453 response : undefined,
9457 run : function(options){
9462 success : function(response){
9467 handleResponse : function(response){
9471 // default connection failure
9472 failure : function(response){
9474 this.response = response;
9475 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9476 this.form.afterAction(this, false);
9479 processResponse : function(response){
9480 this.response = response;
9481 if(!response.responseText){
9484 this.result = this.handleResponse(response);
9488 // utility functions used internally
9489 getUrl : function(appendParams){
9490 var url = this.options.url || this.form.url || this.form.el.dom.action;
9492 var p = this.getParams();
9494 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9500 getMethod : function(){
9501 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9504 getParams : function(){
9505 var bp = this.form.baseParams;
9506 var p = this.options.params;
9508 if(typeof p == "object"){
9509 p = Roo.urlEncode(Roo.applyIf(p, bp));
9510 }else if(typeof p == 'string' && bp){
9511 p += '&' + Roo.urlEncode(bp);
9514 p = Roo.urlEncode(bp);
9519 createCallback : function(){
9521 success: this.success,
9522 failure: this.failure,
9524 timeout: (this.form.timeout*1000),
9525 upload: this.form.fileUpload ? this.success : undefined
9530 Roo.form.Action.Submit = function(form, options){
9531 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9534 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9537 haveProgress : false,
9538 uploadComplete : false,
9540 // uploadProgress indicator.
9541 uploadProgress : function()
9543 if (!this.form.progressUrl) {
9547 if (!this.haveProgress) {
9548 Roo.MessageBox.progress("Uploading", "Uploading");
9550 if (this.uploadComplete) {
9551 Roo.MessageBox.hide();
9555 this.haveProgress = true;
9557 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9559 var c = new Roo.data.Connection();
9561 url : this.form.progressUrl,
9566 success : function(req){
9567 //console.log(data);
9571 rdata = Roo.decode(req.responseText)
9573 Roo.log("Invalid data from server..");
9577 if (!rdata || !rdata.success) {
9579 Roo.MessageBox.alert(Roo.encode(rdata));
9582 var data = rdata.data;
9584 if (this.uploadComplete) {
9585 Roo.MessageBox.hide();
9590 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9591 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9594 this.uploadProgress.defer(2000,this);
9597 failure: function(data) {
9598 Roo.log('progress url failed ');
9609 // run get Values on the form, so it syncs any secondary forms.
9610 this.form.getValues();
9612 var o = this.options;
9613 var method = this.getMethod();
9614 var isPost = method == 'POST';
9615 if(o.clientValidation === false || this.form.isValid()){
9617 if (this.form.progressUrl) {
9618 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9619 (new Date() * 1) + '' + Math.random());
9624 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9625 form:this.form.el.dom,
9626 url:this.getUrl(!isPost),
9628 params:isPost ? this.getParams() : null,
9629 isUpload: this.form.fileUpload,
9630 formData : this.form.formData
9633 this.uploadProgress();
9635 }else if (o.clientValidation !== false){ // client validation failed
9636 this.failureType = Roo.form.Action.CLIENT_INVALID;
9637 this.form.afterAction(this, false);
9641 success : function(response)
9643 this.uploadComplete= true;
9644 if (this.haveProgress) {
9645 Roo.MessageBox.hide();
9649 var result = this.processResponse(response);
9650 if(result === true || result.success){
9651 this.form.afterAction(this, true);
9655 this.form.markInvalid(result.errors);
9656 this.failureType = Roo.form.Action.SERVER_INVALID;
9658 this.form.afterAction(this, false);
9660 failure : function(response)
9662 this.uploadComplete= true;
9663 if (this.haveProgress) {
9664 Roo.MessageBox.hide();
9667 this.response = response;
9668 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9669 this.form.afterAction(this, false);
9672 handleResponse : function(response){
9673 if(this.form.errorReader){
9674 var rs = this.form.errorReader.read(response);
9677 for(var i = 0, len = rs.records.length; i < len; i++) {
9678 var r = rs.records[i];
9682 if(errors.length < 1){
9686 success : rs.success,
9692 ret = Roo.decode(response.responseText);
9696 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9706 Roo.form.Action.Load = function(form, options){
9707 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9708 this.reader = this.form.reader;
9711 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9716 Roo.Ajax.request(Roo.apply(
9717 this.createCallback(), {
9718 method:this.getMethod(),
9719 url:this.getUrl(false),
9720 params:this.getParams()
9724 success : function(response){
9726 var result = this.processResponse(response);
9727 if(result === true || !result.success || !result.data){
9728 this.failureType = Roo.form.Action.LOAD_FAILURE;
9729 this.form.afterAction(this, false);
9732 this.form.clearInvalid();
9733 this.form.setValues(result.data);
9734 this.form.afterAction(this, true);
9737 handleResponse : function(response){
9738 if(this.form.reader){
9739 var rs = this.form.reader.read(response);
9740 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9742 success : rs.success,
9746 return Roo.decode(response.responseText);
9750 Roo.form.Action.ACTION_TYPES = {
9751 'load' : Roo.form.Action.Load,
9752 'submit' : Roo.form.Action.Submit
9761 * @class Roo.bootstrap.Form
9762 * @extends Roo.bootstrap.Component
9763 * Bootstrap Form class
9764 * @cfg {String} method GET | POST (default POST)
9765 * @cfg {String} labelAlign top | left (default top)
9766 * @cfg {String} align left | right - for navbars
9767 * @cfg {Boolean} loadMask load mask when submit (default true)
9772 * @param {Object} config The config object
9776 Roo.bootstrap.Form = function(config){
9778 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9780 Roo.bootstrap.Form.popover.apply();
9784 * @event clientvalidation
9785 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9786 * @param {Form} this
9787 * @param {Boolean} valid true if the form has passed client-side validation
9789 clientvalidation: true,
9791 * @event beforeaction
9792 * Fires before any action is performed. Return false to cancel the action.
9793 * @param {Form} this
9794 * @param {Action} action The action to be performed
9798 * @event actionfailed
9799 * Fires when an action fails.
9800 * @param {Form} this
9801 * @param {Action} action The action that failed
9803 actionfailed : true,
9805 * @event actioncomplete
9806 * Fires when an action is completed.
9807 * @param {Form} this
9808 * @param {Action} action The action that completed
9810 actioncomplete : true
9814 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9817 * @cfg {String} method
9818 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9823 * The URL to use for form actions if one isn't supplied in the action options.
9826 * @cfg {Boolean} fileUpload
9827 * Set to true if this form is a file upload.
9831 * @cfg {Object} baseParams
9832 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9836 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9840 * @cfg {Sting} align (left|right) for navbar forms
9845 activeAction : null,
9848 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9849 * element by passing it or its id or mask the form itself by passing in true.
9852 waitMsgTarget : false,
9857 * @cfg {Boolean} errorMask (true|false) default false
9862 * @cfg {Number} maskOffset Default 100
9867 * @cfg {Boolean} maskBody
9871 getAutoCreate : function(){
9875 method : this.method || 'POST',
9876 id : this.id || Roo.id(),
9879 if (this.parent().xtype.match(/^Nav/)) {
9880 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9884 if (this.labelAlign == 'left' ) {
9885 cfg.cls += ' form-horizontal';
9891 initEvents : function()
9893 this.el.on('submit', this.onSubmit, this);
9894 // this was added as random key presses on the form where triggering form submit.
9895 this.el.on('keypress', function(e) {
9896 if (e.getCharCode() != 13) {
9899 // we might need to allow it for textareas.. and some other items.
9900 // check e.getTarget().
9902 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9906 Roo.log("keypress blocked");
9914 onSubmit : function(e){
9919 * Returns true if client-side validation on the form is successful.
9922 isValid : function(){
9923 var items = this.getItems();
9927 items.each(function(f){
9933 Roo.log('invalid field: ' + f.name);
9937 if(!target && f.el.isVisible(true)){
9943 if(this.errorMask && !valid){
9944 Roo.bootstrap.Form.popover.mask(this, target);
9951 * Returns true if any fields in this form have changed since their original load.
9954 isDirty : function(){
9956 var items = this.getItems();
9957 items.each(function(f){
9967 * Performs a predefined action (submit or load) or custom actions you define on this form.
9968 * @param {String} actionName The name of the action type
9969 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9970 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9971 * accept other config options):
9973 Property Type Description
9974 ---------------- --------------- ----------------------------------------------------------------------------------
9975 url String The url for the action (defaults to the form's url)
9976 method String The form method to use (defaults to the form's method, or POST if not defined)
9977 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9978 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9979 validate the form on the client (defaults to false)
9981 * @return {BasicForm} this
9983 doAction : function(action, options){
9984 if(typeof action == 'string'){
9985 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9987 if(this.fireEvent('beforeaction', this, action) !== false){
9988 this.beforeAction(action);
9989 action.run.defer(100, action);
9995 beforeAction : function(action){
9996 var o = action.options;
10001 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10003 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10006 // not really supported yet.. ??
10008 //if(this.waitMsgTarget === true){
10009 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10010 //}else if(this.waitMsgTarget){
10011 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10012 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10014 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10020 afterAction : function(action, success){
10021 this.activeAction = null;
10022 var o = action.options;
10027 Roo.get(document.body).unmask();
10033 //if(this.waitMsgTarget === true){
10034 // this.el.unmask();
10035 //}else if(this.waitMsgTarget){
10036 // this.waitMsgTarget.unmask();
10038 // Roo.MessageBox.updateProgress(1);
10039 // Roo.MessageBox.hide();
10046 Roo.callback(o.success, o.scope, [this, action]);
10047 this.fireEvent('actioncomplete', this, action);
10051 // failure condition..
10052 // we have a scenario where updates need confirming.
10053 // eg. if a locking scenario exists..
10054 // we look for { errors : { needs_confirm : true }} in the response.
10056 (typeof(action.result) != 'undefined') &&
10057 (typeof(action.result.errors) != 'undefined') &&
10058 (typeof(action.result.errors.needs_confirm) != 'undefined')
10061 Roo.log("not supported yet");
10064 Roo.MessageBox.confirm(
10065 "Change requires confirmation",
10066 action.result.errorMsg,
10071 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10081 Roo.callback(o.failure, o.scope, [this, action]);
10082 // show an error message if no failed handler is set..
10083 if (!this.hasListener('actionfailed')) {
10084 Roo.log("need to add dialog support");
10086 Roo.MessageBox.alert("Error",
10087 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10088 action.result.errorMsg :
10089 "Saving Failed, please check your entries or try again"
10094 this.fireEvent('actionfailed', this, action);
10099 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10100 * @param {String} id The value to search for
10103 findField : function(id){
10104 var items = this.getItems();
10105 var field = items.get(id);
10107 items.each(function(f){
10108 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10115 return field || null;
10118 * Mark fields in this form invalid in bulk.
10119 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10120 * @return {BasicForm} this
10122 markInvalid : function(errors){
10123 if(errors instanceof Array){
10124 for(var i = 0, len = errors.length; i < len; i++){
10125 var fieldError = errors[i];
10126 var f = this.findField(fieldError.id);
10128 f.markInvalid(fieldError.msg);
10134 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10135 field.markInvalid(errors[id]);
10139 //Roo.each(this.childForms || [], function (f) {
10140 // f.markInvalid(errors);
10147 * Set values for fields in this form in bulk.
10148 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10149 * @return {BasicForm} this
10151 setValues : function(values){
10152 if(values instanceof Array){ // array of objects
10153 for(var i = 0, len = values.length; i < len; i++){
10155 var f = this.findField(v.id);
10157 f.setValue(v.value);
10158 if(this.trackResetOnLoad){
10159 f.originalValue = f.getValue();
10163 }else{ // object hash
10166 if(typeof values[id] != 'function' && (field = this.findField(id))){
10168 if (field.setFromData &&
10169 field.valueField &&
10170 field.displayField &&
10171 // combos' with local stores can
10172 // be queried via setValue()
10173 // to set their value..
10174 (field.store && !field.store.isLocal)
10178 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10179 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10180 field.setFromData(sd);
10182 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10184 field.setFromData(values);
10187 field.setValue(values[id]);
10191 if(this.trackResetOnLoad){
10192 field.originalValue = field.getValue();
10198 //Roo.each(this.childForms || [], function (f) {
10199 // f.setValues(values);
10206 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10207 * they are returned as an array.
10208 * @param {Boolean} asString
10211 getValues : function(asString){
10212 //if (this.childForms) {
10213 // copy values from the child forms
10214 // Roo.each(this.childForms, function (f) {
10215 // this.setValues(f.getValues());
10221 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10222 if(asString === true){
10225 return Roo.urlDecode(fs);
10229 * Returns the fields in this form as an object with key/value pairs.
10230 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10233 getFieldValues : function(with_hidden)
10235 var items = this.getItems();
10237 items.each(function(f){
10239 if (!f.getName()) {
10243 var v = f.getValue();
10245 if (f.inputType =='radio') {
10246 if (typeof(ret[f.getName()]) == 'undefined') {
10247 ret[f.getName()] = ''; // empty..
10250 if (!f.el.dom.checked) {
10254 v = f.el.dom.value;
10258 if(f.xtype == 'MoneyField'){
10259 ret[f.currencyName] = f.getCurrency();
10262 // not sure if this supported any more..
10263 if ((typeof(v) == 'object') && f.getRawValue) {
10264 v = f.getRawValue() ; // dates..
10266 // combo boxes where name != hiddenName...
10267 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10268 ret[f.name] = f.getRawValue();
10270 ret[f.getName()] = v;
10277 * Clears all invalid messages in this form.
10278 * @return {BasicForm} this
10280 clearInvalid : function(){
10281 var items = this.getItems();
10283 items.each(function(f){
10291 * Resets this form.
10292 * @return {BasicForm} this
10294 reset : function(){
10295 var items = this.getItems();
10296 items.each(function(f){
10300 Roo.each(this.childForms || [], function (f) {
10308 getItems : function()
10310 var r=new Roo.util.MixedCollection(false, function(o){
10311 return o.id || (o.id = Roo.id());
10313 var iter = function(el) {
10320 Roo.each(el.items,function(e) {
10329 hideFields : function(items)
10331 Roo.each(items, function(i){
10333 var f = this.findField(i);
10344 showFields : function(items)
10346 Roo.each(items, function(i){
10348 var f = this.findField(i);
10361 Roo.apply(Roo.bootstrap.Form, {
10377 intervalID : false,
10383 if(this.isApplied){
10388 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10389 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10390 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10391 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10394 this.maskEl.top.enableDisplayMode("block");
10395 this.maskEl.left.enableDisplayMode("block");
10396 this.maskEl.bottom.enableDisplayMode("block");
10397 this.maskEl.right.enableDisplayMode("block");
10399 this.toolTip = new Roo.bootstrap.Tooltip({
10400 cls : 'roo-form-error-popover',
10402 'left' : ['r-l', [-2,0], 'right'],
10403 'right' : ['l-r', [2,0], 'left'],
10404 'bottom' : ['tl-bl', [0,2], 'top'],
10405 'top' : [ 'bl-tl', [0,-2], 'bottom']
10409 this.toolTip.render(Roo.get(document.body));
10411 this.toolTip.el.enableDisplayMode("block");
10413 Roo.get(document.body).on('click', function(){
10417 Roo.get(document.body).on('touchstart', function(){
10421 this.isApplied = true
10424 mask : function(form, target)
10428 this.target = target;
10430 if(!this.form.errorMask || !target.el){
10434 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10436 Roo.log(scrollable);
10438 var ot = this.target.el.calcOffsetsTo(scrollable);
10440 var scrollTo = ot[1] - this.form.maskOffset;
10442 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10444 scrollable.scrollTo('top', scrollTo);
10446 var box = this.target.el.getBox();
10448 var zIndex = Roo.bootstrap.Modal.zIndex++;
10451 this.maskEl.top.setStyle('position', 'absolute');
10452 this.maskEl.top.setStyle('z-index', zIndex);
10453 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10454 this.maskEl.top.setLeft(0);
10455 this.maskEl.top.setTop(0);
10456 this.maskEl.top.show();
10458 this.maskEl.left.setStyle('position', 'absolute');
10459 this.maskEl.left.setStyle('z-index', zIndex);
10460 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10461 this.maskEl.left.setLeft(0);
10462 this.maskEl.left.setTop(box.y - this.padding);
10463 this.maskEl.left.show();
10465 this.maskEl.bottom.setStyle('position', 'absolute');
10466 this.maskEl.bottom.setStyle('z-index', zIndex);
10467 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10468 this.maskEl.bottom.setLeft(0);
10469 this.maskEl.bottom.setTop(box.bottom + this.padding);
10470 this.maskEl.bottom.show();
10472 this.maskEl.right.setStyle('position', 'absolute');
10473 this.maskEl.right.setStyle('z-index', zIndex);
10474 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10475 this.maskEl.right.setLeft(box.right + this.padding);
10476 this.maskEl.right.setTop(box.y - this.padding);
10477 this.maskEl.right.show();
10479 this.toolTip.bindEl = this.target.el;
10481 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10483 var tip = this.target.blankText;
10485 if(this.target.getValue() !== '' ) {
10487 if (this.target.invalidText.length) {
10488 tip = this.target.invalidText;
10489 } else if (this.target.regexText.length){
10490 tip = this.target.regexText;
10494 this.toolTip.show(tip);
10496 this.intervalID = window.setInterval(function() {
10497 Roo.bootstrap.Form.popover.unmask();
10500 window.onwheel = function(){ return false;};
10502 (function(){ this.isMasked = true; }).defer(500, this);
10506 unmask : function()
10508 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10512 this.maskEl.top.setStyle('position', 'absolute');
10513 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10514 this.maskEl.top.hide();
10516 this.maskEl.left.setStyle('position', 'absolute');
10517 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10518 this.maskEl.left.hide();
10520 this.maskEl.bottom.setStyle('position', 'absolute');
10521 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10522 this.maskEl.bottom.hide();
10524 this.maskEl.right.setStyle('position', 'absolute');
10525 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10526 this.maskEl.right.hide();
10528 this.toolTip.hide();
10530 this.toolTip.el.hide();
10532 window.onwheel = function(){ return true;};
10534 if(this.intervalID){
10535 window.clearInterval(this.intervalID);
10536 this.intervalID = false;
10539 this.isMasked = false;
10549 * Ext JS Library 1.1.1
10550 * Copyright(c) 2006-2007, Ext JS, LLC.
10552 * Originally Released Under LGPL - original licence link has changed is not relivant.
10555 * <script type="text/javascript">
10558 * @class Roo.form.VTypes
10559 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10562 Roo.form.VTypes = function(){
10563 // closure these in so they are only created once.
10564 var alpha = /^[a-zA-Z_]+$/;
10565 var alphanum = /^[a-zA-Z0-9_]+$/;
10566 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10567 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10569 // All these messages and functions are configurable
10572 * The function used to validate email addresses
10573 * @param {String} value The email address
10575 'email' : function(v){
10576 return email.test(v);
10579 * The error text to display when the email validation function returns false
10582 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10584 * The keystroke filter mask to be applied on email input
10587 'emailMask' : /[a-z0-9_\.\-@]/i,
10590 * The function used to validate URLs
10591 * @param {String} value The URL
10593 'url' : function(v){
10594 return url.test(v);
10597 * The error text to display when the url validation function returns false
10600 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10603 * The function used to validate alpha values
10604 * @param {String} value The value
10606 'alpha' : function(v){
10607 return alpha.test(v);
10610 * The error text to display when the alpha validation function returns false
10613 'alphaText' : 'This field should only contain letters and _',
10615 * The keystroke filter mask to be applied on alpha input
10618 'alphaMask' : /[a-z_]/i,
10621 * The function used to validate alphanumeric values
10622 * @param {String} value The value
10624 'alphanum' : function(v){
10625 return alphanum.test(v);
10628 * The error text to display when the alphanumeric validation function returns false
10631 'alphanumText' : 'This field should only contain letters, numbers and _',
10633 * The keystroke filter mask to be applied on alphanumeric input
10636 'alphanumMask' : /[a-z0-9_]/i
10646 * @class Roo.bootstrap.Input
10647 * @extends Roo.bootstrap.Component
10648 * Bootstrap Input class
10649 * @cfg {Boolean} disabled is it disabled
10650 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10651 * @cfg {String} name name of the input
10652 * @cfg {string} fieldLabel - the label associated
10653 * @cfg {string} placeholder - placeholder to put in text.
10654 * @cfg {string} before - input group add on before
10655 * @cfg {string} after - input group add on after
10656 * @cfg {string} size - (lg|sm) or leave empty..
10657 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10658 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10659 * @cfg {Number} md colspan out of 12 for computer-sized screens
10660 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10661 * @cfg {string} value default value of the input
10662 * @cfg {Number} labelWidth set the width of label
10663 * @cfg {Number} labellg set the width of label (1-12)
10664 * @cfg {Number} labelmd set the width of label (1-12)
10665 * @cfg {Number} labelsm set the width of label (1-12)
10666 * @cfg {Number} labelxs set the width of label (1-12)
10667 * @cfg {String} labelAlign (top|left)
10668 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10669 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10670 * @cfg {String} indicatorpos (left|right) default left
10671 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10672 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10673 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10675 * @cfg {String} align (left|center|right) Default left
10676 * @cfg {Boolean} forceFeedback (true|false) Default false
10679 * Create a new Input
10680 * @param {Object} config The config object
10683 Roo.bootstrap.Input = function(config){
10685 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10690 * Fires when this field receives input focus.
10691 * @param {Roo.form.Field} this
10696 * Fires when this field loses input focus.
10697 * @param {Roo.form.Field} this
10701 * @event specialkey
10702 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10703 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10704 * @param {Roo.form.Field} this
10705 * @param {Roo.EventObject} e The event object
10710 * Fires just before the field blurs if the field value has changed.
10711 * @param {Roo.form.Field} this
10712 * @param {Mixed} newValue The new value
10713 * @param {Mixed} oldValue The original value
10718 * Fires after the field has been marked as invalid.
10719 * @param {Roo.form.Field} this
10720 * @param {String} msg The validation message
10725 * Fires after the field has been validated with no errors.
10726 * @param {Roo.form.Field} this
10731 * Fires after the key up
10732 * @param {Roo.form.Field} this
10733 * @param {Roo.EventObject} e The event Object
10738 * Fires after the user pastes into input
10739 * @param {Roo.form.Field} this
10740 * @param {Roo.EventObject} e The event Object
10746 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10748 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10749 automatic validation (defaults to "keyup").
10751 validationEvent : "keyup",
10753 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10755 validateOnBlur : true,
10757 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10759 validationDelay : 250,
10761 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10763 focusClass : "x-form-focus", // not needed???
10767 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10769 invalidClass : "has-warning",
10772 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10774 validClass : "has-success",
10777 * @cfg {Boolean} hasFeedback (true|false) default true
10779 hasFeedback : true,
10782 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10784 invalidFeedbackClass : "glyphicon-warning-sign",
10787 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10789 validFeedbackClass : "glyphicon-ok",
10792 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10794 selectOnFocus : false,
10797 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10801 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10806 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10808 disableKeyFilter : false,
10811 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10815 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10819 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10821 blankText : "Please complete this mandatory field",
10824 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10828 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10830 maxLength : Number.MAX_VALUE,
10832 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10834 minLengthText : "The minimum length for this field is {0}",
10836 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10838 maxLengthText : "The maximum length for this field is {0}",
10842 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10843 * If available, this function will be called only after the basic validators all return true, and will be passed the
10844 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10848 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10849 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10850 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10854 * @cfg {String} regexText -- Depricated - use Invalid Text
10859 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10865 autocomplete: false,
10869 inputType : 'text',
10872 placeholder: false,
10877 preventMark: false,
10878 isFormField : true,
10881 labelAlign : false,
10884 formatedValue : false,
10885 forceFeedback : false,
10887 indicatorpos : 'left',
10897 parentLabelAlign : function()
10900 while (parent.parent()) {
10901 parent = parent.parent();
10902 if (typeof(parent.labelAlign) !='undefined') {
10903 return parent.labelAlign;
10910 getAutoCreate : function()
10912 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10918 if(this.inputType != 'hidden'){
10919 cfg.cls = 'form-group' //input-group
10925 type : this.inputType,
10926 value : this.value,
10927 cls : 'form-control',
10928 placeholder : this.placeholder || '',
10929 autocomplete : this.autocomplete || 'new-password'
10931 if (this.inputType == 'file') {
10932 input.style = 'overflow:hidden'; // why not in CSS?
10935 if(this.capture.length){
10936 input.capture = this.capture;
10939 if(this.accept.length){
10940 input.accept = this.accept + "/*";
10944 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10947 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10948 input.maxLength = this.maxLength;
10951 if (this.disabled) {
10952 input.disabled=true;
10955 if (this.readOnly) {
10956 input.readonly=true;
10960 input.name = this.name;
10964 input.cls += ' input-' + this.size;
10968 ['xs','sm','md','lg'].map(function(size){
10969 if (settings[size]) {
10970 cfg.cls += ' col-' + size + '-' + settings[size];
10974 var inputblock = input;
10978 cls: 'glyphicon form-control-feedback'
10981 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10984 cls : 'has-feedback',
10992 if (this.before || this.after) {
10995 cls : 'input-group',
10999 if (this.before && typeof(this.before) == 'string') {
11001 inputblock.cn.push({
11003 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11007 if (this.before && typeof(this.before) == 'object') {
11008 this.before = Roo.factory(this.before);
11010 inputblock.cn.push({
11012 cls : 'roo-input-before input-group-prepend input-group-' +
11013 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11017 inputblock.cn.push(input);
11019 if (this.after && typeof(this.after) == 'string') {
11020 inputblock.cn.push({
11022 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11026 if (this.after && typeof(this.after) == 'object') {
11027 this.after = Roo.factory(this.after);
11029 inputblock.cn.push({
11031 cls : 'roo-input-after input-group-append input-group-' +
11032 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11036 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11037 inputblock.cls += ' has-feedback';
11038 inputblock.cn.push(feedback);
11043 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11044 tooltip : 'This field is required'
11046 if (this.allowBlank ) {
11047 indicator.style = this.allowBlank ? ' display:none' : '';
11049 if (align ==='left' && this.fieldLabel.length) {
11051 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11058 cls : 'control-label col-form-label',
11059 html : this.fieldLabel
11070 var labelCfg = cfg.cn[1];
11071 var contentCfg = cfg.cn[2];
11073 if(this.indicatorpos == 'right'){
11078 cls : 'control-label col-form-label',
11082 html : this.fieldLabel
11096 labelCfg = cfg.cn[0];
11097 contentCfg = cfg.cn[1];
11101 if(this.labelWidth > 12){
11102 labelCfg.style = "width: " + this.labelWidth + 'px';
11105 if(this.labelWidth < 13 && this.labelmd == 0){
11106 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11109 if(this.labellg > 0){
11110 labelCfg.cls += ' col-lg-' + this.labellg;
11111 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11114 if(this.labelmd > 0){
11115 labelCfg.cls += ' col-md-' + this.labelmd;
11116 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11119 if(this.labelsm > 0){
11120 labelCfg.cls += ' col-sm-' + this.labelsm;
11121 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11124 if(this.labelxs > 0){
11125 labelCfg.cls += ' col-xs-' + this.labelxs;
11126 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11130 } else if ( this.fieldLabel.length) {
11137 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11138 tooltip : 'This field is required',
11139 style : this.allowBlank ? ' display:none' : ''
11143 //cls : 'input-group-addon',
11144 html : this.fieldLabel
11152 if(this.indicatorpos == 'right'){
11157 //cls : 'input-group-addon',
11158 html : this.fieldLabel
11163 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11164 tooltip : 'This field is required',
11165 style : this.allowBlank ? ' display:none' : ''
11185 if (this.parentType === 'Navbar' && this.parent().bar) {
11186 cfg.cls += ' navbar-form';
11189 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11190 // on BS4 we do this only if not form
11191 cfg.cls += ' navbar-form';
11199 * return the real input element.
11201 inputEl: function ()
11203 return this.el.select('input.form-control',true).first();
11206 tooltipEl : function()
11208 return this.inputEl();
11211 indicatorEl : function()
11213 if (Roo.bootstrap.version == 4) {
11214 return false; // not enabled in v4 yet.
11217 var indicator = this.el.select('i.roo-required-indicator',true).first();
11227 setDisabled : function(v)
11229 var i = this.inputEl().dom;
11231 i.removeAttribute('disabled');
11235 i.setAttribute('disabled','true');
11237 initEvents : function()
11240 this.inputEl().on("keydown" , this.fireKey, this);
11241 this.inputEl().on("focus", this.onFocus, this);
11242 this.inputEl().on("blur", this.onBlur, this);
11244 this.inputEl().relayEvent('keyup', this);
11245 this.inputEl().relayEvent('paste', this);
11247 this.indicator = this.indicatorEl();
11249 if(this.indicator){
11250 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11253 // reference to original value for reset
11254 this.originalValue = this.getValue();
11255 //Roo.form.TextField.superclass.initEvents.call(this);
11256 if(this.validationEvent == 'keyup'){
11257 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11258 this.inputEl().on('keyup', this.filterValidation, this);
11260 else if(this.validationEvent !== false){
11261 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11264 if(this.selectOnFocus){
11265 this.on("focus", this.preFocus, this);
11268 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11269 this.inputEl().on("keypress", this.filterKeys, this);
11271 this.inputEl().relayEvent('keypress', this);
11274 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11275 this.el.on("click", this.autoSize, this);
11278 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11279 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11282 if (typeof(this.before) == 'object') {
11283 this.before.render(this.el.select('.roo-input-before',true).first());
11285 if (typeof(this.after) == 'object') {
11286 this.after.render(this.el.select('.roo-input-after',true).first());
11289 this.inputEl().on('change', this.onChange, this);
11292 filterValidation : function(e){
11293 if(!e.isNavKeyPress()){
11294 this.validationTask.delay(this.validationDelay);
11298 * Validates the field value
11299 * @return {Boolean} True if the value is valid, else false
11301 validate : function(){
11302 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11303 if(this.disabled || this.validateValue(this.getRawValue())){
11308 this.markInvalid();
11314 * Validates a value according to the field's validation rules and marks the field as invalid
11315 * if the validation fails
11316 * @param {Mixed} value The value to validate
11317 * @return {Boolean} True if the value is valid, else false
11319 validateValue : function(value)
11321 if(this.getVisibilityEl().hasClass('hidden')){
11325 if(value.length < 1) { // if it's blank
11326 if(this.allowBlank){
11332 if(value.length < this.minLength){
11335 if(value.length > this.maxLength){
11339 var vt = Roo.form.VTypes;
11340 if(!vt[this.vtype](value, this)){
11344 if(typeof this.validator == "function"){
11345 var msg = this.validator(value);
11349 if (typeof(msg) == 'string') {
11350 this.invalidText = msg;
11354 if(this.regex && !this.regex.test(value)){
11362 fireKey : function(e){
11363 //Roo.log('field ' + e.getKey());
11364 if(e.isNavKeyPress()){
11365 this.fireEvent("specialkey", this, e);
11368 focus : function (selectText){
11370 this.inputEl().focus();
11371 if(selectText === true){
11372 this.inputEl().dom.select();
11378 onFocus : function(){
11379 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11380 // this.el.addClass(this.focusClass);
11382 if(!this.hasFocus){
11383 this.hasFocus = true;
11384 this.startValue = this.getValue();
11385 this.fireEvent("focus", this);
11389 beforeBlur : Roo.emptyFn,
11393 onBlur : function(){
11395 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11396 //this.el.removeClass(this.focusClass);
11398 this.hasFocus = false;
11399 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11402 var v = this.getValue();
11403 if(String(v) !== String(this.startValue)){
11404 this.fireEvent('change', this, v, this.startValue);
11406 this.fireEvent("blur", this);
11409 onChange : function(e)
11411 var v = this.getValue();
11412 if(String(v) !== String(this.startValue)){
11413 this.fireEvent('change', this, v, this.startValue);
11419 * Resets the current field value to the originally loaded value and clears any validation messages
11421 reset : function(){
11422 this.setValue(this.originalValue);
11426 * Returns the name of the field
11427 * @return {Mixed} name The name field
11429 getName: function(){
11433 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11434 * @return {Mixed} value The field value
11436 getValue : function(){
11438 var v = this.inputEl().getValue();
11443 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11444 * @return {Mixed} value The field value
11446 getRawValue : function(){
11447 var v = this.inputEl().getValue();
11453 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11454 * @param {Mixed} value The value to set
11456 setRawValue : function(v){
11457 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11460 selectText : function(start, end){
11461 var v = this.getRawValue();
11463 start = start === undefined ? 0 : start;
11464 end = end === undefined ? v.length : end;
11465 var d = this.inputEl().dom;
11466 if(d.setSelectionRange){
11467 d.setSelectionRange(start, end);
11468 }else if(d.createTextRange){
11469 var range = d.createTextRange();
11470 range.moveStart("character", start);
11471 range.moveEnd("character", v.length-end);
11478 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11479 * @param {Mixed} value The value to set
11481 setValue : function(v){
11484 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11490 processValue : function(value){
11491 if(this.stripCharsRe){
11492 var newValue = value.replace(this.stripCharsRe, '');
11493 if(newValue !== value){
11494 this.setRawValue(newValue);
11501 preFocus : function(){
11503 if(this.selectOnFocus){
11504 this.inputEl().dom.select();
11507 filterKeys : function(e){
11508 var k = e.getKey();
11509 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11512 var c = e.getCharCode(), cc = String.fromCharCode(c);
11513 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11516 if(!this.maskRe.test(cc)){
11521 * Clear any invalid styles/messages for this field
11523 clearInvalid : function(){
11525 if(!this.el || this.preventMark){ // not rendered
11530 this.el.removeClass([this.invalidClass, 'is-invalid']);
11532 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11534 var feedback = this.el.select('.form-control-feedback', true).first();
11537 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11542 if(this.indicator){
11543 this.indicator.removeClass('visible');
11544 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11547 this.fireEvent('valid', this);
11551 * Mark this field as valid
11553 markValid : function()
11555 if(!this.el || this.preventMark){ // not rendered...
11559 this.el.removeClass([this.invalidClass, this.validClass]);
11560 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11562 var feedback = this.el.select('.form-control-feedback', true).first();
11565 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11568 if(this.indicator){
11569 this.indicator.removeClass('visible');
11570 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11578 if(this.allowBlank && !this.getRawValue().length){
11581 if (Roo.bootstrap.version == 3) {
11582 this.el.addClass(this.validClass);
11584 this.inputEl().addClass('is-valid');
11587 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11589 var feedback = this.el.select('.form-control-feedback', true).first();
11592 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11593 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11598 this.fireEvent('valid', this);
11602 * Mark this field as invalid
11603 * @param {String} msg The validation message
11605 markInvalid : function(msg)
11607 if(!this.el || this.preventMark){ // not rendered
11611 this.el.removeClass([this.invalidClass, this.validClass]);
11612 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11614 var feedback = this.el.select('.form-control-feedback', true).first();
11617 this.el.select('.form-control-feedback', true).first().removeClass(
11618 [this.invalidFeedbackClass, this.validFeedbackClass]);
11625 if(this.allowBlank && !this.getRawValue().length){
11629 if(this.indicator){
11630 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11631 this.indicator.addClass('visible');
11633 if (Roo.bootstrap.version == 3) {
11634 this.el.addClass(this.invalidClass);
11636 this.inputEl().addClass('is-invalid');
11641 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11643 var feedback = this.el.select('.form-control-feedback', true).first();
11646 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11648 if(this.getValue().length || this.forceFeedback){
11649 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11656 this.fireEvent('invalid', this, msg);
11659 SafariOnKeyDown : function(event)
11661 // this is a workaround for a password hang bug on chrome/ webkit.
11662 if (this.inputEl().dom.type != 'password') {
11666 var isSelectAll = false;
11668 if(this.inputEl().dom.selectionEnd > 0){
11669 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11671 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11672 event.preventDefault();
11677 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11679 event.preventDefault();
11680 // this is very hacky as keydown always get's upper case.
11682 var cc = String.fromCharCode(event.getCharCode());
11683 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11687 adjustWidth : function(tag, w){
11688 tag = tag.toLowerCase();
11689 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11690 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11691 if(tag == 'input'){
11694 if(tag == 'textarea'){
11697 }else if(Roo.isOpera){
11698 if(tag == 'input'){
11701 if(tag == 'textarea'){
11709 setFieldLabel : function(v)
11711 if(!this.rendered){
11715 if(this.indicatorEl()){
11716 var ar = this.el.select('label > span',true);
11718 if (ar.elements.length) {
11719 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11720 this.fieldLabel = v;
11724 var br = this.el.select('label',true);
11726 if(br.elements.length) {
11727 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11728 this.fieldLabel = v;
11732 Roo.log('Cannot Found any of label > span || label in input');
11736 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11737 this.fieldLabel = v;
11752 * @class Roo.bootstrap.TextArea
11753 * @extends Roo.bootstrap.Input
11754 * Bootstrap TextArea class
11755 * @cfg {Number} cols Specifies the visible width of a text area
11756 * @cfg {Number} rows Specifies the visible number of lines in a text area
11757 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11758 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11759 * @cfg {string} html text
11762 * Create a new TextArea
11763 * @param {Object} config The config object
11766 Roo.bootstrap.TextArea = function(config){
11767 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11771 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11781 getAutoCreate : function(){
11783 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11789 if(this.inputType != 'hidden'){
11790 cfg.cls = 'form-group' //input-group
11798 value : this.value || '',
11799 html: this.html || '',
11800 cls : 'form-control',
11801 placeholder : this.placeholder || ''
11805 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11806 input.maxLength = this.maxLength;
11810 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11814 input.cols = this.cols;
11817 if (this.readOnly) {
11818 input.readonly = true;
11822 input.name = this.name;
11826 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11830 ['xs','sm','md','lg'].map(function(size){
11831 if (settings[size]) {
11832 cfg.cls += ' col-' + size + '-' + settings[size];
11836 var inputblock = input;
11838 if(this.hasFeedback && !this.allowBlank){
11842 cls: 'glyphicon form-control-feedback'
11846 cls : 'has-feedback',
11855 if (this.before || this.after) {
11858 cls : 'input-group',
11862 inputblock.cn.push({
11864 cls : 'input-group-addon',
11869 inputblock.cn.push(input);
11871 if(this.hasFeedback && !this.allowBlank){
11872 inputblock.cls += ' has-feedback';
11873 inputblock.cn.push(feedback);
11877 inputblock.cn.push({
11879 cls : 'input-group-addon',
11886 if (align ==='left' && this.fieldLabel.length) {
11891 cls : 'control-label',
11892 html : this.fieldLabel
11903 if(this.labelWidth > 12){
11904 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11907 if(this.labelWidth < 13 && this.labelmd == 0){
11908 this.labelmd = this.labelWidth;
11911 if(this.labellg > 0){
11912 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11913 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11916 if(this.labelmd > 0){
11917 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11918 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11921 if(this.labelsm > 0){
11922 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11923 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11926 if(this.labelxs > 0){
11927 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11928 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11931 } else if ( this.fieldLabel.length) {
11936 //cls : 'input-group-addon',
11937 html : this.fieldLabel
11955 if (this.disabled) {
11956 input.disabled=true;
11963 * return the real textarea element.
11965 inputEl: function ()
11967 return this.el.select('textarea.form-control',true).first();
11971 * Clear any invalid styles/messages for this field
11973 clearInvalid : function()
11976 if(!this.el || this.preventMark){ // not rendered
11980 var label = this.el.select('label', true).first();
11981 var icon = this.el.select('i.fa-star', true).first();
11986 this.el.removeClass( this.validClass);
11987 this.inputEl().removeClass('is-invalid');
11989 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11991 var feedback = this.el.select('.form-control-feedback', true).first();
11994 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11999 this.fireEvent('valid', this);
12003 * Mark this field as valid
12005 markValid : function()
12007 if(!this.el || this.preventMark){ // not rendered
12011 this.el.removeClass([this.invalidClass, this.validClass]);
12012 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12014 var feedback = this.el.select('.form-control-feedback', true).first();
12017 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12020 if(this.disabled || this.allowBlank){
12024 var label = this.el.select('label', true).first();
12025 var icon = this.el.select('i.fa-star', true).first();
12030 if (Roo.bootstrap.version == 3) {
12031 this.el.addClass(this.validClass);
12033 this.inputEl().addClass('is-valid');
12037 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12039 var feedback = this.el.select('.form-control-feedback', true).first();
12042 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12043 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12048 this.fireEvent('valid', this);
12052 * Mark this field as invalid
12053 * @param {String} msg The validation message
12055 markInvalid : function(msg)
12057 if(!this.el || this.preventMark){ // not rendered
12061 this.el.removeClass([this.invalidClass, this.validClass]);
12062 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12064 var feedback = this.el.select('.form-control-feedback', true).first();
12067 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12070 if(this.disabled || this.allowBlank){
12074 var label = this.el.select('label', true).first();
12075 var icon = this.el.select('i.fa-star', true).first();
12077 if(!this.getValue().length && label && !icon){
12078 this.el.createChild({
12080 cls : 'text-danger fa fa-lg fa-star',
12081 tooltip : 'This field is required',
12082 style : 'margin-right:5px;'
12086 if (Roo.bootstrap.version == 3) {
12087 this.el.addClass(this.invalidClass);
12089 this.inputEl().addClass('is-invalid');
12092 // fixme ... this may be depricated need to test..
12093 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12095 var feedback = this.el.select('.form-control-feedback', true).first();
12098 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12100 if(this.getValue().length || this.forceFeedback){
12101 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12108 this.fireEvent('invalid', this, msg);
12116 * trigger field - base class for combo..
12121 * @class Roo.bootstrap.TriggerField
12122 * @extends Roo.bootstrap.Input
12123 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12124 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12125 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12126 * for which you can provide a custom implementation. For example:
12128 var trigger = new Roo.bootstrap.TriggerField();
12129 trigger.onTriggerClick = myTriggerFn;
12130 trigger.applyTo('my-field');
12133 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12134 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12135 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12136 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12137 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12140 * Create a new TriggerField.
12141 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12142 * to the base TextField)
12144 Roo.bootstrap.TriggerField = function(config){
12145 this.mimicing = false;
12146 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12149 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12151 * @cfg {String} triggerClass A CSS class to apply to the trigger
12154 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12159 * @cfg {Boolean} removable (true|false) special filter default false
12163 /** @cfg {Boolean} grow @hide */
12164 /** @cfg {Number} growMin @hide */
12165 /** @cfg {Number} growMax @hide */
12171 autoSize: Roo.emptyFn,
12175 deferHeight : true,
12178 actionMode : 'wrap',
12183 getAutoCreate : function(){
12185 var align = this.labelAlign || this.parentLabelAlign();
12190 cls: 'form-group' //input-group
12197 type : this.inputType,
12198 cls : 'form-control',
12199 autocomplete: 'new-password',
12200 placeholder : this.placeholder || ''
12204 input.name = this.name;
12207 input.cls += ' input-' + this.size;
12210 if (this.disabled) {
12211 input.disabled=true;
12214 var inputblock = input;
12216 if(this.hasFeedback && !this.allowBlank){
12220 cls: 'glyphicon form-control-feedback'
12223 if(this.removable && !this.editable ){
12225 cls : 'has-feedback',
12231 cls : 'roo-combo-removable-btn close'
12238 cls : 'has-feedback',
12247 if(this.removable && !this.editable ){
12249 cls : 'roo-removable',
12255 cls : 'roo-combo-removable-btn close'
12262 if (this.before || this.after) {
12265 cls : 'input-group',
12269 inputblock.cn.push({
12271 cls : 'input-group-addon input-group-prepend input-group-text',
12276 inputblock.cn.push(input);
12278 if(this.hasFeedback && !this.allowBlank){
12279 inputblock.cls += ' has-feedback';
12280 inputblock.cn.push(feedback);
12284 inputblock.cn.push({
12286 cls : 'input-group-addon input-group-append input-group-text',
12295 var ibwrap = inputblock;
12300 cls: 'roo-select2-choices',
12304 cls: 'roo-select2-search-field',
12316 cls: 'roo-select2-container input-group',
12321 cls: 'form-hidden-field'
12327 if(!this.multiple && this.showToggleBtn){
12333 if (this.caret != false) {
12336 cls: 'fa fa-' + this.caret
12343 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12345 Roo.bootstrap.version == 3 ? caret : '',
12348 cls: 'combobox-clear',
12362 combobox.cls += ' roo-select2-container-multi';
12366 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12367 tooltip : 'This field is required'
12369 if (Roo.bootstrap.version == 4) {
12372 style : 'display:none'
12377 if (align ==='left' && this.fieldLabel.length) {
12379 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12386 cls : 'control-label',
12387 html : this.fieldLabel
12399 var labelCfg = cfg.cn[1];
12400 var contentCfg = cfg.cn[2];
12402 if(this.indicatorpos == 'right'){
12407 cls : 'control-label',
12411 html : this.fieldLabel
12425 labelCfg = cfg.cn[0];
12426 contentCfg = cfg.cn[1];
12429 if(this.labelWidth > 12){
12430 labelCfg.style = "width: " + this.labelWidth + 'px';
12433 if(this.labelWidth < 13 && this.labelmd == 0){
12434 this.labelmd = this.labelWidth;
12437 if(this.labellg > 0){
12438 labelCfg.cls += ' col-lg-' + this.labellg;
12439 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12442 if(this.labelmd > 0){
12443 labelCfg.cls += ' col-md-' + this.labelmd;
12444 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12447 if(this.labelsm > 0){
12448 labelCfg.cls += ' col-sm-' + this.labelsm;
12449 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12452 if(this.labelxs > 0){
12453 labelCfg.cls += ' col-xs-' + this.labelxs;
12454 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12457 } else if ( this.fieldLabel.length) {
12458 // Roo.log(" label");
12463 //cls : 'input-group-addon',
12464 html : this.fieldLabel
12472 if(this.indicatorpos == 'right'){
12480 html : this.fieldLabel
12494 // Roo.log(" no label && no align");
12501 ['xs','sm','md','lg'].map(function(size){
12502 if (settings[size]) {
12503 cfg.cls += ' col-' + size + '-' + settings[size];
12514 onResize : function(w, h){
12515 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12516 // if(typeof w == 'number'){
12517 // var x = w - this.trigger.getWidth();
12518 // this.inputEl().setWidth(this.adjustWidth('input', x));
12519 // this.trigger.setStyle('left', x+'px');
12524 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12527 getResizeEl : function(){
12528 return this.inputEl();
12532 getPositionEl : function(){
12533 return this.inputEl();
12537 alignErrorIcon : function(){
12538 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12542 initEvents : function(){
12546 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12547 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12548 if(!this.multiple && this.showToggleBtn){
12549 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12550 if(this.hideTrigger){
12551 this.trigger.setDisplayed(false);
12553 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12557 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12560 if(this.removable && !this.editable && !this.tickable){
12561 var close = this.closeTriggerEl();
12564 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12565 close.on('click', this.removeBtnClick, this, close);
12569 //this.trigger.addClassOnOver('x-form-trigger-over');
12570 //this.trigger.addClassOnClick('x-form-trigger-click');
12573 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12577 closeTriggerEl : function()
12579 var close = this.el.select('.roo-combo-removable-btn', true).first();
12580 return close ? close : false;
12583 removeBtnClick : function(e, h, el)
12585 e.preventDefault();
12587 if(this.fireEvent("remove", this) !== false){
12589 this.fireEvent("afterremove", this)
12593 createList : function()
12595 this.list = Roo.get(document.body).createChild({
12596 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12597 cls: 'typeahead typeahead-long dropdown-menu shadow',
12598 style: 'display:none'
12601 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12606 initTrigger : function(){
12611 onDestroy : function(){
12613 this.trigger.removeAllListeners();
12614 // this.trigger.remove();
12617 // this.wrap.remove();
12619 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12623 onFocus : function(){
12624 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12626 if(!this.mimicing){
12627 this.wrap.addClass('x-trigger-wrap-focus');
12628 this.mimicing = true;
12629 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12630 if(this.monitorTab){
12631 this.el.on("keydown", this.checkTab, this);
12638 checkTab : function(e){
12639 if(e.getKey() == e.TAB){
12640 this.triggerBlur();
12645 onBlur : function(){
12650 mimicBlur : function(e, t){
12652 if(!this.wrap.contains(t) && this.validateBlur()){
12653 this.triggerBlur();
12659 triggerBlur : function(){
12660 this.mimicing = false;
12661 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12662 if(this.monitorTab){
12663 this.el.un("keydown", this.checkTab, this);
12665 //this.wrap.removeClass('x-trigger-wrap-focus');
12666 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12670 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12671 validateBlur : function(e, t){
12676 onDisable : function(){
12677 this.inputEl().dom.disabled = true;
12678 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12680 // this.wrap.addClass('x-item-disabled');
12685 onEnable : function(){
12686 this.inputEl().dom.disabled = false;
12687 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12689 // this.el.removeClass('x-item-disabled');
12694 onShow : function(){
12695 var ae = this.getActionEl();
12698 ae.dom.style.display = '';
12699 ae.dom.style.visibility = 'visible';
12705 onHide : function(){
12706 var ae = this.getActionEl();
12707 ae.dom.style.display = 'none';
12711 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12712 * by an implementing function.
12714 * @param {EventObject} e
12716 onTriggerClick : Roo.emptyFn
12724 * @class Roo.bootstrap.CardUploader
12725 * @extends Roo.bootstrap.Button
12726 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12727 * @cfg {Number} errorTimeout default 3000
12728 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12729 * @cfg {Array} html The button text.
12733 * Create a new CardUploader
12734 * @param {Object} config The config object
12737 Roo.bootstrap.CardUploader = function(config){
12741 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12744 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12752 * When a image is clicked on - and needs to display a slideshow or similar..
12753 * @param {Roo.bootstrap.Card} this
12754 * @param {Object} The image information data
12760 * When a the download link is clicked
12761 * @param {Roo.bootstrap.Card} this
12762 * @param {Object} The image information data contains
12769 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12772 errorTimeout : 3000,
12776 fileCollection : false,
12779 getAutoCreate : function()
12783 cls :'form-group' ,
12788 //cls : 'input-group-addon',
12789 html : this.fieldLabel
12797 value : this.value,
12798 cls : 'd-none form-control'
12803 multiple : 'multiple',
12805 cls : 'd-none roo-card-upload-selector'
12809 cls : 'roo-card-uploader-button-container w-100 mb-2'
12812 cls : 'card-columns roo-card-uploader-container'
12822 getChildContainer : function() /// what children are added to.
12824 return this.containerEl;
12827 getButtonContainer : function() /// what children are added to.
12829 return this.el.select(".roo-card-uploader-button-container").first();
12832 initEvents : function()
12835 Roo.bootstrap.Input.prototype.initEvents.call(this);
12839 xns: Roo.bootstrap,
12842 container_method : 'getButtonContainer' ,
12843 html : this.html, // fix changable?
12846 'click' : function(btn, e) {
12855 this.urlAPI = (window.createObjectURL && window) ||
12856 (window.URL && URL.revokeObjectURL && URL) ||
12857 (window.webkitURL && webkitURL);
12862 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12864 this.selectorEl.on('change', this.onFileSelected, this);
12867 this.images.forEach(function(img) {
12870 this.images = false;
12872 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12878 onClick : function(e)
12880 e.preventDefault();
12882 this.selectorEl.dom.click();
12886 onFileSelected : function(e)
12888 e.preventDefault();
12890 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12894 Roo.each(this.selectorEl.dom.files, function(file){
12895 this.addFile(file);
12904 addFile : function(file)
12907 if(typeof(file) === 'string'){
12908 throw "Add file by name?"; // should not happen
12912 if(!file || !this.urlAPI){
12922 var url = _this.urlAPI.createObjectURL( file);
12925 id : Roo.bootstrap.CardUploader.ID--,
12926 is_uploaded : false,
12930 mimetype : file.type,
12938 * addCard - add an Attachment to the uploader
12939 * @param data - the data about the image to upload
12943 title : "Title of file",
12944 is_uploaded : false,
12945 src : "http://.....",
12946 srcfile : { the File upload object },
12947 mimetype : file.type,
12950 .. any other data...
12956 addCard : function (data)
12958 // hidden input element?
12959 // if the file is not an image...
12960 //then we need to use something other that and header_image
12965 xns : Roo.bootstrap,
12966 xtype : 'CardFooter',
12969 xns : Roo.bootstrap,
12975 xns : Roo.bootstrap,
12977 html : String.format("<small>{0}</small>", data.title),
12978 cls : 'col-10 text-left',
12983 click : function() {
12985 t.fireEvent( "download", t, data );
12991 xns : Roo.bootstrap,
12993 style: 'max-height: 28px; ',
12999 click : function() {
13000 t.removeCard(data.id)
13012 var cn = this.addxtype(
13015 xns : Roo.bootstrap,
13018 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13019 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13020 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13025 initEvents : function() {
13026 Roo.bootstrap.Card.prototype.initEvents.call(this);
13028 this.imgEl = this.el.select('.card-img-top').first();
13030 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13031 this.imgEl.set({ 'pointer' : 'cursor' });
13034 this.getCardFooter().addClass('p-1');
13041 // dont' really need ot update items.
13042 // this.items.push(cn);
13043 this.fileCollection.add(cn);
13045 if (!data.srcfile) {
13046 this.updateInput();
13051 var reader = new FileReader();
13052 reader.addEventListener("load", function() {
13053 data.srcdata = reader.result;
13056 reader.readAsDataURL(data.srcfile);
13061 removeCard : function(id)
13064 var card = this.fileCollection.get(id);
13065 card.data.is_deleted = 1;
13066 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13067 //this.fileCollection.remove(card);
13068 //this.items = this.items.filter(function(e) { return e != card });
13069 // dont' really need ot update items.
13070 card.el.dom.parentNode.removeChild(card.el.dom);
13071 this.updateInput();
13077 this.fileCollection.each(function(card) {
13078 if (card.el.dom && card.el.dom.parentNode) {
13079 card.el.dom.parentNode.removeChild(card.el.dom);
13082 this.fileCollection.clear();
13083 this.updateInput();
13086 updateInput : function()
13089 this.fileCollection.each(function(e) {
13093 this.inputEl().dom.value = JSON.stringify(data);
13103 Roo.bootstrap.CardUploader.ID = -1;/*
13105 * Ext JS Library 1.1.1
13106 * Copyright(c) 2006-2007, Ext JS, LLC.
13108 * Originally Released Under LGPL - original licence link has changed is not relivant.
13111 * <script type="text/javascript">
13116 * @class Roo.data.SortTypes
13118 * Defines the default sorting (casting?) comparison functions used when sorting data.
13120 Roo.data.SortTypes = {
13122 * Default sort that does nothing
13123 * @param {Mixed} s The value being converted
13124 * @return {Mixed} The comparison value
13126 none : function(s){
13131 * The regular expression used to strip tags
13135 stripTagsRE : /<\/?[^>]+>/gi,
13138 * Strips all HTML tags to sort on text only
13139 * @param {Mixed} s The value being converted
13140 * @return {String} The comparison value
13142 asText : function(s){
13143 return String(s).replace(this.stripTagsRE, "");
13147 * Strips all HTML tags to sort on text only - Case insensitive
13148 * @param {Mixed} s The value being converted
13149 * @return {String} The comparison value
13151 asUCText : function(s){
13152 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13156 * Case insensitive string
13157 * @param {Mixed} s The value being converted
13158 * @return {String} The comparison value
13160 asUCString : function(s) {
13161 return String(s).toUpperCase();
13166 * @param {Mixed} s The value being converted
13167 * @return {Number} The comparison value
13169 asDate : function(s) {
13173 if(s instanceof Date){
13174 return s.getTime();
13176 return Date.parse(String(s));
13181 * @param {Mixed} s The value being converted
13182 * @return {Float} The comparison value
13184 asFloat : function(s) {
13185 var val = parseFloat(String(s).replace(/,/g, ""));
13194 * @param {Mixed} s The value being converted
13195 * @return {Number} The comparison value
13197 asInt : function(s) {
13198 var val = parseInt(String(s).replace(/,/g, ""));
13206 * Ext JS Library 1.1.1
13207 * Copyright(c) 2006-2007, Ext JS, LLC.
13209 * Originally Released Under LGPL - original licence link has changed is not relivant.
13212 * <script type="text/javascript">
13216 * @class Roo.data.Record
13217 * Instances of this class encapsulate both record <em>definition</em> information, and record
13218 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13219 * to access Records cached in an {@link Roo.data.Store} object.<br>
13221 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13222 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13225 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13227 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13228 * {@link #create}. The parameters are the same.
13229 * @param {Array} data An associative Array of data values keyed by the field name.
13230 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13231 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13232 * not specified an integer id is generated.
13234 Roo.data.Record = function(data, id){
13235 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13240 * Generate a constructor for a specific record layout.
13241 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13242 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13243 * Each field definition object may contain the following properties: <ul>
13244 * <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,
13245 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13246 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13247 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13248 * is being used, then this is a string containing the javascript expression to reference the data relative to
13249 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13250 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13251 * this may be omitted.</p></li>
13252 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13253 * <ul><li>auto (Default, implies no conversion)</li>
13258 * <li>date</li></ul></p></li>
13259 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13260 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13261 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13262 * by the Reader into an object that will be stored in the Record. It is passed the
13263 * following parameters:<ul>
13264 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13266 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13268 * <br>usage:<br><pre><code>
13269 var TopicRecord = Roo.data.Record.create(
13270 {name: 'title', mapping: 'topic_title'},
13271 {name: 'author', mapping: 'username'},
13272 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13273 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13274 {name: 'lastPoster', mapping: 'user2'},
13275 {name: 'excerpt', mapping: 'post_text'}
13278 var myNewRecord = new TopicRecord({
13279 title: 'Do my job please',
13282 lastPost: new Date(),
13283 lastPoster: 'Animal',
13284 excerpt: 'No way dude!'
13286 myStore.add(myNewRecord);
13291 Roo.data.Record.create = function(o){
13292 var f = function(){
13293 f.superclass.constructor.apply(this, arguments);
13295 Roo.extend(f, Roo.data.Record);
13296 var p = f.prototype;
13297 p.fields = new Roo.util.MixedCollection(false, function(field){
13300 for(var i = 0, len = o.length; i < len; i++){
13301 p.fields.add(new Roo.data.Field(o[i]));
13303 f.getField = function(name){
13304 return p.fields.get(name);
13309 Roo.data.Record.AUTO_ID = 1000;
13310 Roo.data.Record.EDIT = 'edit';
13311 Roo.data.Record.REJECT = 'reject';
13312 Roo.data.Record.COMMIT = 'commit';
13314 Roo.data.Record.prototype = {
13316 * Readonly flag - true if this record has been modified.
13325 join : function(store){
13326 this.store = store;
13330 * Set the named field to the specified value.
13331 * @param {String} name The name of the field to set.
13332 * @param {Object} value The value to set the field to.
13334 set : function(name, value){
13335 if(this.data[name] == value){
13339 if(!this.modified){
13340 this.modified = {};
13342 if(typeof this.modified[name] == 'undefined'){
13343 this.modified[name] = this.data[name];
13345 this.data[name] = value;
13346 if(!this.editing && this.store){
13347 this.store.afterEdit(this);
13352 * Get the value of the named field.
13353 * @param {String} name The name of the field to get the value of.
13354 * @return {Object} The value of the field.
13356 get : function(name){
13357 return this.data[name];
13361 beginEdit : function(){
13362 this.editing = true;
13363 this.modified = {};
13367 cancelEdit : function(){
13368 this.editing = false;
13369 delete this.modified;
13373 endEdit : function(){
13374 this.editing = false;
13375 if(this.dirty && this.store){
13376 this.store.afterEdit(this);
13381 * Usually called by the {@link Roo.data.Store} which owns the Record.
13382 * Rejects all changes made to the Record since either creation, or the last commit operation.
13383 * Modified fields are reverted to their original values.
13385 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13386 * of reject operations.
13388 reject : function(){
13389 var m = this.modified;
13391 if(typeof m[n] != "function"){
13392 this.data[n] = m[n];
13395 this.dirty = false;
13396 delete this.modified;
13397 this.editing = false;
13399 this.store.afterReject(this);
13404 * Usually called by the {@link Roo.data.Store} which owns the Record.
13405 * Commits all changes made to the Record since either creation, or the last commit operation.
13407 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13408 * of commit operations.
13410 commit : function(){
13411 this.dirty = false;
13412 delete this.modified;
13413 this.editing = false;
13415 this.store.afterCommit(this);
13420 hasError : function(){
13421 return this.error != null;
13425 clearError : function(){
13430 * Creates a copy of this record.
13431 * @param {String} id (optional) A new record id if you don't want to use this record's id
13434 copy : function(newId) {
13435 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13439 * Ext JS Library 1.1.1
13440 * Copyright(c) 2006-2007, Ext JS, LLC.
13442 * Originally Released Under LGPL - original licence link has changed is not relivant.
13445 * <script type="text/javascript">
13451 * @class Roo.data.Store
13452 * @extends Roo.util.Observable
13453 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13454 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13456 * 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
13457 * has no knowledge of the format of the data returned by the Proxy.<br>
13459 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13460 * instances from the data object. These records are cached and made available through accessor functions.
13462 * Creates a new Store.
13463 * @param {Object} config A config object containing the objects needed for the Store to access data,
13464 * and read the data into Records.
13466 Roo.data.Store = function(config){
13467 this.data = new Roo.util.MixedCollection(false);
13468 this.data.getKey = function(o){
13471 this.baseParams = {};
13473 this.paramNames = {
13478 "multisort" : "_multisort"
13481 if(config && config.data){
13482 this.inlineData = config.data;
13483 delete config.data;
13486 Roo.apply(this, config);
13488 if(this.reader){ // reader passed
13489 this.reader = Roo.factory(this.reader, Roo.data);
13490 this.reader.xmodule = this.xmodule || false;
13491 if(!this.recordType){
13492 this.recordType = this.reader.recordType;
13494 if(this.reader.onMetaChange){
13495 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13499 if(this.recordType){
13500 this.fields = this.recordType.prototype.fields;
13502 this.modified = [];
13506 * @event datachanged
13507 * Fires when the data cache has changed, and a widget which is using this Store
13508 * as a Record cache should refresh its view.
13509 * @param {Store} this
13511 datachanged : true,
13513 * @event metachange
13514 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13515 * @param {Store} this
13516 * @param {Object} meta The JSON metadata
13521 * Fires when Records have been added to the Store
13522 * @param {Store} this
13523 * @param {Roo.data.Record[]} records The array of Records added
13524 * @param {Number} index The index at which the record(s) were added
13529 * Fires when a Record has been removed from the Store
13530 * @param {Store} this
13531 * @param {Roo.data.Record} record The Record that was removed
13532 * @param {Number} index The index at which the record was removed
13537 * Fires when a Record has been updated
13538 * @param {Store} this
13539 * @param {Roo.data.Record} record The Record that was updated
13540 * @param {String} operation The update operation being performed. Value may be one of:
13542 Roo.data.Record.EDIT
13543 Roo.data.Record.REJECT
13544 Roo.data.Record.COMMIT
13550 * Fires when the data cache has been cleared.
13551 * @param {Store} this
13555 * @event beforeload
13556 * Fires before a request is made for a new data object. If the beforeload handler returns false
13557 * the load action will be canceled.
13558 * @param {Store} this
13559 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13563 * @event beforeloadadd
13564 * Fires after a new set of Records has been loaded.
13565 * @param {Store} this
13566 * @param {Roo.data.Record[]} records The Records that were loaded
13567 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13569 beforeloadadd : true,
13572 * Fires after a new set of Records has been loaded, before they are added to the store.
13573 * @param {Store} this
13574 * @param {Roo.data.Record[]} records The Records that were loaded
13575 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13576 * @params {Object} return from reader
13580 * @event loadexception
13581 * Fires if an exception occurs in the Proxy during loading.
13582 * Called with the signature of the Proxy's "loadexception" event.
13583 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13586 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13587 * @param {Object} load options
13588 * @param {Object} jsonData from your request (normally this contains the Exception)
13590 loadexception : true
13594 this.proxy = Roo.factory(this.proxy, Roo.data);
13595 this.proxy.xmodule = this.xmodule || false;
13596 this.relayEvents(this.proxy, ["loadexception"]);
13598 this.sortToggle = {};
13599 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13601 Roo.data.Store.superclass.constructor.call(this);
13603 if(this.inlineData){
13604 this.loadData(this.inlineData);
13605 delete this.inlineData;
13609 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13611 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13612 * without a remote query - used by combo/forms at present.
13616 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13619 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13622 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13623 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13626 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13627 * on any HTTP request
13630 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13633 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13637 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13638 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13640 remoteSort : false,
13643 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13644 * loaded or when a record is removed. (defaults to false).
13646 pruneModifiedRecords : false,
13649 lastOptions : null,
13652 * Add Records to the Store and fires the add event.
13653 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13655 add : function(records){
13656 records = [].concat(records);
13657 for(var i = 0, len = records.length; i < len; i++){
13658 records[i].join(this);
13660 var index = this.data.length;
13661 this.data.addAll(records);
13662 this.fireEvent("add", this, records, index);
13666 * Remove a Record from the Store and fires the remove event.
13667 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13669 remove : function(record){
13670 var index = this.data.indexOf(record);
13671 this.data.removeAt(index);
13673 if(this.pruneModifiedRecords){
13674 this.modified.remove(record);
13676 this.fireEvent("remove", this, record, index);
13680 * Remove all Records from the Store and fires the clear event.
13682 removeAll : function(){
13684 if(this.pruneModifiedRecords){
13685 this.modified = [];
13687 this.fireEvent("clear", this);
13691 * Inserts Records to the Store at the given index and fires the add event.
13692 * @param {Number} index The start index at which to insert the passed Records.
13693 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13695 insert : function(index, records){
13696 records = [].concat(records);
13697 for(var i = 0, len = records.length; i < len; i++){
13698 this.data.insert(index, records[i]);
13699 records[i].join(this);
13701 this.fireEvent("add", this, records, index);
13705 * Get the index within the cache of the passed Record.
13706 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13707 * @return {Number} The index of the passed Record. Returns -1 if not found.
13709 indexOf : function(record){
13710 return this.data.indexOf(record);
13714 * Get the index within the cache of the Record with the passed id.
13715 * @param {String} id The id of the Record to find.
13716 * @return {Number} The index of the Record. Returns -1 if not found.
13718 indexOfId : function(id){
13719 return this.data.indexOfKey(id);
13723 * Get the Record with the specified id.
13724 * @param {String} id The id of the Record to find.
13725 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13727 getById : function(id){
13728 return this.data.key(id);
13732 * Get the Record at the specified index.
13733 * @param {Number} index The index of the Record to find.
13734 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13736 getAt : function(index){
13737 return this.data.itemAt(index);
13741 * Returns a range of Records between specified indices.
13742 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13743 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13744 * @return {Roo.data.Record[]} An array of Records
13746 getRange : function(start, end){
13747 return this.data.getRange(start, end);
13751 storeOptions : function(o){
13752 o = Roo.apply({}, o);
13755 this.lastOptions = o;
13759 * Loads the Record cache from the configured Proxy using the configured Reader.
13761 * If using remote paging, then the first load call must specify the <em>start</em>
13762 * and <em>limit</em> properties in the options.params property to establish the initial
13763 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13765 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13766 * and this call will return before the new data has been loaded. Perform any post-processing
13767 * in a callback function, or in a "load" event handler.</strong>
13769 * @param {Object} options An object containing properties which control loading options:<ul>
13770 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13771 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13772 * passed the following arguments:<ul>
13773 * <li>r : Roo.data.Record[]</li>
13774 * <li>options: Options object from the load call</li>
13775 * <li>success: Boolean success indicator</li></ul></li>
13776 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13777 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13780 load : function(options){
13781 options = options || {};
13782 if(this.fireEvent("beforeload", this, options) !== false){
13783 this.storeOptions(options);
13784 var p = Roo.apply(options.params || {}, this.baseParams);
13785 // if meta was not loaded from remote source.. try requesting it.
13786 if (!this.reader.metaFromRemote) {
13787 p._requestMeta = 1;
13789 if(this.sortInfo && this.remoteSort){
13790 var pn = this.paramNames;
13791 p[pn["sort"]] = this.sortInfo.field;
13792 p[pn["dir"]] = this.sortInfo.direction;
13794 if (this.multiSort) {
13795 var pn = this.paramNames;
13796 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13799 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13804 * Reloads the Record cache from the configured Proxy using the configured Reader and
13805 * the options from the last load operation performed.
13806 * @param {Object} options (optional) An object containing properties which may override the options
13807 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13808 * the most recently used options are reused).
13810 reload : function(options){
13811 this.load(Roo.applyIf(options||{}, this.lastOptions));
13815 // Called as a callback by the Reader during a load operation.
13816 loadRecords : function(o, options, success){
13817 if(!o || success === false){
13818 if(success !== false){
13819 this.fireEvent("load", this, [], options, o);
13821 if(options.callback){
13822 options.callback.call(options.scope || this, [], options, false);
13826 // if data returned failure - throw an exception.
13827 if (o.success === false) {
13828 // show a message if no listener is registered.
13829 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13830 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13832 // loadmask wil be hooked into this..
13833 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13836 var r = o.records, t = o.totalRecords || r.length;
13838 this.fireEvent("beforeloadadd", this, r, options, o);
13840 if(!options || options.add !== true){
13841 if(this.pruneModifiedRecords){
13842 this.modified = [];
13844 for(var i = 0, len = r.length; i < len; i++){
13848 this.data = this.snapshot;
13849 delete this.snapshot;
13852 this.data.addAll(r);
13853 this.totalLength = t;
13855 this.fireEvent("datachanged", this);
13857 this.totalLength = Math.max(t, this.data.length+r.length);
13861 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13863 var e = new Roo.data.Record({});
13865 e.set(this.parent.displayField, this.parent.emptyTitle);
13866 e.set(this.parent.valueField, '');
13871 this.fireEvent("load", this, r, options, o);
13872 if(options.callback){
13873 options.callback.call(options.scope || this, r, options, true);
13879 * Loads data from a passed data block. A Reader which understands the format of the data
13880 * must have been configured in the constructor.
13881 * @param {Object} data The data block from which to read the Records. The format of the data expected
13882 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13883 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13885 loadData : function(o, append){
13886 var r = this.reader.readRecords(o);
13887 this.loadRecords(r, {add: append}, true);
13891 * using 'cn' the nested child reader read the child array into it's child stores.
13892 * @param {Object} rec The record with a 'children array
13894 loadDataFromChildren : function(rec)
13896 this.loadData(this.reader.toLoadData(rec));
13901 * Gets the number of cached records.
13903 * <em>If using paging, this may not be the total size of the dataset. If the data object
13904 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13905 * the data set size</em>
13907 getCount : function(){
13908 return this.data.length || 0;
13912 * Gets the total number of records in the dataset as returned by the server.
13914 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13915 * the dataset size</em>
13917 getTotalCount : function(){
13918 return this.totalLength || 0;
13922 * Returns the sort state of the Store as an object with two properties:
13924 field {String} The name of the field by which the Records are sorted
13925 direction {String} The sort order, "ASC" or "DESC"
13928 getSortState : function(){
13929 return this.sortInfo;
13933 applySort : function(){
13934 if(this.sortInfo && !this.remoteSort){
13935 var s = this.sortInfo, f = s.field;
13936 var st = this.fields.get(f).sortType;
13937 var fn = function(r1, r2){
13938 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13939 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13941 this.data.sort(s.direction, fn);
13942 if(this.snapshot && this.snapshot != this.data){
13943 this.snapshot.sort(s.direction, fn);
13949 * Sets the default sort column and order to be used by the next load operation.
13950 * @param {String} fieldName The name of the field to sort by.
13951 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13953 setDefaultSort : function(field, dir){
13954 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13958 * Sort the Records.
13959 * If remote sorting is used, the sort is performed on the server, and the cache is
13960 * reloaded. If local sorting is used, the cache is sorted internally.
13961 * @param {String} fieldName The name of the field to sort by.
13962 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13964 sort : function(fieldName, dir){
13965 var f = this.fields.get(fieldName);
13967 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13969 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13970 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13975 this.sortToggle[f.name] = dir;
13976 this.sortInfo = {field: f.name, direction: dir};
13977 if(!this.remoteSort){
13979 this.fireEvent("datachanged", this);
13981 this.load(this.lastOptions);
13986 * Calls the specified function for each of the Records in the cache.
13987 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13988 * Returning <em>false</em> aborts and exits the iteration.
13989 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13991 each : function(fn, scope){
13992 this.data.each(fn, scope);
13996 * Gets all records modified since the last commit. Modified records are persisted across load operations
13997 * (e.g., during paging).
13998 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14000 getModifiedRecords : function(){
14001 return this.modified;
14005 createFilterFn : function(property, value, anyMatch){
14006 if(!value.exec){ // not a regex
14007 value = String(value);
14008 if(value.length == 0){
14011 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14013 return function(r){
14014 return value.test(r.data[property]);
14019 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14020 * @param {String} property A field on your records
14021 * @param {Number} start The record index to start at (defaults to 0)
14022 * @param {Number} end The last record index to include (defaults to length - 1)
14023 * @return {Number} The sum
14025 sum : function(property, start, end){
14026 var rs = this.data.items, v = 0;
14027 start = start || 0;
14028 end = (end || end === 0) ? end : rs.length-1;
14030 for(var i = start; i <= end; i++){
14031 v += (rs[i].data[property] || 0);
14037 * Filter the records by a specified property.
14038 * @param {String} field A field on your records
14039 * @param {String/RegExp} value Either a string that the field
14040 * should start with or a RegExp to test against the field
14041 * @param {Boolean} anyMatch True to match any part not just the beginning
14043 filter : function(property, value, anyMatch){
14044 var fn = this.createFilterFn(property, value, anyMatch);
14045 return fn ? this.filterBy(fn) : this.clearFilter();
14049 * Filter by a function. The specified function will be called with each
14050 * record in this data source. If the function returns true the record is included,
14051 * otherwise it is filtered.
14052 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14053 * @param {Object} scope (optional) The scope of the function (defaults to this)
14055 filterBy : function(fn, scope){
14056 this.snapshot = this.snapshot || this.data;
14057 this.data = this.queryBy(fn, scope||this);
14058 this.fireEvent("datachanged", this);
14062 * Query the records by a specified property.
14063 * @param {String} field A field on your records
14064 * @param {String/RegExp} value Either a string that the field
14065 * should start with or a RegExp to test against the field
14066 * @param {Boolean} anyMatch True to match any part not just the beginning
14067 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14069 query : function(property, value, anyMatch){
14070 var fn = this.createFilterFn(property, value, anyMatch);
14071 return fn ? this.queryBy(fn) : this.data.clone();
14075 * Query by a function. The specified function will be called with each
14076 * record in this data source. If the function returns true the record is included
14078 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14079 * @param {Object} scope (optional) The scope of the function (defaults to this)
14080 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14082 queryBy : function(fn, scope){
14083 var data = this.snapshot || this.data;
14084 return data.filterBy(fn, scope||this);
14088 * Collects unique values for a particular dataIndex from this store.
14089 * @param {String} dataIndex The property to collect
14090 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14091 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14092 * @return {Array} An array of the unique values
14094 collect : function(dataIndex, allowNull, bypassFilter){
14095 var d = (bypassFilter === true && this.snapshot) ?
14096 this.snapshot.items : this.data.items;
14097 var v, sv, r = [], l = {};
14098 for(var i = 0, len = d.length; i < len; i++){
14099 v = d[i].data[dataIndex];
14101 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14110 * Revert to a view of the Record cache with no filtering applied.
14111 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14113 clearFilter : function(suppressEvent){
14114 if(this.snapshot && this.snapshot != this.data){
14115 this.data = this.snapshot;
14116 delete this.snapshot;
14117 if(suppressEvent !== true){
14118 this.fireEvent("datachanged", this);
14124 afterEdit : function(record){
14125 if(this.modified.indexOf(record) == -1){
14126 this.modified.push(record);
14128 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14132 afterReject : function(record){
14133 this.modified.remove(record);
14134 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14138 afterCommit : function(record){
14139 this.modified.remove(record);
14140 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14144 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14145 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14147 commitChanges : function(){
14148 var m = this.modified.slice(0);
14149 this.modified = [];
14150 for(var i = 0, len = m.length; i < len; i++){
14156 * Cancel outstanding changes on all changed records.
14158 rejectChanges : function(){
14159 var m = this.modified.slice(0);
14160 this.modified = [];
14161 for(var i = 0, len = m.length; i < len; i++){
14166 onMetaChange : function(meta, rtype, o){
14167 this.recordType = rtype;
14168 this.fields = rtype.prototype.fields;
14169 delete this.snapshot;
14170 this.sortInfo = meta.sortInfo || this.sortInfo;
14171 this.modified = [];
14172 this.fireEvent('metachange', this, this.reader.meta);
14175 moveIndex : function(data, type)
14177 var index = this.indexOf(data);
14179 var newIndex = index + type;
14183 this.insert(newIndex, data);
14188 * Ext JS Library 1.1.1
14189 * Copyright(c) 2006-2007, Ext JS, LLC.
14191 * Originally Released Under LGPL - original licence link has changed is not relivant.
14194 * <script type="text/javascript">
14198 * @class Roo.data.SimpleStore
14199 * @extends Roo.data.Store
14200 * Small helper class to make creating Stores from Array data easier.
14201 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14202 * @cfg {Array} fields An array of field definition objects, or field name strings.
14203 * @cfg {Object} an existing reader (eg. copied from another store)
14204 * @cfg {Array} data The multi-dimensional array of data
14206 * @param {Object} config
14208 Roo.data.SimpleStore = function(config)
14210 Roo.data.SimpleStore.superclass.constructor.call(this, {
14212 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14215 Roo.data.Record.create(config.fields)
14217 proxy : new Roo.data.MemoryProxy(config.data)
14221 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14223 * Ext JS Library 1.1.1
14224 * Copyright(c) 2006-2007, Ext JS, LLC.
14226 * Originally Released Under LGPL - original licence link has changed is not relivant.
14229 * <script type="text/javascript">
14234 * @extends Roo.data.Store
14235 * @class Roo.data.JsonStore
14236 * Small helper class to make creating Stores for JSON data easier. <br/>
14238 var store = new Roo.data.JsonStore({
14239 url: 'get-images.php',
14241 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14244 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14245 * JsonReader and HttpProxy (unless inline data is provided).</b>
14246 * @cfg {Array} fields An array of field definition objects, or field name strings.
14248 * @param {Object} config
14250 Roo.data.JsonStore = function(c){
14251 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14252 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14253 reader: new Roo.data.JsonReader(c, c.fields)
14256 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14258 * Ext JS Library 1.1.1
14259 * Copyright(c) 2006-2007, Ext JS, LLC.
14261 * Originally Released Under LGPL - original licence link has changed is not relivant.
14264 * <script type="text/javascript">
14268 Roo.data.Field = function(config){
14269 if(typeof config == "string"){
14270 config = {name: config};
14272 Roo.apply(this, config);
14275 this.type = "auto";
14278 var st = Roo.data.SortTypes;
14279 // named sortTypes are supported, here we look them up
14280 if(typeof this.sortType == "string"){
14281 this.sortType = st[this.sortType];
14284 // set default sortType for strings and dates
14285 if(!this.sortType){
14288 this.sortType = st.asUCString;
14291 this.sortType = st.asDate;
14294 this.sortType = st.none;
14299 var stripRe = /[\$,%]/g;
14301 // prebuilt conversion function for this field, instead of
14302 // switching every time we're reading a value
14304 var cv, dateFormat = this.dateFormat;
14309 cv = function(v){ return v; };
14312 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14316 return v !== undefined && v !== null && v !== '' ?
14317 parseInt(String(v).replace(stripRe, ""), 10) : '';
14322 return v !== undefined && v !== null && v !== '' ?
14323 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14328 cv = function(v){ return v === true || v === "true" || v == 1; };
14335 if(v instanceof Date){
14339 if(dateFormat == "timestamp"){
14340 return new Date(v*1000);
14342 return Date.parseDate(v, dateFormat);
14344 var parsed = Date.parse(v);
14345 return parsed ? new Date(parsed) : null;
14354 Roo.data.Field.prototype = {
14362 * Ext JS Library 1.1.1
14363 * Copyright(c) 2006-2007, Ext JS, LLC.
14365 * Originally Released Under LGPL - original licence link has changed is not relivant.
14368 * <script type="text/javascript">
14371 // Base class for reading structured data from a data source. This class is intended to be
14372 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14375 * @class Roo.data.DataReader
14376 * Base class for reading structured data from a data source. This class is intended to be
14377 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14380 Roo.data.DataReader = function(meta, recordType){
14384 this.recordType = recordType instanceof Array ?
14385 Roo.data.Record.create(recordType) : recordType;
14388 Roo.data.DataReader.prototype = {
14391 readerType : 'Data',
14393 * Create an empty record
14394 * @param {Object} data (optional) - overlay some values
14395 * @return {Roo.data.Record} record created.
14397 newRow : function(d) {
14399 this.recordType.prototype.fields.each(function(c) {
14401 case 'int' : da[c.name] = 0; break;
14402 case 'date' : da[c.name] = new Date(); break;
14403 case 'float' : da[c.name] = 0.0; break;
14404 case 'boolean' : da[c.name] = false; break;
14405 default : da[c.name] = ""; break;
14409 return new this.recordType(Roo.apply(da, d));
14415 * Ext JS Library 1.1.1
14416 * Copyright(c) 2006-2007, Ext JS, LLC.
14418 * Originally Released Under LGPL - original licence link has changed is not relivant.
14421 * <script type="text/javascript">
14425 * @class Roo.data.DataProxy
14426 * @extends Roo.data.Observable
14427 * This class is an abstract base class for implementations which provide retrieval of
14428 * unformatted data objects.<br>
14430 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14431 * (of the appropriate type which knows how to parse the data object) to provide a block of
14432 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14434 * Custom implementations must implement the load method as described in
14435 * {@link Roo.data.HttpProxy#load}.
14437 Roo.data.DataProxy = function(){
14440 * @event beforeload
14441 * Fires before a network request is made to retrieve a data object.
14442 * @param {Object} This DataProxy object.
14443 * @param {Object} params The params parameter to the load function.
14448 * Fires before the load method's callback is called.
14449 * @param {Object} This DataProxy object.
14450 * @param {Object} o The data object.
14451 * @param {Object} arg The callback argument object passed to the load function.
14455 * @event loadexception
14456 * Fires if an Exception occurs during data retrieval.
14457 * @param {Object} This DataProxy object.
14458 * @param {Object} o The data object.
14459 * @param {Object} arg The callback argument object passed to the load function.
14460 * @param {Object} e The Exception.
14462 loadexception : true
14464 Roo.data.DataProxy.superclass.constructor.call(this);
14467 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14470 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14474 * Ext JS Library 1.1.1
14475 * Copyright(c) 2006-2007, Ext JS, LLC.
14477 * Originally Released Under LGPL - original licence link has changed is not relivant.
14480 * <script type="text/javascript">
14483 * @class Roo.data.MemoryProxy
14484 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14485 * to the Reader when its load method is called.
14487 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14489 Roo.data.MemoryProxy = function(data){
14493 Roo.data.MemoryProxy.superclass.constructor.call(this);
14497 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14500 * Load data from the requested source (in this case an in-memory
14501 * data object passed to the constructor), read the data object into
14502 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14503 * process that block using the passed callback.
14504 * @param {Object} params This parameter is not used by the MemoryProxy class.
14505 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14506 * object into a block of Roo.data.Records.
14507 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14508 * The function must be passed <ul>
14509 * <li>The Record block object</li>
14510 * <li>The "arg" argument from the load function</li>
14511 * <li>A boolean success indicator</li>
14513 * @param {Object} scope The scope in which to call the callback
14514 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14516 load : function(params, reader, callback, scope, arg){
14517 params = params || {};
14520 result = reader.readRecords(params.data ? params.data :this.data);
14522 this.fireEvent("loadexception", this, arg, null, e);
14523 callback.call(scope, null, arg, false);
14526 callback.call(scope, result, arg, true);
14530 update : function(params, records){
14535 * Ext JS Library 1.1.1
14536 * Copyright(c) 2006-2007, Ext JS, LLC.
14538 * Originally Released Under LGPL - original licence link has changed is not relivant.
14541 * <script type="text/javascript">
14544 * @class Roo.data.HttpProxy
14545 * @extends Roo.data.DataProxy
14546 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14547 * configured to reference a certain URL.<br><br>
14549 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14550 * from which the running page was served.<br><br>
14552 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14554 * Be aware that to enable the browser to parse an XML document, the server must set
14555 * the Content-Type header in the HTTP response to "text/xml".
14557 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14558 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14559 * will be used to make the request.
14561 Roo.data.HttpProxy = function(conn){
14562 Roo.data.HttpProxy.superclass.constructor.call(this);
14563 // is conn a conn config or a real conn?
14565 this.useAjax = !conn || !conn.events;
14569 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14570 // thse are take from connection...
14573 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14576 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14577 * extra parameters to each request made by this object. (defaults to undefined)
14580 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14581 * to each request made by this object. (defaults to undefined)
14584 * @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)
14587 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14590 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14596 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14600 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14601 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14602 * a finer-grained basis than the DataProxy events.
14604 getConnection : function(){
14605 return this.useAjax ? Roo.Ajax : this.conn;
14609 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14610 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14611 * process that block using the passed callback.
14612 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14613 * for the request to the remote server.
14614 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14615 * object into a block of Roo.data.Records.
14616 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14617 * The function must be passed <ul>
14618 * <li>The Record block object</li>
14619 * <li>The "arg" argument from the load function</li>
14620 * <li>A boolean success indicator</li>
14622 * @param {Object} scope The scope in which to call the callback
14623 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14625 load : function(params, reader, callback, scope, arg){
14626 if(this.fireEvent("beforeload", this, params) !== false){
14628 params : params || {},
14630 callback : callback,
14635 callback : this.loadResponse,
14639 Roo.applyIf(o, this.conn);
14640 if(this.activeRequest){
14641 Roo.Ajax.abort(this.activeRequest);
14643 this.activeRequest = Roo.Ajax.request(o);
14645 this.conn.request(o);
14648 callback.call(scope||this, null, arg, false);
14653 loadResponse : function(o, success, response){
14654 delete this.activeRequest;
14656 this.fireEvent("loadexception", this, o, response);
14657 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14662 result = o.reader.read(response);
14664 this.fireEvent("loadexception", this, o, response, e);
14665 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14669 this.fireEvent("load", this, o, o.request.arg);
14670 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14674 update : function(dataSet){
14679 updateResponse : function(dataSet){
14684 * Ext JS Library 1.1.1
14685 * Copyright(c) 2006-2007, Ext JS, LLC.
14687 * Originally Released Under LGPL - original licence link has changed is not relivant.
14690 * <script type="text/javascript">
14694 * @class Roo.data.ScriptTagProxy
14695 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14696 * other than the originating domain of the running page.<br><br>
14698 * <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
14699 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14701 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14702 * source code that is used as the source inside a <script> tag.<br><br>
14704 * In order for the browser to process the returned data, the server must wrap the data object
14705 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14706 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14707 * depending on whether the callback name was passed:
14710 boolean scriptTag = false;
14711 String cb = request.getParameter("callback");
14714 response.setContentType("text/javascript");
14716 response.setContentType("application/x-json");
14718 Writer out = response.getWriter();
14720 out.write(cb + "(");
14722 out.print(dataBlock.toJsonString());
14729 * @param {Object} config A configuration object.
14731 Roo.data.ScriptTagProxy = function(config){
14732 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14733 Roo.apply(this, config);
14734 this.head = document.getElementsByTagName("head")[0];
14737 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14739 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14741 * @cfg {String} url The URL from which to request the data object.
14744 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14748 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14749 * the server the name of the callback function set up by the load call to process the returned data object.
14750 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14751 * javascript output which calls this named function passing the data object as its only parameter.
14753 callbackParam : "callback",
14755 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14756 * name to the request.
14761 * Load data from the configured URL, read the data object into
14762 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14763 * process that block using the passed callback.
14764 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14765 * for the request to the remote server.
14766 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14767 * object into a block of Roo.data.Records.
14768 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14769 * The function must be passed <ul>
14770 * <li>The Record block object</li>
14771 * <li>The "arg" argument from the load function</li>
14772 * <li>A boolean success indicator</li>
14774 * @param {Object} scope The scope in which to call the callback
14775 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14777 load : function(params, reader, callback, scope, arg){
14778 if(this.fireEvent("beforeload", this, params) !== false){
14780 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14782 var url = this.url;
14783 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14785 url += "&_dc=" + (new Date().getTime());
14787 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14790 cb : "stcCallback"+transId,
14791 scriptId : "stcScript"+transId,
14795 callback : callback,
14801 window[trans.cb] = function(o){
14802 conn.handleResponse(o, trans);
14805 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14807 if(this.autoAbort !== false){
14811 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14813 var script = document.createElement("script");
14814 script.setAttribute("src", url);
14815 script.setAttribute("type", "text/javascript");
14816 script.setAttribute("id", trans.scriptId);
14817 this.head.appendChild(script);
14819 this.trans = trans;
14821 callback.call(scope||this, null, arg, false);
14826 isLoading : function(){
14827 return this.trans ? true : false;
14831 * Abort the current server request.
14833 abort : function(){
14834 if(this.isLoading()){
14835 this.destroyTrans(this.trans);
14840 destroyTrans : function(trans, isLoaded){
14841 this.head.removeChild(document.getElementById(trans.scriptId));
14842 clearTimeout(trans.timeoutId);
14844 window[trans.cb] = undefined;
14846 delete window[trans.cb];
14849 // if hasn't been loaded, wait for load to remove it to prevent script error
14850 window[trans.cb] = function(){
14851 window[trans.cb] = undefined;
14853 delete window[trans.cb];
14860 handleResponse : function(o, trans){
14861 this.trans = false;
14862 this.destroyTrans(trans, true);
14865 result = trans.reader.readRecords(o);
14867 this.fireEvent("loadexception", this, o, trans.arg, e);
14868 trans.callback.call(trans.scope||window, null, trans.arg, false);
14871 this.fireEvent("load", this, o, trans.arg);
14872 trans.callback.call(trans.scope||window, result, trans.arg, true);
14876 handleFailure : function(trans){
14877 this.trans = false;
14878 this.destroyTrans(trans, false);
14879 this.fireEvent("loadexception", this, null, trans.arg);
14880 trans.callback.call(trans.scope||window, null, trans.arg, false);
14884 * Ext JS Library 1.1.1
14885 * Copyright(c) 2006-2007, Ext JS, LLC.
14887 * Originally Released Under LGPL - original licence link has changed is not relivant.
14890 * <script type="text/javascript">
14894 * @class Roo.data.JsonReader
14895 * @extends Roo.data.DataReader
14896 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14897 * based on mappings in a provided Roo.data.Record constructor.
14899 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14900 * in the reply previously.
14905 var RecordDef = Roo.data.Record.create([
14906 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14907 {name: 'occupation'} // This field will use "occupation" as the mapping.
14909 var myReader = new Roo.data.JsonReader({
14910 totalProperty: "results", // The property which contains the total dataset size (optional)
14911 root: "rows", // The property which contains an Array of row objects
14912 id: "id" // The property within each row object that provides an ID for the record (optional)
14916 * This would consume a JSON file like this:
14918 { 'results': 2, 'rows': [
14919 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14920 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14923 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14924 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14925 * paged from the remote server.
14926 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14927 * @cfg {String} root name of the property which contains the Array of row objects.
14928 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14929 * @cfg {Array} fields Array of field definition objects
14931 * Create a new JsonReader
14932 * @param {Object} meta Metadata configuration options
14933 * @param {Object} recordType Either an Array of field definition objects,
14934 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14936 Roo.data.JsonReader = function(meta, recordType){
14939 // set some defaults:
14940 Roo.applyIf(meta, {
14941 totalProperty: 'total',
14942 successProperty : 'success',
14947 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14949 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14951 readerType : 'Json',
14954 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14955 * Used by Store query builder to append _requestMeta to params.
14958 metaFromRemote : false,
14960 * This method is only used by a DataProxy which has retrieved data from a remote server.
14961 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14962 * @return {Object} data A data block which is used by an Roo.data.Store object as
14963 * a cache of Roo.data.Records.
14965 read : function(response){
14966 var json = response.responseText;
14968 var o = /* eval:var:o */ eval("("+json+")");
14970 throw {message: "JsonReader.read: Json object not found"};
14976 this.metaFromRemote = true;
14977 this.meta = o.metaData;
14978 this.recordType = Roo.data.Record.create(o.metaData.fields);
14979 this.onMetaChange(this.meta, this.recordType, o);
14981 return this.readRecords(o);
14984 // private function a store will implement
14985 onMetaChange : function(meta, recordType, o){
14992 simpleAccess: function(obj, subsc) {
14999 getJsonAccessor: function(){
15001 return function(expr) {
15003 return(re.test(expr))
15004 ? new Function("obj", "return obj." + expr)
15009 return Roo.emptyFn;
15014 * Create a data block containing Roo.data.Records from an XML document.
15015 * @param {Object} o An object which contains an Array of row objects in the property specified
15016 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15017 * which contains the total size of the dataset.
15018 * @return {Object} data A data block which is used by an Roo.data.Store object as
15019 * a cache of Roo.data.Records.
15021 readRecords : function(o){
15023 * After any data loads, the raw JSON data is available for further custom processing.
15027 var s = this.meta, Record = this.recordType,
15028 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15030 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15032 if(s.totalProperty) {
15033 this.getTotal = this.getJsonAccessor(s.totalProperty);
15035 if(s.successProperty) {
15036 this.getSuccess = this.getJsonAccessor(s.successProperty);
15038 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15040 var g = this.getJsonAccessor(s.id);
15041 this.getId = function(rec) {
15043 return (r === undefined || r === "") ? null : r;
15046 this.getId = function(){return null;};
15049 for(var jj = 0; jj < fl; jj++){
15051 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15052 this.ef[jj] = this.getJsonAccessor(map);
15056 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15057 if(s.totalProperty){
15058 var vt = parseInt(this.getTotal(o), 10);
15063 if(s.successProperty){
15064 var vs = this.getSuccess(o);
15065 if(vs === false || vs === 'false'){
15070 for(var i = 0; i < c; i++){
15073 var id = this.getId(n);
15074 for(var j = 0; j < fl; j++){
15076 var v = this.ef[j](n);
15078 Roo.log('missing convert for ' + f.name);
15082 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15084 var record = new Record(values, id);
15086 records[i] = record;
15092 totalRecords : totalRecords
15095 // used when loading children.. @see loadDataFromChildren
15096 toLoadData: function(rec)
15098 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15099 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15100 return { data : data, total : data.length };
15105 * Ext JS Library 1.1.1
15106 * Copyright(c) 2006-2007, Ext JS, LLC.
15108 * Originally Released Under LGPL - original licence link has changed is not relivant.
15111 * <script type="text/javascript">
15115 * @class Roo.data.ArrayReader
15116 * @extends Roo.data.DataReader
15117 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15118 * Each element of that Array represents a row of data fields. The
15119 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15120 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15124 var RecordDef = Roo.data.Record.create([
15125 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15126 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15128 var myReader = new Roo.data.ArrayReader({
15129 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15133 * This would consume an Array like this:
15135 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15139 * Create a new JsonReader
15140 * @param {Object} meta Metadata configuration options.
15141 * @param {Object|Array} recordType Either an Array of field definition objects
15143 * @cfg {Array} fields Array of field definition objects
15144 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15145 * as specified to {@link Roo.data.Record#create},
15146 * or an {@link Roo.data.Record} object
15149 * created using {@link Roo.data.Record#create}.
15151 Roo.data.ArrayReader = function(meta, recordType)
15153 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15156 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15159 * Create a data block containing Roo.data.Records from an XML document.
15160 * @param {Object} o An Array of row objects which represents the dataset.
15161 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15162 * a cache of Roo.data.Records.
15164 readRecords : function(o)
15166 var sid = this.meta ? this.meta.id : null;
15167 var recordType = this.recordType, fields = recordType.prototype.fields;
15170 for(var i = 0; i < root.length; i++){
15173 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15174 for(var j = 0, jlen = fields.length; j < jlen; j++){
15175 var f = fields.items[j];
15176 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15177 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15179 values[f.name] = v;
15181 var record = new recordType(values, id);
15183 records[records.length] = record;
15187 totalRecords : records.length
15190 // used when loading children.. @see loadDataFromChildren
15191 toLoadData: function(rec)
15193 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15194 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15205 * @class Roo.bootstrap.ComboBox
15206 * @extends Roo.bootstrap.TriggerField
15207 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15208 * @cfg {Boolean} append (true|false) default false
15209 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15210 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15211 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15212 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15213 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15214 * @cfg {Boolean} animate default true
15215 * @cfg {Boolean} emptyResultText only for touch device
15216 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15217 * @cfg {String} emptyTitle default ''
15218 * @cfg {Number} width fixed with? experimental
15220 * Create a new ComboBox.
15221 * @param {Object} config Configuration options
15223 Roo.bootstrap.ComboBox = function(config){
15224 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15228 * Fires when the dropdown list is expanded
15229 * @param {Roo.bootstrap.ComboBox} combo This combo box
15234 * Fires when the dropdown list is collapsed
15235 * @param {Roo.bootstrap.ComboBox} combo This combo box
15239 * @event beforeselect
15240 * Fires before a list item is selected. Return false to cancel the selection.
15241 * @param {Roo.bootstrap.ComboBox} combo This combo box
15242 * @param {Roo.data.Record} record The data record returned from the underlying store
15243 * @param {Number} index The index of the selected item in the dropdown list
15245 'beforeselect' : true,
15248 * Fires when a list item is selected
15249 * @param {Roo.bootstrap.ComboBox} combo This combo box
15250 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15251 * @param {Number} index The index of the selected item in the dropdown list
15255 * @event beforequery
15256 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15257 * The event object passed has these properties:
15258 * @param {Roo.bootstrap.ComboBox} combo This combo box
15259 * @param {String} query The query
15260 * @param {Boolean} forceAll true to force "all" query
15261 * @param {Boolean} cancel true to cancel the query
15262 * @param {Object} e The query event object
15264 'beforequery': true,
15267 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15268 * @param {Roo.bootstrap.ComboBox} combo This combo box
15273 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15274 * @param {Roo.bootstrap.ComboBox} combo This combo box
15275 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15280 * Fires when the remove value from the combobox array
15281 * @param {Roo.bootstrap.ComboBox} combo This combo box
15285 * @event afterremove
15286 * Fires when the remove value from the combobox array
15287 * @param {Roo.bootstrap.ComboBox} combo This combo box
15289 'afterremove' : true,
15291 * @event specialfilter
15292 * Fires when specialfilter
15293 * @param {Roo.bootstrap.ComboBox} combo This combo box
15295 'specialfilter' : true,
15298 * Fires when tick the element
15299 * @param {Roo.bootstrap.ComboBox} combo This combo box
15303 * @event touchviewdisplay
15304 * Fires when touch view require special display (default is using displayField)
15305 * @param {Roo.bootstrap.ComboBox} combo This combo box
15306 * @param {Object} cfg set html .
15308 'touchviewdisplay' : true
15313 this.tickItems = [];
15315 this.selectedIndex = -1;
15316 if(this.mode == 'local'){
15317 if(config.queryDelay === undefined){
15318 this.queryDelay = 10;
15320 if(config.minChars === undefined){
15326 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15329 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15330 * rendering into an Roo.Editor, defaults to false)
15333 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15334 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15337 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15340 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15341 * the dropdown list (defaults to undefined, with no header element)
15345 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15349 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15351 listWidth: undefined,
15353 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15354 * mode = 'remote' or 'text' if mode = 'local')
15356 displayField: undefined,
15359 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15360 * mode = 'remote' or 'value' if mode = 'local').
15361 * Note: use of a valueField requires the user make a selection
15362 * in order for a value to be mapped.
15364 valueField: undefined,
15366 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15371 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15372 * field's data value (defaults to the underlying DOM element's name)
15374 hiddenName: undefined,
15376 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15380 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15382 selectedClass: 'active',
15385 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15389 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15390 * anchor positions (defaults to 'tl-bl')
15392 listAlign: 'tl-bl?',
15394 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15398 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15399 * query specified by the allQuery config option (defaults to 'query')
15401 triggerAction: 'query',
15403 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15404 * (defaults to 4, does not apply if editable = false)
15408 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15409 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15413 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15414 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15418 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15419 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15423 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15424 * when editable = true (defaults to false)
15426 selectOnFocus:false,
15428 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15430 queryParam: 'query',
15432 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15433 * when mode = 'remote' (defaults to 'Loading...')
15435 loadingText: 'Loading...',
15437 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15441 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15445 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15446 * traditional select (defaults to true)
15450 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15454 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15458 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15459 * listWidth has a higher value)
15463 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15464 * allow the user to set arbitrary text into the field (defaults to false)
15466 forceSelection:false,
15468 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15469 * if typeAhead = true (defaults to 250)
15471 typeAheadDelay : 250,
15473 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15474 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15476 valueNotFoundText : undefined,
15478 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15480 blockFocus : false,
15483 * @cfg {Boolean} disableClear Disable showing of clear button.
15485 disableClear : false,
15487 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15489 alwaysQuery : false,
15492 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15497 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15499 invalidClass : "has-warning",
15502 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15504 validClass : "has-success",
15507 * @cfg {Boolean} specialFilter (true|false) special filter default false
15509 specialFilter : false,
15512 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15514 mobileTouchView : true,
15517 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15519 useNativeIOS : false,
15522 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15524 mobile_restrict_height : false,
15526 ios_options : false,
15538 btnPosition : 'right',
15539 triggerList : true,
15540 showToggleBtn : true,
15542 emptyResultText: 'Empty',
15543 triggerText : 'Select',
15547 // element that contains real text value.. (when hidden is used..)
15549 getAutoCreate : function()
15554 * Render classic select for iso
15557 if(Roo.isIOS && this.useNativeIOS){
15558 cfg = this.getAutoCreateNativeIOS();
15566 if(Roo.isTouch && this.mobileTouchView){
15567 cfg = this.getAutoCreateTouchView();
15574 if(!this.tickable){
15575 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15580 * ComboBox with tickable selections
15583 var align = this.labelAlign || this.parentLabelAlign();
15586 cls : 'form-group roo-combobox-tickable' //input-group
15589 var btn_text_select = '';
15590 var btn_text_done = '';
15591 var btn_text_cancel = '';
15593 if (this.btn_text_show) {
15594 btn_text_select = 'Select';
15595 btn_text_done = 'Done';
15596 btn_text_cancel = 'Cancel';
15601 cls : 'tickable-buttons',
15606 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15607 //html : this.triggerText
15608 html: btn_text_select
15614 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15616 html: btn_text_done
15622 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15624 html: btn_text_cancel
15630 buttons.cn.unshift({
15632 cls: 'roo-select2-search-field-input'
15638 Roo.each(buttons.cn, function(c){
15640 c.cls += ' btn-' + _this.size;
15643 if (_this.disabled) {
15650 style : 'display: contents',
15655 cls: 'form-hidden-field'
15659 cls: 'roo-select2-choices',
15663 cls: 'roo-select2-search-field',
15674 cls: 'roo-select2-container input-group roo-select2-container-multi',
15680 // cls: 'typeahead typeahead-long dropdown-menu',
15681 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15686 if(this.hasFeedback && !this.allowBlank){
15690 cls: 'glyphicon form-control-feedback'
15693 combobox.cn.push(feedback);
15700 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15701 tooltip : 'This field is required'
15703 if (Roo.bootstrap.version == 4) {
15706 style : 'display:none'
15709 if (align ==='left' && this.fieldLabel.length) {
15711 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15718 cls : 'control-label col-form-label',
15719 html : this.fieldLabel
15731 var labelCfg = cfg.cn[1];
15732 var contentCfg = cfg.cn[2];
15735 if(this.indicatorpos == 'right'){
15741 cls : 'control-label col-form-label',
15745 html : this.fieldLabel
15761 labelCfg = cfg.cn[0];
15762 contentCfg = cfg.cn[1];
15766 if(this.labelWidth > 12){
15767 labelCfg.style = "width: " + this.labelWidth + 'px';
15769 if(this.width * 1 > 0){
15770 contentCfg.style = "width: " + this.width + 'px';
15772 if(this.labelWidth < 13 && this.labelmd == 0){
15773 this.labelmd = this.labelWidth;
15776 if(this.labellg > 0){
15777 labelCfg.cls += ' col-lg-' + this.labellg;
15778 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15781 if(this.labelmd > 0){
15782 labelCfg.cls += ' col-md-' + this.labelmd;
15783 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15786 if(this.labelsm > 0){
15787 labelCfg.cls += ' col-sm-' + this.labelsm;
15788 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15791 if(this.labelxs > 0){
15792 labelCfg.cls += ' col-xs-' + this.labelxs;
15793 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15797 } else if ( this.fieldLabel.length) {
15798 // Roo.log(" label");
15803 //cls : 'input-group-addon',
15804 html : this.fieldLabel
15809 if(this.indicatorpos == 'right'){
15813 //cls : 'input-group-addon',
15814 html : this.fieldLabel
15824 // Roo.log(" no label && no align");
15831 ['xs','sm','md','lg'].map(function(size){
15832 if (settings[size]) {
15833 cfg.cls += ' col-' + size + '-' + settings[size];
15841 _initEventsCalled : false,
15844 initEvents: function()
15846 if (this._initEventsCalled) { // as we call render... prevent looping...
15849 this._initEventsCalled = true;
15852 throw "can not find store for combo";
15855 this.indicator = this.indicatorEl();
15857 this.store = Roo.factory(this.store, Roo.data);
15858 this.store.parent = this;
15860 // if we are building from html. then this element is so complex, that we can not really
15861 // use the rendered HTML.
15862 // so we have to trash and replace the previous code.
15863 if (Roo.XComponent.build_from_html) {
15864 // remove this element....
15865 var e = this.el.dom, k=0;
15866 while (e ) { e = e.previousSibling; ++k;}
15871 this.rendered = false;
15873 this.render(this.parent().getChildContainer(true), k);
15876 if(Roo.isIOS && this.useNativeIOS){
15877 this.initIOSView();
15885 if(Roo.isTouch && this.mobileTouchView){
15886 this.initTouchView();
15891 this.initTickableEvents();
15895 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15897 if(this.hiddenName){
15899 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15901 this.hiddenField.dom.value =
15902 this.hiddenValue !== undefined ? this.hiddenValue :
15903 this.value !== undefined ? this.value : '';
15905 // prevent input submission
15906 this.el.dom.removeAttribute('name');
15907 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15912 // this.el.dom.setAttribute('autocomplete', 'off');
15915 var cls = 'x-combo-list';
15917 //this.list = new Roo.Layer({
15918 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15924 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15925 _this.list.setWidth(lw);
15928 this.list.on('mouseover', this.onViewOver, this);
15929 this.list.on('mousemove', this.onViewMove, this);
15930 this.list.on('scroll', this.onViewScroll, this);
15933 this.list.swallowEvent('mousewheel');
15934 this.assetHeight = 0;
15937 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15938 this.assetHeight += this.header.getHeight();
15941 this.innerList = this.list.createChild({cls:cls+'-inner'});
15942 this.innerList.on('mouseover', this.onViewOver, this);
15943 this.innerList.on('mousemove', this.onViewMove, this);
15944 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15946 if(this.allowBlank && !this.pageSize && !this.disableClear){
15947 this.footer = this.list.createChild({cls:cls+'-ft'});
15948 this.pageTb = new Roo.Toolbar(this.footer);
15952 this.footer = this.list.createChild({cls:cls+'-ft'});
15953 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15954 {pageSize: this.pageSize});
15958 if (this.pageTb && this.allowBlank && !this.disableClear) {
15960 this.pageTb.add(new Roo.Toolbar.Fill(), {
15961 cls: 'x-btn-icon x-btn-clear',
15963 handler: function()
15966 _this.clearValue();
15967 _this.onSelect(false, -1);
15972 this.assetHeight += this.footer.getHeight();
15977 this.tpl = Roo.bootstrap.version == 4 ?
15978 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15979 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15982 this.view = new Roo.View(this.list, this.tpl, {
15983 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15985 //this.view.wrapEl.setDisplayed(false);
15986 this.view.on('click', this.onViewClick, this);
15989 this.store.on('beforeload', this.onBeforeLoad, this);
15990 this.store.on('load', this.onLoad, this);
15991 this.store.on('loadexception', this.onLoadException, this);
15993 if(this.resizable){
15994 this.resizer = new Roo.Resizable(this.list, {
15995 pinned:true, handles:'se'
15997 this.resizer.on('resize', function(r, w, h){
15998 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15999 this.listWidth = w;
16000 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16001 this.restrictHeight();
16003 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16006 if(!this.editable){
16007 this.editable = true;
16008 this.setEditable(false);
16013 if (typeof(this.events.add.listeners) != 'undefined') {
16015 this.addicon = this.wrap.createChild(
16016 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16018 this.addicon.on('click', function(e) {
16019 this.fireEvent('add', this);
16022 if (typeof(this.events.edit.listeners) != 'undefined') {
16024 this.editicon = this.wrap.createChild(
16025 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16026 if (this.addicon) {
16027 this.editicon.setStyle('margin-left', '40px');
16029 this.editicon.on('click', function(e) {
16031 // we fire even if inothing is selected..
16032 this.fireEvent('edit', this, this.lastData );
16038 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16039 "up" : function(e){
16040 this.inKeyMode = true;
16044 "down" : function(e){
16045 if(!this.isExpanded()){
16046 this.onTriggerClick();
16048 this.inKeyMode = true;
16053 "enter" : function(e){
16054 // this.onViewClick();
16058 if(this.fireEvent("specialkey", this, e)){
16059 this.onViewClick(false);
16065 "esc" : function(e){
16069 "tab" : function(e){
16072 if(this.fireEvent("specialkey", this, e)){
16073 this.onViewClick(false);
16081 doRelay : function(foo, bar, hname){
16082 if(hname == 'down' || this.scope.isExpanded()){
16083 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16092 this.queryDelay = Math.max(this.queryDelay || 10,
16093 this.mode == 'local' ? 10 : 250);
16096 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16098 if(this.typeAhead){
16099 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16101 if(this.editable !== false){
16102 this.inputEl().on("keyup", this.onKeyUp, this);
16104 if(this.forceSelection){
16105 this.inputEl().on('blur', this.doForce, this);
16109 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16110 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16114 initTickableEvents: function()
16118 if(this.hiddenName){
16120 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16122 this.hiddenField.dom.value =
16123 this.hiddenValue !== undefined ? this.hiddenValue :
16124 this.value !== undefined ? this.value : '';
16126 // prevent input submission
16127 this.el.dom.removeAttribute('name');
16128 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16133 // this.list = this.el.select('ul.dropdown-menu',true).first();
16135 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16136 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16137 if(this.triggerList){
16138 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16141 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16142 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16144 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16145 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16147 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16148 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16150 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16151 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16152 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16155 this.cancelBtn.hide();
16160 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16161 _this.list.setWidth(lw);
16164 this.list.on('mouseover', this.onViewOver, this);
16165 this.list.on('mousemove', this.onViewMove, this);
16167 this.list.on('scroll', this.onViewScroll, this);
16170 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16171 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16174 this.view = new Roo.View(this.list, this.tpl, {
16179 selectedClass: this.selectedClass
16182 //this.view.wrapEl.setDisplayed(false);
16183 this.view.on('click', this.onViewClick, this);
16187 this.store.on('beforeload', this.onBeforeLoad, this);
16188 this.store.on('load', this.onLoad, this);
16189 this.store.on('loadexception', this.onLoadException, this);
16192 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16193 "up" : function(e){
16194 this.inKeyMode = true;
16198 "down" : function(e){
16199 this.inKeyMode = true;
16203 "enter" : function(e){
16204 if(this.fireEvent("specialkey", this, e)){
16205 this.onViewClick(false);
16211 "esc" : function(e){
16212 this.onTickableFooterButtonClick(e, false, false);
16215 "tab" : function(e){
16216 this.fireEvent("specialkey", this, e);
16218 this.onTickableFooterButtonClick(e, false, false);
16225 doRelay : function(e, fn, key){
16226 if(this.scope.isExpanded()){
16227 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16236 this.queryDelay = Math.max(this.queryDelay || 10,
16237 this.mode == 'local' ? 10 : 250);
16240 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16242 if(this.typeAhead){
16243 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16246 if(this.editable !== false){
16247 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16250 this.indicator = this.indicatorEl();
16252 if(this.indicator){
16253 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16254 this.indicator.hide();
16259 onDestroy : function(){
16261 this.view.setStore(null);
16262 this.view.el.removeAllListeners();
16263 this.view.el.remove();
16264 this.view.purgeListeners();
16267 this.list.dom.innerHTML = '';
16271 this.store.un('beforeload', this.onBeforeLoad, this);
16272 this.store.un('load', this.onLoad, this);
16273 this.store.un('loadexception', this.onLoadException, this);
16275 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16279 fireKey : function(e){
16280 if(e.isNavKeyPress() && !this.list.isVisible()){
16281 this.fireEvent("specialkey", this, e);
16286 onResize: function(w, h)
16290 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16292 // if(typeof w != 'number'){
16293 // // we do not handle it!?!?
16296 // var tw = this.trigger.getWidth();
16297 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16298 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16300 // this.inputEl().setWidth( this.adjustWidth('input', x));
16302 // //this.trigger.setStyle('left', x+'px');
16304 // if(this.list && this.listWidth === undefined){
16305 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16306 // this.list.setWidth(lw);
16307 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16315 * Allow or prevent the user from directly editing the field text. If false is passed,
16316 * the user will only be able to select from the items defined in the dropdown list. This method
16317 * is the runtime equivalent of setting the 'editable' config option at config time.
16318 * @param {Boolean} value True to allow the user to directly edit the field text
16320 setEditable : function(value){
16321 if(value == this.editable){
16324 this.editable = value;
16326 this.inputEl().dom.setAttribute('readOnly', true);
16327 this.inputEl().on('mousedown', this.onTriggerClick, this);
16328 this.inputEl().addClass('x-combo-noedit');
16330 this.inputEl().dom.setAttribute('readOnly', false);
16331 this.inputEl().un('mousedown', this.onTriggerClick, this);
16332 this.inputEl().removeClass('x-combo-noedit');
16338 onBeforeLoad : function(combo,opts){
16339 if(!this.hasFocus){
16343 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16345 this.restrictHeight();
16346 this.selectedIndex = -1;
16350 onLoad : function(){
16352 this.hasQuery = false;
16354 if(!this.hasFocus){
16358 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16359 this.loading.hide();
16362 if(this.store.getCount() > 0){
16365 this.restrictHeight();
16366 if(this.lastQuery == this.allQuery){
16367 if(this.editable && !this.tickable){
16368 this.inputEl().dom.select();
16372 !this.selectByValue(this.value, true) &&
16375 !this.store.lastOptions ||
16376 typeof(this.store.lastOptions.add) == 'undefined' ||
16377 this.store.lastOptions.add != true
16380 this.select(0, true);
16383 if(this.autoFocus){
16386 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16387 this.taTask.delay(this.typeAheadDelay);
16391 this.onEmptyResults();
16397 onLoadException : function()
16399 this.hasQuery = false;
16401 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16402 this.loading.hide();
16405 if(this.tickable && this.editable){
16410 // only causes errors at present
16411 //Roo.log(this.store.reader.jsonData);
16412 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16414 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16420 onTypeAhead : function(){
16421 if(this.store.getCount() > 0){
16422 var r = this.store.getAt(0);
16423 var newValue = r.data[this.displayField];
16424 var len = newValue.length;
16425 var selStart = this.getRawValue().length;
16427 if(selStart != len){
16428 this.setRawValue(newValue);
16429 this.selectText(selStart, newValue.length);
16435 onSelect : function(record, index){
16437 if(this.fireEvent('beforeselect', this, record, index) !== false){
16439 this.setFromData(index > -1 ? record.data : false);
16442 this.fireEvent('select', this, record, index);
16447 * Returns the currently selected field value or empty string if no value is set.
16448 * @return {String} value The selected value
16450 getValue : function()
16452 if(Roo.isIOS && this.useNativeIOS){
16453 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16457 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16460 if(this.valueField){
16461 return typeof this.value != 'undefined' ? this.value : '';
16463 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16467 getRawValue : function()
16469 if(Roo.isIOS && this.useNativeIOS){
16470 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16473 var v = this.inputEl().getValue();
16479 * Clears any text/value currently set in the field
16481 clearValue : function(){
16483 if(this.hiddenField){
16484 this.hiddenField.dom.value = '';
16487 this.setRawValue('');
16488 this.lastSelectionText = '';
16489 this.lastData = false;
16491 var close = this.closeTriggerEl();
16502 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16503 * will be displayed in the field. If the value does not match the data value of an existing item,
16504 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16505 * Otherwise the field will be blank (although the value will still be set).
16506 * @param {String} value The value to match
16508 setValue : function(v)
16510 if(Roo.isIOS && this.useNativeIOS){
16511 this.setIOSValue(v);
16521 if(this.valueField){
16522 var r = this.findRecord(this.valueField, v);
16524 text = r.data[this.displayField];
16525 }else if(this.valueNotFoundText !== undefined){
16526 text = this.valueNotFoundText;
16529 this.lastSelectionText = text;
16530 if(this.hiddenField){
16531 this.hiddenField.dom.value = v;
16533 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16536 var close = this.closeTriggerEl();
16539 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16545 * @property {Object} the last set data for the element
16550 * Sets the value of the field based on a object which is related to the record format for the store.
16551 * @param {Object} value the value to set as. or false on reset?
16553 setFromData : function(o){
16560 var dv = ''; // display value
16561 var vv = ''; // value value..
16563 if (this.displayField) {
16564 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16566 // this is an error condition!!!
16567 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16570 if(this.valueField){
16571 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16574 var close = this.closeTriggerEl();
16577 if(dv.length || vv * 1 > 0){
16579 this.blockFocus=true;
16585 if(this.hiddenField){
16586 this.hiddenField.dom.value = vv;
16588 this.lastSelectionText = dv;
16589 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16593 // no hidden field.. - we store the value in 'value', but still display
16594 // display field!!!!
16595 this.lastSelectionText = dv;
16596 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16603 reset : function(){
16604 // overridden so that last data is reset..
16611 this.setValue(this.originalValue);
16612 //this.clearInvalid();
16613 this.lastData = false;
16615 this.view.clearSelections();
16621 findRecord : function(prop, value){
16623 if(this.store.getCount() > 0){
16624 this.store.each(function(r){
16625 if(r.data[prop] == value){
16635 getName: function()
16637 // returns hidden if it's set..
16638 if (!this.rendered) {return ''};
16639 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16643 onViewMove : function(e, t){
16644 this.inKeyMode = false;
16648 onViewOver : function(e, t){
16649 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16652 var item = this.view.findItemFromChild(t);
16655 var index = this.view.indexOf(item);
16656 this.select(index, false);
16661 onViewClick : function(view, doFocus, el, e)
16663 var index = this.view.getSelectedIndexes()[0];
16665 var r = this.store.getAt(index);
16669 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16676 Roo.each(this.tickItems, function(v,k){
16678 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16680 _this.tickItems.splice(k, 1);
16682 if(typeof(e) == 'undefined' && view == false){
16683 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16695 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16696 this.tickItems.push(r.data);
16699 if(typeof(e) == 'undefined' && view == false){
16700 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16707 this.onSelect(r, index);
16709 if(doFocus !== false && !this.blockFocus){
16710 this.inputEl().focus();
16715 restrictHeight : function(){
16716 //this.innerList.dom.style.height = '';
16717 //var inner = this.innerList.dom;
16718 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16719 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16720 //this.list.beginUpdate();
16721 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16722 this.list.alignTo(this.inputEl(), this.listAlign);
16723 this.list.alignTo(this.inputEl(), this.listAlign);
16724 //this.list.endUpdate();
16728 onEmptyResults : function(){
16730 if(this.tickable && this.editable){
16731 this.hasFocus = false;
16732 this.restrictHeight();
16740 * Returns true if the dropdown list is expanded, else false.
16742 isExpanded : function(){
16743 return this.list.isVisible();
16747 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16748 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16749 * @param {String} value The data value of the item to select
16750 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16751 * selected item if it is not currently in view (defaults to true)
16752 * @return {Boolean} True if the value matched an item in the list, else false
16754 selectByValue : function(v, scrollIntoView){
16755 if(v !== undefined && v !== null){
16756 var r = this.findRecord(this.valueField || this.displayField, v);
16758 this.select(this.store.indexOf(r), scrollIntoView);
16766 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16767 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16768 * @param {Number} index The zero-based index of the list item to select
16769 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16770 * selected item if it is not currently in view (defaults to true)
16772 select : function(index, scrollIntoView){
16773 this.selectedIndex = index;
16774 this.view.select(index);
16775 if(scrollIntoView !== false){
16776 var el = this.view.getNode(index);
16778 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16781 this.list.scrollChildIntoView(el, false);
16787 selectNext : function(){
16788 var ct = this.store.getCount();
16790 if(this.selectedIndex == -1){
16792 }else if(this.selectedIndex < ct-1){
16793 this.select(this.selectedIndex+1);
16799 selectPrev : function(){
16800 var ct = this.store.getCount();
16802 if(this.selectedIndex == -1){
16804 }else if(this.selectedIndex != 0){
16805 this.select(this.selectedIndex-1);
16811 onKeyUp : function(e){
16812 if(this.editable !== false && !e.isSpecialKey()){
16813 this.lastKey = e.getKey();
16814 this.dqTask.delay(this.queryDelay);
16819 validateBlur : function(){
16820 return !this.list || !this.list.isVisible();
16824 initQuery : function(){
16826 var v = this.getRawValue();
16828 if(this.tickable && this.editable){
16829 v = this.tickableInputEl().getValue();
16836 doForce : function(){
16837 if(this.inputEl().dom.value.length > 0){
16838 this.inputEl().dom.value =
16839 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16845 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16846 * query allowing the query action to be canceled if needed.
16847 * @param {String} query The SQL query to execute
16848 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16849 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16850 * saved in the current store (defaults to false)
16852 doQuery : function(q, forceAll){
16854 if(q === undefined || q === null){
16859 forceAll: forceAll,
16863 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16868 forceAll = qe.forceAll;
16869 if(forceAll === true || (q.length >= this.minChars)){
16871 this.hasQuery = true;
16873 if(this.lastQuery != q || this.alwaysQuery){
16874 this.lastQuery = q;
16875 if(this.mode == 'local'){
16876 this.selectedIndex = -1;
16878 this.store.clearFilter();
16881 if(this.specialFilter){
16882 this.fireEvent('specialfilter', this);
16887 this.store.filter(this.displayField, q);
16890 this.store.fireEvent("datachanged", this.store);
16897 this.store.baseParams[this.queryParam] = q;
16899 var options = {params : this.getParams(q)};
16902 options.add = true;
16903 options.params.start = this.page * this.pageSize;
16906 this.store.load(options);
16909 * this code will make the page width larger, at the beginning, the list not align correctly,
16910 * we should expand the list on onLoad
16911 * so command out it
16916 this.selectedIndex = -1;
16921 this.loadNext = false;
16925 getParams : function(q){
16927 //p[this.queryParam] = q;
16931 p.limit = this.pageSize;
16937 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16939 collapse : function(){
16940 if(!this.isExpanded()){
16946 this.hasFocus = false;
16950 this.cancelBtn.hide();
16951 this.trigger.show();
16954 this.tickableInputEl().dom.value = '';
16955 this.tickableInputEl().blur();
16960 Roo.get(document).un('mousedown', this.collapseIf, this);
16961 Roo.get(document).un('mousewheel', this.collapseIf, this);
16962 if (!this.editable) {
16963 Roo.get(document).un('keydown', this.listKeyPress, this);
16965 this.fireEvent('collapse', this);
16971 collapseIf : function(e){
16972 var in_combo = e.within(this.el);
16973 var in_list = e.within(this.list);
16974 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16976 if (in_combo || in_list || is_list) {
16977 //e.stopPropagation();
16982 this.onTickableFooterButtonClick(e, false, false);
16990 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16992 expand : function(){
16994 if(this.isExpanded() || !this.hasFocus){
16998 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16999 this.list.setWidth(lw);
17005 this.restrictHeight();
17009 this.tickItems = Roo.apply([], this.item);
17012 this.cancelBtn.show();
17013 this.trigger.hide();
17016 this.tickableInputEl().focus();
17021 Roo.get(document).on('mousedown', this.collapseIf, this);
17022 Roo.get(document).on('mousewheel', this.collapseIf, this);
17023 if (!this.editable) {
17024 Roo.get(document).on('keydown', this.listKeyPress, this);
17027 this.fireEvent('expand', this);
17031 // Implements the default empty TriggerField.onTriggerClick function
17032 onTriggerClick : function(e)
17034 Roo.log('trigger click');
17036 if(this.disabled || !this.triggerList){
17041 this.loadNext = false;
17043 if(this.isExpanded()){
17045 if (!this.blockFocus) {
17046 this.inputEl().focus();
17050 this.hasFocus = true;
17051 if(this.triggerAction == 'all') {
17052 this.doQuery(this.allQuery, true);
17054 this.doQuery(this.getRawValue());
17056 if (!this.blockFocus) {
17057 this.inputEl().focus();
17062 onTickableTriggerClick : function(e)
17069 this.loadNext = false;
17070 this.hasFocus = true;
17072 if(this.triggerAction == 'all') {
17073 this.doQuery(this.allQuery, true);
17075 this.doQuery(this.getRawValue());
17079 onSearchFieldClick : function(e)
17081 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17082 this.onTickableFooterButtonClick(e, false, false);
17086 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17091 this.loadNext = false;
17092 this.hasFocus = true;
17094 if(this.triggerAction == 'all') {
17095 this.doQuery(this.allQuery, true);
17097 this.doQuery(this.getRawValue());
17101 listKeyPress : function(e)
17103 //Roo.log('listkeypress');
17104 // scroll to first matching element based on key pres..
17105 if (e.isSpecialKey()) {
17108 var k = String.fromCharCode(e.getKey()).toUpperCase();
17111 var csel = this.view.getSelectedNodes();
17112 var cselitem = false;
17114 var ix = this.view.indexOf(csel[0]);
17115 cselitem = this.store.getAt(ix);
17116 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17122 this.store.each(function(v) {
17124 // start at existing selection.
17125 if (cselitem.id == v.id) {
17131 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17132 match = this.store.indexOf(v);
17138 if (match === false) {
17139 return true; // no more action?
17142 this.view.select(match);
17143 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17144 sn.scrollIntoView(sn.dom.parentNode, false);
17147 onViewScroll : function(e, t){
17149 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){
17153 this.hasQuery = true;
17155 this.loading = this.list.select('.loading', true).first();
17157 if(this.loading === null){
17158 this.list.createChild({
17160 cls: 'loading roo-select2-more-results roo-select2-active',
17161 html: 'Loading more results...'
17164 this.loading = this.list.select('.loading', true).first();
17166 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17168 this.loading.hide();
17171 this.loading.show();
17176 this.loadNext = true;
17178 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17183 addItem : function(o)
17185 var dv = ''; // display value
17187 if (this.displayField) {
17188 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17190 // this is an error condition!!!
17191 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17198 var choice = this.choices.createChild({
17200 cls: 'roo-select2-search-choice',
17209 cls: 'roo-select2-search-choice-close fa fa-times',
17214 }, this.searchField);
17216 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17218 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17226 this.inputEl().dom.value = '';
17231 onRemoveItem : function(e, _self, o)
17233 e.preventDefault();
17235 this.lastItem = Roo.apply([], this.item);
17237 var index = this.item.indexOf(o.data) * 1;
17240 Roo.log('not this item?!');
17244 this.item.splice(index, 1);
17249 this.fireEvent('remove', this, e);
17255 syncValue : function()
17257 if(!this.item.length){
17264 Roo.each(this.item, function(i){
17265 if(_this.valueField){
17266 value.push(i[_this.valueField]);
17273 this.value = value.join(',');
17275 if(this.hiddenField){
17276 this.hiddenField.dom.value = this.value;
17279 this.store.fireEvent("datachanged", this.store);
17284 clearItem : function()
17286 if(!this.multiple){
17292 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17300 if(this.tickable && !Roo.isTouch){
17301 this.view.refresh();
17305 inputEl: function ()
17307 if(Roo.isIOS && this.useNativeIOS){
17308 return this.el.select('select.roo-ios-select', true).first();
17311 if(Roo.isTouch && this.mobileTouchView){
17312 return this.el.select('input.form-control',true).first();
17316 return this.searchField;
17319 return this.el.select('input.form-control',true).first();
17322 onTickableFooterButtonClick : function(e, btn, el)
17324 e.preventDefault();
17326 this.lastItem = Roo.apply([], this.item);
17328 if(btn && btn.name == 'cancel'){
17329 this.tickItems = Roo.apply([], this.item);
17338 Roo.each(this.tickItems, function(o){
17346 validate : function()
17348 if(this.getVisibilityEl().hasClass('hidden')){
17352 var v = this.getRawValue();
17355 v = this.getValue();
17358 if(this.disabled || this.allowBlank || v.length){
17363 this.markInvalid();
17367 tickableInputEl : function()
17369 if(!this.tickable || !this.editable){
17370 return this.inputEl();
17373 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17377 getAutoCreateTouchView : function()
17382 cls: 'form-group' //input-group
17388 type : this.inputType,
17389 cls : 'form-control x-combo-noedit',
17390 autocomplete: 'new-password',
17391 placeholder : this.placeholder || '',
17396 input.name = this.name;
17400 input.cls += ' input-' + this.size;
17403 if (this.disabled) {
17404 input.disabled = true;
17408 cls : 'roo-combobox-wrap',
17415 inputblock.cls += ' input-group';
17417 inputblock.cn.unshift({
17419 cls : 'input-group-addon input-group-prepend input-group-text',
17424 if(this.removable && !this.multiple){
17425 inputblock.cls += ' roo-removable';
17427 inputblock.cn.push({
17430 cls : 'roo-combo-removable-btn close'
17434 if(this.hasFeedback && !this.allowBlank){
17436 inputblock.cls += ' has-feedback';
17438 inputblock.cn.push({
17440 cls: 'glyphicon form-control-feedback'
17447 inputblock.cls += (this.before) ? '' : ' input-group';
17449 inputblock.cn.push({
17451 cls : 'input-group-addon input-group-append input-group-text',
17457 var ibwrap = inputblock;
17462 cls: 'roo-select2-choices',
17466 cls: 'roo-select2-search-field',
17479 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17484 cls: 'form-hidden-field'
17490 if(!this.multiple && this.showToggleBtn){
17496 if (this.caret != false) {
17499 cls: 'fa fa-' + this.caret
17506 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17508 Roo.bootstrap.version == 3 ? caret : '',
17511 cls: 'combobox-clear',
17525 combobox.cls += ' roo-select2-container-multi';
17528 var align = this.labelAlign || this.parentLabelAlign();
17530 if (align ==='left' && this.fieldLabel.length) {
17535 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17536 tooltip : 'This field is required'
17540 cls : 'control-label col-form-label',
17541 html : this.fieldLabel
17545 cls : 'roo-combobox-wrap ',
17552 var labelCfg = cfg.cn[1];
17553 var contentCfg = cfg.cn[2];
17556 if(this.indicatorpos == 'right'){
17561 cls : 'control-label col-form-label',
17565 html : this.fieldLabel
17569 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17570 tooltip : 'This field is required'
17575 cls : "roo-combobox-wrap ",
17583 labelCfg = cfg.cn[0];
17584 contentCfg = cfg.cn[1];
17589 if(this.labelWidth > 12){
17590 labelCfg.style = "width: " + this.labelWidth + 'px';
17593 if(this.labelWidth < 13 && this.labelmd == 0){
17594 this.labelmd = this.labelWidth;
17597 if(this.labellg > 0){
17598 labelCfg.cls += ' col-lg-' + this.labellg;
17599 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17602 if(this.labelmd > 0){
17603 labelCfg.cls += ' col-md-' + this.labelmd;
17604 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17607 if(this.labelsm > 0){
17608 labelCfg.cls += ' col-sm-' + this.labelsm;
17609 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17612 if(this.labelxs > 0){
17613 labelCfg.cls += ' col-xs-' + this.labelxs;
17614 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17618 } else if ( this.fieldLabel.length) {
17622 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17623 tooltip : 'This field is required'
17627 cls : 'control-label',
17628 html : this.fieldLabel
17639 if(this.indicatorpos == 'right'){
17643 cls : 'control-label',
17644 html : this.fieldLabel,
17648 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17649 tooltip : 'This field is required'
17666 var settings = this;
17668 ['xs','sm','md','lg'].map(function(size){
17669 if (settings[size]) {
17670 cfg.cls += ' col-' + size + '-' + settings[size];
17677 initTouchView : function()
17679 this.renderTouchView();
17681 this.touchViewEl.on('scroll', function(){
17682 this.el.dom.scrollTop = 0;
17685 this.originalValue = this.getValue();
17687 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17689 this.inputEl().on("click", this.showTouchView, this);
17690 if (this.triggerEl) {
17691 this.triggerEl.on("click", this.showTouchView, this);
17695 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17696 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17698 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17700 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17701 this.store.on('load', this.onTouchViewLoad, this);
17702 this.store.on('loadexception', this.onTouchViewLoadException, this);
17704 if(this.hiddenName){
17706 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17708 this.hiddenField.dom.value =
17709 this.hiddenValue !== undefined ? this.hiddenValue :
17710 this.value !== undefined ? this.value : '';
17712 this.el.dom.removeAttribute('name');
17713 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17717 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17718 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17721 if(this.removable && !this.multiple){
17722 var close = this.closeTriggerEl();
17724 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17725 close.on('click', this.removeBtnClick, this, close);
17729 * fix the bug in Safari iOS8
17731 this.inputEl().on("focus", function(e){
17732 document.activeElement.blur();
17735 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17742 renderTouchView : function()
17744 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17745 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17747 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17748 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17750 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17751 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17752 this.touchViewBodyEl.setStyle('overflow', 'auto');
17754 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17755 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17757 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17758 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17762 showTouchView : function()
17768 this.touchViewHeaderEl.hide();
17770 if(this.modalTitle.length){
17771 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17772 this.touchViewHeaderEl.show();
17775 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17776 this.touchViewEl.show();
17778 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17780 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17781 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17783 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17785 if(this.modalTitle.length){
17786 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17789 this.touchViewBodyEl.setHeight(bodyHeight);
17793 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17795 this.touchViewEl.addClass(['in','show']);
17798 if(this._touchViewMask){
17799 Roo.get(document.body).addClass("x-body-masked");
17800 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17801 this._touchViewMask.setStyle('z-index', 10000);
17802 this._touchViewMask.addClass('show');
17805 this.doTouchViewQuery();
17809 hideTouchView : function()
17811 this.touchViewEl.removeClass(['in','show']);
17815 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17817 this.touchViewEl.setStyle('display', 'none');
17820 if(this._touchViewMask){
17821 this._touchViewMask.removeClass('show');
17822 Roo.get(document.body).removeClass("x-body-masked");
17826 setTouchViewValue : function()
17833 Roo.each(this.tickItems, function(o){
17838 this.hideTouchView();
17841 doTouchViewQuery : function()
17850 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17854 if(!this.alwaysQuery || this.mode == 'local'){
17855 this.onTouchViewLoad();
17862 onTouchViewBeforeLoad : function(combo,opts)
17868 onTouchViewLoad : function()
17870 if(this.store.getCount() < 1){
17871 this.onTouchViewEmptyResults();
17875 this.clearTouchView();
17877 var rawValue = this.getRawValue();
17879 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17881 this.tickItems = [];
17883 this.store.data.each(function(d, rowIndex){
17884 var row = this.touchViewListGroup.createChild(template);
17886 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17887 row.addClass(d.data.cls);
17890 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17893 html : d.data[this.displayField]
17896 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17897 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17900 row.removeClass('selected');
17901 if(!this.multiple && this.valueField &&
17902 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17905 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17906 row.addClass('selected');
17909 if(this.multiple && this.valueField &&
17910 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17914 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17915 this.tickItems.push(d.data);
17918 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17922 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17924 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17926 if(this.modalTitle.length){
17927 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17930 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17932 if(this.mobile_restrict_height && listHeight < bodyHeight){
17933 this.touchViewBodyEl.setHeight(listHeight);
17938 if(firstChecked && listHeight > bodyHeight){
17939 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17944 onTouchViewLoadException : function()
17946 this.hideTouchView();
17949 onTouchViewEmptyResults : function()
17951 this.clearTouchView();
17953 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17955 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17959 clearTouchView : function()
17961 this.touchViewListGroup.dom.innerHTML = '';
17964 onTouchViewClick : function(e, el, o)
17966 e.preventDefault();
17969 var rowIndex = o.rowIndex;
17971 var r = this.store.getAt(rowIndex);
17973 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17975 if(!this.multiple){
17976 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17977 c.dom.removeAttribute('checked');
17980 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17982 this.setFromData(r.data);
17984 var close = this.closeTriggerEl();
17990 this.hideTouchView();
17992 this.fireEvent('select', this, r, rowIndex);
17997 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17998 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17999 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18003 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18004 this.addItem(r.data);
18005 this.tickItems.push(r.data);
18009 getAutoCreateNativeIOS : function()
18012 cls: 'form-group' //input-group,
18017 cls : 'roo-ios-select'
18021 combobox.name = this.name;
18024 if (this.disabled) {
18025 combobox.disabled = true;
18028 var settings = this;
18030 ['xs','sm','md','lg'].map(function(size){
18031 if (settings[size]) {
18032 cfg.cls += ' col-' + size + '-' + settings[size];
18042 initIOSView : function()
18044 this.store.on('load', this.onIOSViewLoad, this);
18049 onIOSViewLoad : function()
18051 if(this.store.getCount() < 1){
18055 this.clearIOSView();
18057 if(this.allowBlank) {
18059 var default_text = '-- SELECT --';
18061 if(this.placeholder.length){
18062 default_text = this.placeholder;
18065 if(this.emptyTitle.length){
18066 default_text += ' - ' + this.emptyTitle + ' -';
18069 var opt = this.inputEl().createChild({
18072 html : default_text
18076 o[this.valueField] = 0;
18077 o[this.displayField] = default_text;
18079 this.ios_options.push({
18086 this.store.data.each(function(d, rowIndex){
18090 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18091 html = d.data[this.displayField];
18096 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18097 value = d.data[this.valueField];
18106 if(this.value == d.data[this.valueField]){
18107 option['selected'] = true;
18110 var opt = this.inputEl().createChild(option);
18112 this.ios_options.push({
18119 this.inputEl().on('change', function(){
18120 this.fireEvent('select', this);
18125 clearIOSView: function()
18127 this.inputEl().dom.innerHTML = '';
18129 this.ios_options = [];
18132 setIOSValue: function(v)
18136 if(!this.ios_options){
18140 Roo.each(this.ios_options, function(opts){
18142 opts.el.dom.removeAttribute('selected');
18144 if(opts.data[this.valueField] != v){
18148 opts.el.dom.setAttribute('selected', true);
18154 * @cfg {Boolean} grow
18158 * @cfg {Number} growMin
18162 * @cfg {Number} growMax
18171 Roo.apply(Roo.bootstrap.ComboBox, {
18175 cls: 'modal-header',
18197 cls: 'list-group-item',
18201 cls: 'roo-combobox-list-group-item-value'
18205 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18219 listItemCheckbox : {
18221 cls: 'list-group-item',
18225 cls: 'roo-combobox-list-group-item-value'
18229 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18245 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18250 cls: 'modal-footer',
18258 cls: 'col-xs-6 text-left',
18261 cls: 'btn btn-danger roo-touch-view-cancel',
18267 cls: 'col-xs-6 text-right',
18270 cls: 'btn btn-success roo-touch-view-ok',
18281 Roo.apply(Roo.bootstrap.ComboBox, {
18283 touchViewTemplate : {
18285 cls: 'modal fade roo-combobox-touch-view',
18289 cls: 'modal-dialog',
18290 style : 'position:fixed', // we have to fix position....
18294 cls: 'modal-content',
18296 Roo.bootstrap.ComboBox.header,
18297 Roo.bootstrap.ComboBox.body,
18298 Roo.bootstrap.ComboBox.footer
18307 * Ext JS Library 1.1.1
18308 * Copyright(c) 2006-2007, Ext JS, LLC.
18310 * Originally Released Under LGPL - original licence link has changed is not relivant.
18313 * <script type="text/javascript">
18318 * @extends Roo.util.Observable
18319 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18320 * This class also supports single and multi selection modes. <br>
18321 * Create a data model bound view:
18323 var store = new Roo.data.Store(...);
18325 var view = new Roo.View({
18327 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18329 singleSelect: true,
18330 selectedClass: "ydataview-selected",
18334 // listen for node click?
18335 view.on("click", function(vw, index, node, e){
18336 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18340 dataModel.load("foobar.xml");
18342 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18344 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18345 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18347 * Note: old style constructor is still suported (container, template, config)
18350 * Create a new View
18351 * @param {Object} config The config object
18354 Roo.View = function(config, depreciated_tpl, depreciated_config){
18356 this.parent = false;
18358 if (typeof(depreciated_tpl) == 'undefined') {
18359 // new way.. - universal constructor.
18360 Roo.apply(this, config);
18361 this.el = Roo.get(this.el);
18364 this.el = Roo.get(config);
18365 this.tpl = depreciated_tpl;
18366 Roo.apply(this, depreciated_config);
18368 this.wrapEl = this.el.wrap().wrap();
18369 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18372 if(typeof(this.tpl) == "string"){
18373 this.tpl = new Roo.Template(this.tpl);
18375 // support xtype ctors..
18376 this.tpl = new Roo.factory(this.tpl, Roo);
18380 this.tpl.compile();
18385 * @event beforeclick
18386 * Fires before a click is processed. Returns false to cancel the default action.
18387 * @param {Roo.View} this
18388 * @param {Number} index The index of the target node
18389 * @param {HTMLElement} node The target node
18390 * @param {Roo.EventObject} e The raw event object
18392 "beforeclick" : true,
18395 * Fires when a template node is clicked.
18396 * @param {Roo.View} this
18397 * @param {Number} index The index of the target node
18398 * @param {HTMLElement} node The target node
18399 * @param {Roo.EventObject} e The raw event object
18404 * Fires when a template node is double clicked.
18405 * @param {Roo.View} this
18406 * @param {Number} index The index of the target node
18407 * @param {HTMLElement} node The target node
18408 * @param {Roo.EventObject} e The raw event object
18412 * @event contextmenu
18413 * Fires when a template node is right clicked.
18414 * @param {Roo.View} this
18415 * @param {Number} index The index of the target node
18416 * @param {HTMLElement} node The target node
18417 * @param {Roo.EventObject} e The raw event object
18419 "contextmenu" : true,
18421 * @event selectionchange
18422 * Fires when the selected nodes change.
18423 * @param {Roo.View} this
18424 * @param {Array} selections Array of the selected nodes
18426 "selectionchange" : true,
18429 * @event beforeselect
18430 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18431 * @param {Roo.View} this
18432 * @param {HTMLElement} node The node to be selected
18433 * @param {Array} selections Array of currently selected nodes
18435 "beforeselect" : true,
18437 * @event preparedata
18438 * Fires on every row to render, to allow you to change the data.
18439 * @param {Roo.View} this
18440 * @param {Object} data to be rendered (change this)
18442 "preparedata" : true
18450 "click": this.onClick,
18451 "dblclick": this.onDblClick,
18452 "contextmenu": this.onContextMenu,
18456 this.selections = [];
18458 this.cmp = new Roo.CompositeElementLite([]);
18460 this.store = Roo.factory(this.store, Roo.data);
18461 this.setStore(this.store, true);
18464 if ( this.footer && this.footer.xtype) {
18466 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18468 this.footer.dataSource = this.store;
18469 this.footer.container = fctr;
18470 this.footer = Roo.factory(this.footer, Roo);
18471 fctr.insertFirst(this.el);
18473 // this is a bit insane - as the paging toolbar seems to detach the el..
18474 // dom.parentNode.parentNode.parentNode
18475 // they get detached?
18479 Roo.View.superclass.constructor.call(this);
18484 Roo.extend(Roo.View, Roo.util.Observable, {
18487 * @cfg {Roo.data.Store} store Data store to load data from.
18492 * @cfg {String|Roo.Element} el The container element.
18497 * @cfg {String|Roo.Template} tpl The template used by this View
18501 * @cfg {String} dataName the named area of the template to use as the data area
18502 * Works with domtemplates roo-name="name"
18506 * @cfg {String} selectedClass The css class to add to selected nodes
18508 selectedClass : "x-view-selected",
18510 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18515 * @cfg {String} text to display on mask (default Loading)
18519 * @cfg {Boolean} multiSelect Allow multiple selection
18521 multiSelect : false,
18523 * @cfg {Boolean} singleSelect Allow single selection
18525 singleSelect: false,
18528 * @cfg {Boolean} toggleSelect - selecting
18530 toggleSelect : false,
18533 * @cfg {Boolean} tickable - selecting
18538 * Returns the element this view is bound to.
18539 * @return {Roo.Element}
18541 getEl : function(){
18542 return this.wrapEl;
18548 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18550 refresh : function(){
18551 //Roo.log('refresh');
18554 // if we are using something like 'domtemplate', then
18555 // the what gets used is:
18556 // t.applySubtemplate(NAME, data, wrapping data..)
18557 // the outer template then get' applied with
18558 // the store 'extra data'
18559 // and the body get's added to the
18560 // roo-name="data" node?
18561 // <span class='roo-tpl-{name}'></span> ?????
18565 this.clearSelections();
18566 this.el.update("");
18568 var records = this.store.getRange();
18569 if(records.length < 1) {
18571 // is this valid?? = should it render a template??
18573 this.el.update(this.emptyText);
18577 if (this.dataName) {
18578 this.el.update(t.apply(this.store.meta)); //????
18579 el = this.el.child('.roo-tpl-' + this.dataName);
18582 for(var i = 0, len = records.length; i < len; i++){
18583 var data = this.prepareData(records[i].data, i, records[i]);
18584 this.fireEvent("preparedata", this, data, i, records[i]);
18586 var d = Roo.apply({}, data);
18589 Roo.apply(d, {'roo-id' : Roo.id()});
18593 Roo.each(this.parent.item, function(item){
18594 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18597 Roo.apply(d, {'roo-data-checked' : 'checked'});
18601 html[html.length] = Roo.util.Format.trim(
18603 t.applySubtemplate(this.dataName, d, this.store.meta) :
18610 el.update(html.join(""));
18611 this.nodes = el.dom.childNodes;
18612 this.updateIndexes(0);
18617 * Function to override to reformat the data that is sent to
18618 * the template for each node.
18619 * DEPRICATED - use the preparedata event handler.
18620 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18621 * a JSON object for an UpdateManager bound view).
18623 prepareData : function(data, index, record)
18625 this.fireEvent("preparedata", this, data, index, record);
18629 onUpdate : function(ds, record){
18630 // Roo.log('on update');
18631 this.clearSelections();
18632 var index = this.store.indexOf(record);
18633 var n = this.nodes[index];
18634 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18635 n.parentNode.removeChild(n);
18636 this.updateIndexes(index, index);
18642 onAdd : function(ds, records, index)
18644 //Roo.log(['on Add', ds, records, index] );
18645 this.clearSelections();
18646 if(this.nodes.length == 0){
18650 var n = this.nodes[index];
18651 for(var i = 0, len = records.length; i < len; i++){
18652 var d = this.prepareData(records[i].data, i, records[i]);
18654 this.tpl.insertBefore(n, d);
18657 this.tpl.append(this.el, d);
18660 this.updateIndexes(index);
18663 onRemove : function(ds, record, index){
18664 // Roo.log('onRemove');
18665 this.clearSelections();
18666 var el = this.dataName ?
18667 this.el.child('.roo-tpl-' + this.dataName) :
18670 el.dom.removeChild(this.nodes[index]);
18671 this.updateIndexes(index);
18675 * Refresh an individual node.
18676 * @param {Number} index
18678 refreshNode : function(index){
18679 this.onUpdate(this.store, this.store.getAt(index));
18682 updateIndexes : function(startIndex, endIndex){
18683 var ns = this.nodes;
18684 startIndex = startIndex || 0;
18685 endIndex = endIndex || ns.length - 1;
18686 for(var i = startIndex; i <= endIndex; i++){
18687 ns[i].nodeIndex = i;
18692 * Changes the data store this view uses and refresh the view.
18693 * @param {Store} store
18695 setStore : function(store, initial){
18696 if(!initial && this.store){
18697 this.store.un("datachanged", this.refresh);
18698 this.store.un("add", this.onAdd);
18699 this.store.un("remove", this.onRemove);
18700 this.store.un("update", this.onUpdate);
18701 this.store.un("clear", this.refresh);
18702 this.store.un("beforeload", this.onBeforeLoad);
18703 this.store.un("load", this.onLoad);
18704 this.store.un("loadexception", this.onLoad);
18708 store.on("datachanged", this.refresh, this);
18709 store.on("add", this.onAdd, this);
18710 store.on("remove", this.onRemove, this);
18711 store.on("update", this.onUpdate, this);
18712 store.on("clear", this.refresh, this);
18713 store.on("beforeload", this.onBeforeLoad, this);
18714 store.on("load", this.onLoad, this);
18715 store.on("loadexception", this.onLoad, this);
18723 * onbeforeLoad - masks the loading area.
18726 onBeforeLoad : function(store,opts)
18728 //Roo.log('onBeforeLoad');
18730 this.el.update("");
18732 this.el.mask(this.mask ? this.mask : "Loading" );
18734 onLoad : function ()
18741 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18742 * @param {HTMLElement} node
18743 * @return {HTMLElement} The template node
18745 findItemFromChild : function(node){
18746 var el = this.dataName ?
18747 this.el.child('.roo-tpl-' + this.dataName,true) :
18750 if(!node || node.parentNode == el){
18753 var p = node.parentNode;
18754 while(p && p != el){
18755 if(p.parentNode == el){
18764 onClick : function(e){
18765 var item = this.findItemFromChild(e.getTarget());
18767 var index = this.indexOf(item);
18768 if(this.onItemClick(item, index, e) !== false){
18769 this.fireEvent("click", this, index, item, e);
18772 this.clearSelections();
18777 onContextMenu : function(e){
18778 var item = this.findItemFromChild(e.getTarget());
18780 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18785 onDblClick : function(e){
18786 var item = this.findItemFromChild(e.getTarget());
18788 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18792 onItemClick : function(item, index, e)
18794 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18797 if (this.toggleSelect) {
18798 var m = this.isSelected(item) ? 'unselect' : 'select';
18801 _t[m](item, true, false);
18804 if(this.multiSelect || this.singleSelect){
18805 if(this.multiSelect && e.shiftKey && this.lastSelection){
18806 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18808 this.select(item, this.multiSelect && e.ctrlKey);
18809 this.lastSelection = item;
18812 if(!this.tickable){
18813 e.preventDefault();
18821 * Get the number of selected nodes.
18824 getSelectionCount : function(){
18825 return this.selections.length;
18829 * Get the currently selected nodes.
18830 * @return {Array} An array of HTMLElements
18832 getSelectedNodes : function(){
18833 return this.selections;
18837 * Get the indexes of the selected nodes.
18840 getSelectedIndexes : function(){
18841 var indexes = [], s = this.selections;
18842 for(var i = 0, len = s.length; i < len; i++){
18843 indexes.push(s[i].nodeIndex);
18849 * Clear all selections
18850 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18852 clearSelections : function(suppressEvent){
18853 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18854 this.cmp.elements = this.selections;
18855 this.cmp.removeClass(this.selectedClass);
18856 this.selections = [];
18857 if(!suppressEvent){
18858 this.fireEvent("selectionchange", this, this.selections);
18864 * Returns true if the passed node is selected
18865 * @param {HTMLElement/Number} node The node or node index
18866 * @return {Boolean}
18868 isSelected : function(node){
18869 var s = this.selections;
18873 node = this.getNode(node);
18874 return s.indexOf(node) !== -1;
18879 * @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
18880 * @param {Boolean} keepExisting (optional) true to keep existing selections
18881 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18883 select : function(nodeInfo, keepExisting, suppressEvent){
18884 if(nodeInfo instanceof Array){
18886 this.clearSelections(true);
18888 for(var i = 0, len = nodeInfo.length; i < len; i++){
18889 this.select(nodeInfo[i], true, true);
18893 var node = this.getNode(nodeInfo);
18894 if(!node || this.isSelected(node)){
18895 return; // already selected.
18898 this.clearSelections(true);
18901 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18902 Roo.fly(node).addClass(this.selectedClass);
18903 this.selections.push(node);
18904 if(!suppressEvent){
18905 this.fireEvent("selectionchange", this, this.selections);
18913 * @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
18914 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18915 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18917 unselect : function(nodeInfo, keepExisting, suppressEvent)
18919 if(nodeInfo instanceof Array){
18920 Roo.each(this.selections, function(s) {
18921 this.unselect(s, nodeInfo);
18925 var node = this.getNode(nodeInfo);
18926 if(!node || !this.isSelected(node)){
18927 //Roo.log("not selected");
18928 return; // not selected.
18932 Roo.each(this.selections, function(s) {
18934 Roo.fly(node).removeClass(this.selectedClass);
18941 this.selections= ns;
18942 this.fireEvent("selectionchange", this, this.selections);
18946 * Gets a template node.
18947 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18948 * @return {HTMLElement} The node or null if it wasn't found
18950 getNode : function(nodeInfo){
18951 if(typeof nodeInfo == "string"){
18952 return document.getElementById(nodeInfo);
18953 }else if(typeof nodeInfo == "number"){
18954 return this.nodes[nodeInfo];
18960 * Gets a range template nodes.
18961 * @param {Number} startIndex
18962 * @param {Number} endIndex
18963 * @return {Array} An array of nodes
18965 getNodes : function(start, end){
18966 var ns = this.nodes;
18967 start = start || 0;
18968 end = typeof end == "undefined" ? ns.length - 1 : end;
18971 for(var i = start; i <= end; i++){
18975 for(var i = start; i >= end; i--){
18983 * Finds the index of the passed node
18984 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18985 * @return {Number} The index of the node or -1
18987 indexOf : function(node){
18988 node = this.getNode(node);
18989 if(typeof node.nodeIndex == "number"){
18990 return node.nodeIndex;
18992 var ns = this.nodes;
18993 for(var i = 0, len = ns.length; i < len; i++){
19004 * based on jquery fullcalendar
19008 Roo.bootstrap = Roo.bootstrap || {};
19010 * @class Roo.bootstrap.Calendar
19011 * @extends Roo.bootstrap.Component
19012 * Bootstrap Calendar class
19013 * @cfg {Boolean} loadMask (true|false) default false
19014 * @cfg {Object} header generate the user specific header of the calendar, default false
19017 * Create a new Container
19018 * @param {Object} config The config object
19023 Roo.bootstrap.Calendar = function(config){
19024 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19028 * Fires when a date is selected
19029 * @param {DatePicker} this
19030 * @param {Date} date The selected date
19034 * @event monthchange
19035 * Fires when the displayed month changes
19036 * @param {DatePicker} this
19037 * @param {Date} date The selected month
19039 'monthchange': true,
19041 * @event evententer
19042 * Fires when mouse over an event
19043 * @param {Calendar} this
19044 * @param {event} Event
19046 'evententer': true,
19048 * @event eventleave
19049 * Fires when the mouse leaves an
19050 * @param {Calendar} this
19053 'eventleave': true,
19055 * @event eventclick
19056 * Fires when the mouse click an
19057 * @param {Calendar} this
19066 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19069 * @cfg {Number} startDay
19070 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19078 getAutoCreate : function(){
19081 var fc_button = function(name, corner, style, content ) {
19082 return Roo.apply({},{
19084 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19086 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19089 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19100 style : 'width:100%',
19107 cls : 'fc-header-left',
19109 fc_button('prev', 'left', 'arrow', '‹' ),
19110 fc_button('next', 'right', 'arrow', '›' ),
19111 { tag: 'span', cls: 'fc-header-space' },
19112 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19120 cls : 'fc-header-center',
19124 cls: 'fc-header-title',
19127 html : 'month / year'
19135 cls : 'fc-header-right',
19137 /* fc_button('month', 'left', '', 'month' ),
19138 fc_button('week', '', '', 'week' ),
19139 fc_button('day', 'right', '', 'day' )
19151 header = this.header;
19154 var cal_heads = function() {
19156 // fixme - handle this.
19158 for (var i =0; i < Date.dayNames.length; i++) {
19159 var d = Date.dayNames[i];
19162 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19163 html : d.substring(0,3)
19167 ret[0].cls += ' fc-first';
19168 ret[6].cls += ' fc-last';
19171 var cal_cell = function(n) {
19174 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19179 cls: 'fc-day-number',
19183 cls: 'fc-day-content',
19187 style: 'position: relative;' // height: 17px;
19199 var cal_rows = function() {
19202 for (var r = 0; r < 6; r++) {
19209 for (var i =0; i < Date.dayNames.length; i++) {
19210 var d = Date.dayNames[i];
19211 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19214 row.cn[0].cls+=' fc-first';
19215 row.cn[0].cn[0].style = 'min-height:90px';
19216 row.cn[6].cls+=' fc-last';
19220 ret[0].cls += ' fc-first';
19221 ret[4].cls += ' fc-prev-last';
19222 ret[5].cls += ' fc-last';
19229 cls: 'fc-border-separate',
19230 style : 'width:100%',
19238 cls : 'fc-first fc-last',
19256 cls : 'fc-content',
19257 style : "position: relative;",
19260 cls : 'fc-view fc-view-month fc-grid',
19261 style : 'position: relative',
19262 unselectable : 'on',
19265 cls : 'fc-event-container',
19266 style : 'position:absolute;z-index:8;top:0;left:0;'
19284 initEvents : function()
19287 throw "can not find store for calendar";
19293 style: "text-align:center",
19297 style: "background-color:white;width:50%;margin:250 auto",
19301 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19312 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19314 var size = this.el.select('.fc-content', true).first().getSize();
19315 this.maskEl.setSize(size.width, size.height);
19316 this.maskEl.enableDisplayMode("block");
19317 if(!this.loadMask){
19318 this.maskEl.hide();
19321 this.store = Roo.factory(this.store, Roo.data);
19322 this.store.on('load', this.onLoad, this);
19323 this.store.on('beforeload', this.onBeforeLoad, this);
19327 this.cells = this.el.select('.fc-day',true);
19328 //Roo.log(this.cells);
19329 this.textNodes = this.el.query('.fc-day-number');
19330 this.cells.addClassOnOver('fc-state-hover');
19332 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19333 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19334 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19335 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19337 this.on('monthchange', this.onMonthChange, this);
19339 this.update(new Date().clearTime());
19342 resize : function() {
19343 var sz = this.el.getSize();
19345 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19346 this.el.select('.fc-day-content div',true).setHeight(34);
19351 showPrevMonth : function(e){
19352 this.update(this.activeDate.add("mo", -1));
19354 showToday : function(e){
19355 this.update(new Date().clearTime());
19358 showNextMonth : function(e){
19359 this.update(this.activeDate.add("mo", 1));
19363 showPrevYear : function(){
19364 this.update(this.activeDate.add("y", -1));
19368 showNextYear : function(){
19369 this.update(this.activeDate.add("y", 1));
19374 update : function(date)
19376 var vd = this.activeDate;
19377 this.activeDate = date;
19378 // if(vd && this.el){
19379 // var t = date.getTime();
19380 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19381 // Roo.log('using add remove');
19383 // this.fireEvent('monthchange', this, date);
19385 // this.cells.removeClass("fc-state-highlight");
19386 // this.cells.each(function(c){
19387 // if(c.dateValue == t){
19388 // c.addClass("fc-state-highlight");
19389 // setTimeout(function(){
19390 // try{c.dom.firstChild.focus();}catch(e){}
19400 var days = date.getDaysInMonth();
19402 var firstOfMonth = date.getFirstDateOfMonth();
19403 var startingPos = firstOfMonth.getDay()-this.startDay;
19405 if(startingPos < this.startDay){
19409 var pm = date.add(Date.MONTH, -1);
19410 var prevStart = pm.getDaysInMonth()-startingPos;
19412 this.cells = this.el.select('.fc-day',true);
19413 this.textNodes = this.el.query('.fc-day-number');
19414 this.cells.addClassOnOver('fc-state-hover');
19416 var cells = this.cells.elements;
19417 var textEls = this.textNodes;
19419 Roo.each(cells, function(cell){
19420 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19423 days += startingPos;
19425 // convert everything to numbers so it's fast
19426 var day = 86400000;
19427 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19430 //Roo.log(prevStart);
19432 var today = new Date().clearTime().getTime();
19433 var sel = date.clearTime().getTime();
19434 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19435 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19436 var ddMatch = this.disabledDatesRE;
19437 var ddText = this.disabledDatesText;
19438 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19439 var ddaysText = this.disabledDaysText;
19440 var format = this.format;
19442 var setCellClass = function(cal, cell){
19446 //Roo.log('set Cell Class');
19448 var t = d.getTime();
19452 cell.dateValue = t;
19454 cell.className += " fc-today";
19455 cell.className += " fc-state-highlight";
19456 cell.title = cal.todayText;
19459 // disable highlight in other month..
19460 //cell.className += " fc-state-highlight";
19465 cell.className = " fc-state-disabled";
19466 cell.title = cal.minText;
19470 cell.className = " fc-state-disabled";
19471 cell.title = cal.maxText;
19475 if(ddays.indexOf(d.getDay()) != -1){
19476 cell.title = ddaysText;
19477 cell.className = " fc-state-disabled";
19480 if(ddMatch && format){
19481 var fvalue = d.dateFormat(format);
19482 if(ddMatch.test(fvalue)){
19483 cell.title = ddText.replace("%0", fvalue);
19484 cell.className = " fc-state-disabled";
19488 if (!cell.initialClassName) {
19489 cell.initialClassName = cell.dom.className;
19492 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19497 for(; i < startingPos; i++) {
19498 textEls[i].innerHTML = (++prevStart);
19499 d.setDate(d.getDate()+1);
19501 cells[i].className = "fc-past fc-other-month";
19502 setCellClass(this, cells[i]);
19507 for(; i < days; i++){
19508 intDay = i - startingPos + 1;
19509 textEls[i].innerHTML = (intDay);
19510 d.setDate(d.getDate()+1);
19512 cells[i].className = ''; // "x-date-active";
19513 setCellClass(this, cells[i]);
19517 for(; i < 42; i++) {
19518 textEls[i].innerHTML = (++extraDays);
19519 d.setDate(d.getDate()+1);
19521 cells[i].className = "fc-future fc-other-month";
19522 setCellClass(this, cells[i]);
19525 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19527 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19529 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19530 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19532 if(totalRows != 6){
19533 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19534 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19537 this.fireEvent('monthchange', this, date);
19541 if(!this.internalRender){
19542 var main = this.el.dom.firstChild;
19543 var w = main.offsetWidth;
19544 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19545 Roo.fly(main).setWidth(w);
19546 this.internalRender = true;
19547 // opera does not respect the auto grow header center column
19548 // then, after it gets a width opera refuses to recalculate
19549 // without a second pass
19550 if(Roo.isOpera && !this.secondPass){
19551 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19552 this.secondPass = true;
19553 this.update.defer(10, this, [date]);
19560 findCell : function(dt) {
19561 dt = dt.clearTime().getTime();
19563 this.cells.each(function(c){
19564 //Roo.log("check " +c.dateValue + '?=' + dt);
19565 if(c.dateValue == dt){
19575 findCells : function(ev) {
19576 var s = ev.start.clone().clearTime().getTime();
19578 var e= ev.end.clone().clearTime().getTime();
19581 this.cells.each(function(c){
19582 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19584 if(c.dateValue > e){
19587 if(c.dateValue < s){
19596 // findBestRow: function(cells)
19600 // for (var i =0 ; i < cells.length;i++) {
19601 // ret = Math.max(cells[i].rows || 0,ret);
19608 addItem : function(ev)
19610 // look for vertical location slot in
19611 var cells = this.findCells(ev);
19613 // ev.row = this.findBestRow(cells);
19615 // work out the location.
19619 for(var i =0; i < cells.length; i++) {
19621 cells[i].row = cells[0].row;
19624 cells[i].row = cells[i].row + 1;
19634 if (crow.start.getY() == cells[i].getY()) {
19636 crow.end = cells[i];
19653 cells[0].events.push(ev);
19655 this.calevents.push(ev);
19658 clearEvents: function() {
19660 if(!this.calevents){
19664 Roo.each(this.cells.elements, function(c){
19670 Roo.each(this.calevents, function(e) {
19671 Roo.each(e.els, function(el) {
19672 el.un('mouseenter' ,this.onEventEnter, this);
19673 el.un('mouseleave' ,this.onEventLeave, this);
19678 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19684 renderEvents: function()
19688 this.cells.each(function(c) {
19697 if(c.row != c.events.length){
19698 r = 4 - (4 - (c.row - c.events.length));
19701 c.events = ev.slice(0, r);
19702 c.more = ev.slice(r);
19704 if(c.more.length && c.more.length == 1){
19705 c.events.push(c.more.pop());
19708 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19712 this.cells.each(function(c) {
19714 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19717 for (var e = 0; e < c.events.length; e++){
19718 var ev = c.events[e];
19719 var rows = ev.rows;
19721 for(var i = 0; i < rows.length; i++) {
19723 // how many rows should it span..
19726 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19727 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19729 unselectable : "on",
19732 cls: 'fc-event-inner',
19736 // cls: 'fc-event-time',
19737 // html : cells.length > 1 ? '' : ev.time
19741 cls: 'fc-event-title',
19742 html : String.format('{0}', ev.title)
19749 cls: 'ui-resizable-handle ui-resizable-e',
19750 html : '  '
19757 cfg.cls += ' fc-event-start';
19759 if ((i+1) == rows.length) {
19760 cfg.cls += ' fc-event-end';
19763 var ctr = _this.el.select('.fc-event-container',true).first();
19764 var cg = ctr.createChild(cfg);
19766 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19767 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19769 var r = (c.more.length) ? 1 : 0;
19770 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19771 cg.setWidth(ebox.right - sbox.x -2);
19773 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19774 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19775 cg.on('click', _this.onEventClick, _this, ev);
19786 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19787 style : 'position: absolute',
19788 unselectable : "on",
19791 cls: 'fc-event-inner',
19795 cls: 'fc-event-title',
19803 cls: 'ui-resizable-handle ui-resizable-e',
19804 html : '  '
19810 var ctr = _this.el.select('.fc-event-container',true).first();
19811 var cg = ctr.createChild(cfg);
19813 var sbox = c.select('.fc-day-content',true).first().getBox();
19814 var ebox = c.select('.fc-day-content',true).first().getBox();
19816 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19817 cg.setWidth(ebox.right - sbox.x -2);
19819 cg.on('click', _this.onMoreEventClick, _this, c.more);
19829 onEventEnter: function (e, el,event,d) {
19830 this.fireEvent('evententer', this, el, event);
19833 onEventLeave: function (e, el,event,d) {
19834 this.fireEvent('eventleave', this, el, event);
19837 onEventClick: function (e, el,event,d) {
19838 this.fireEvent('eventclick', this, el, event);
19841 onMonthChange: function () {
19845 onMoreEventClick: function(e, el, more)
19849 this.calpopover.placement = 'right';
19850 this.calpopover.setTitle('More');
19852 this.calpopover.setContent('');
19854 var ctr = this.calpopover.el.select('.popover-content', true).first();
19856 Roo.each(more, function(m){
19858 cls : 'fc-event-hori fc-event-draggable',
19861 var cg = ctr.createChild(cfg);
19863 cg.on('click', _this.onEventClick, _this, m);
19866 this.calpopover.show(el);
19871 onLoad: function ()
19873 this.calevents = [];
19876 if(this.store.getCount() > 0){
19877 this.store.data.each(function(d){
19880 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19881 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19882 time : d.data.start_time,
19883 title : d.data.title,
19884 description : d.data.description,
19885 venue : d.data.venue
19890 this.renderEvents();
19892 if(this.calevents.length && this.loadMask){
19893 this.maskEl.hide();
19897 onBeforeLoad: function()
19899 this.clearEvents();
19901 this.maskEl.show();
19915 * @class Roo.bootstrap.Popover
19916 * @extends Roo.bootstrap.Component
19917 * Bootstrap Popover class
19918 * @cfg {String} html contents of the popover (or false to use children..)
19919 * @cfg {String} title of popover (or false to hide)
19920 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19921 * @cfg {String} trigger click || hover (or false to trigger manually)
19922 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19923 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19924 * - if false and it has a 'parent' then it will be automatically added to that element
19925 * - if string - Roo.get will be called
19926 * @cfg {Number} delay - delay before showing
19929 * Create a new Popover
19930 * @param {Object} config The config object
19933 Roo.bootstrap.Popover = function(config){
19934 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19940 * After the popover show
19942 * @param {Roo.bootstrap.Popover} this
19947 * After the popover hide
19949 * @param {Roo.bootstrap.Popover} this
19955 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19960 placement : 'right',
19961 trigger : 'hover', // hover
19967 can_build_overlaid : false,
19969 maskEl : false, // the mask element
19972 alignEl : false, // when show is called with an element - this get's stored.
19974 getChildContainer : function()
19976 return this.contentEl;
19979 getPopoverHeader : function()
19981 this.title = true; // flag not to hide it..
19982 this.headerEl.addClass('p-0');
19983 return this.headerEl
19987 getAutoCreate : function(){
19990 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19991 style: 'display:block',
19997 cls : 'popover-inner ',
20001 cls: 'popover-title popover-header',
20002 html : this.title === false ? '' : this.title
20005 cls : 'popover-content popover-body ' + (this.cls || ''),
20006 html : this.html || ''
20017 * @param {string} the title
20019 setTitle: function(str)
20023 this.headerEl.dom.innerHTML = str;
20028 * @param {string} the body content
20030 setContent: function(str)
20033 if (this.contentEl) {
20034 this.contentEl.dom.innerHTML = str;
20038 // as it get's added to the bottom of the page.
20039 onRender : function(ct, position)
20041 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20046 var cfg = Roo.apply({}, this.getAutoCreate());
20050 cfg.cls += ' ' + this.cls;
20053 cfg.style = this.style;
20055 //Roo.log("adding to ");
20056 this.el = Roo.get(document.body).createChild(cfg, position);
20057 // Roo.log(this.el);
20060 this.contentEl = this.el.select('.popover-content',true).first();
20061 this.headerEl = this.el.select('.popover-title',true).first();
20064 if(typeof(this.items) != 'undefined'){
20065 var items = this.items;
20068 for(var i =0;i < items.length;i++) {
20069 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20073 this.items = nitems;
20075 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20076 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20083 resizeMask : function()
20085 this.maskEl.setSize(
20086 Roo.lib.Dom.getViewWidth(true),
20087 Roo.lib.Dom.getViewHeight(true)
20091 initEvents : function()
20095 Roo.bootstrap.Popover.register(this);
20098 this.arrowEl = this.el.select('.arrow',true).first();
20099 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20100 this.el.enableDisplayMode('block');
20104 if (this.over === false && !this.parent()) {
20107 if (this.triggers === false) {
20112 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20113 var triggers = this.trigger ? this.trigger.split(' ') : [];
20114 Roo.each(triggers, function(trigger) {
20116 if (trigger == 'click') {
20117 on_el.on('click', this.toggle, this);
20118 } else if (trigger != 'manual') {
20119 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20120 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20122 on_el.on(eventIn ,this.enter, this);
20123 on_el.on(eventOut, this.leave, this);
20133 toggle : function () {
20134 this.hoverState == 'in' ? this.leave() : this.enter();
20137 enter : function () {
20139 clearTimeout(this.timeout);
20141 this.hoverState = 'in';
20143 if (!this.delay || !this.delay.show) {
20148 this.timeout = setTimeout(function () {
20149 if (_t.hoverState == 'in') {
20152 }, this.delay.show)
20155 leave : function() {
20156 clearTimeout(this.timeout);
20158 this.hoverState = 'out';
20160 if (!this.delay || !this.delay.hide) {
20165 this.timeout = setTimeout(function () {
20166 if (_t.hoverState == 'out') {
20169 }, this.delay.hide)
20173 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20174 * @param {string} (left|right|top|bottom) position
20176 show : function (on_el, placement)
20178 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20179 on_el = on_el || false; // default to false
20182 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20183 on_el = this.parent().el;
20184 } else if (this.over) {
20185 Roo.get(this.over);
20190 this.alignEl = Roo.get( on_el );
20193 this.render(document.body);
20199 if (this.title === false) {
20200 this.headerEl.hide();
20205 this.el.dom.style.display = 'block';
20208 if (this.alignEl) {
20209 this.updatePosition(this.placement, true);
20212 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20213 var es = this.el.getSize();
20214 var x = Roo.lib.Dom.getViewWidth()/2;
20215 var y = Roo.lib.Dom.getViewHeight()/2;
20216 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20221 //var arrow = this.el.select('.arrow',true).first();
20222 //arrow.set(align[2],
20224 this.el.addClass('in');
20228 this.hoverState = 'in';
20231 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20232 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20233 this.maskEl.dom.style.display = 'block';
20234 this.maskEl.addClass('show');
20236 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20238 this.fireEvent('show', this);
20242 * fire this manually after loading a grid in the table for example
20243 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20244 * @param {Boolean} try and move it if we cant get right position.
20246 updatePosition : function(placement, try_move)
20248 // allow for calling with no parameters
20249 placement = placement ? placement : this.placement;
20250 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20252 this.el.removeClass([
20253 'fade','top','bottom', 'left', 'right','in',
20254 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20256 this.el.addClass(placement + ' bs-popover-' + placement);
20258 if (!this.alignEl ) {
20262 switch (placement) {
20264 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20265 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20266 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20267 //normal display... or moved up/down.
20268 this.el.setXY(offset);
20269 var xy = this.alignEl.getAnchorXY('tr', false);
20271 this.arrowEl.setXY(xy);
20274 // continue through...
20275 return this.updatePosition('left', false);
20279 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20280 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20281 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20282 //normal display... or moved up/down.
20283 this.el.setXY(offset);
20284 var xy = this.alignEl.getAnchorXY('tl', false);
20285 xy[0]-=10;xy[1]+=5; // << fix me
20286 this.arrowEl.setXY(xy);
20290 return this.updatePosition('right', false);
20293 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20294 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20295 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20296 //normal display... or moved up/down.
20297 this.el.setXY(offset);
20298 var xy = this.alignEl.getAnchorXY('t', false);
20299 xy[1]-=10; // << fix me
20300 this.arrowEl.setXY(xy);
20304 return this.updatePosition('bottom', false);
20307 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20308 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20309 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20310 //normal display... or moved up/down.
20311 this.el.setXY(offset);
20312 var xy = this.alignEl.getAnchorXY('b', false);
20313 xy[1]+=2; // << fix me
20314 this.arrowEl.setXY(xy);
20318 return this.updatePosition('top', false);
20329 this.el.setXY([0,0]);
20330 this.el.removeClass('in');
20332 this.hoverState = null;
20333 this.maskEl.hide(); // always..
20334 this.fireEvent('hide', this);
20340 Roo.apply(Roo.bootstrap.Popover, {
20343 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20344 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20345 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20346 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20351 clickHander : false,
20354 onMouseDown : function(e)
20356 if (!e.getTarget(".roo-popover")) {
20364 register : function(popup)
20366 if (!Roo.bootstrap.Popover.clickHandler) {
20367 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20369 // hide other popups.
20371 this.popups.push(popup);
20373 hideAll : function()
20375 this.popups.forEach(function(p) {
20383 * Card header - holder for the card header elements.
20388 * @class Roo.bootstrap.PopoverNav
20389 * @extends Roo.bootstrap.NavGroup
20390 * Bootstrap Popover header navigation class
20392 * Create a new Popover Header Navigation
20393 * @param {Object} config The config object
20396 Roo.bootstrap.PopoverNav = function(config){
20397 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20400 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20403 container_method : 'getPopoverHeader'
20421 * @class Roo.bootstrap.Progress
20422 * @extends Roo.bootstrap.Component
20423 * Bootstrap Progress class
20424 * @cfg {Boolean} striped striped of the progress bar
20425 * @cfg {Boolean} active animated of the progress bar
20429 * Create a new Progress
20430 * @param {Object} config The config object
20433 Roo.bootstrap.Progress = function(config){
20434 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20437 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20442 getAutoCreate : function(){
20450 cfg.cls += ' progress-striped';
20454 cfg.cls += ' active';
20473 * @class Roo.bootstrap.ProgressBar
20474 * @extends Roo.bootstrap.Component
20475 * Bootstrap ProgressBar class
20476 * @cfg {Number} aria_valuenow aria-value now
20477 * @cfg {Number} aria_valuemin aria-value min
20478 * @cfg {Number} aria_valuemax aria-value max
20479 * @cfg {String} label label for the progress bar
20480 * @cfg {String} panel (success | info | warning | danger )
20481 * @cfg {String} role role of the progress bar
20482 * @cfg {String} sr_only text
20486 * Create a new ProgressBar
20487 * @param {Object} config The config object
20490 Roo.bootstrap.ProgressBar = function(config){
20491 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20494 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20498 aria_valuemax : 100,
20504 getAutoCreate : function()
20509 cls: 'progress-bar',
20510 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20522 cfg.role = this.role;
20525 if(this.aria_valuenow){
20526 cfg['aria-valuenow'] = this.aria_valuenow;
20529 if(this.aria_valuemin){
20530 cfg['aria-valuemin'] = this.aria_valuemin;
20533 if(this.aria_valuemax){
20534 cfg['aria-valuemax'] = this.aria_valuemax;
20537 if(this.label && !this.sr_only){
20538 cfg.html = this.label;
20542 cfg.cls += ' progress-bar-' + this.panel;
20548 update : function(aria_valuenow)
20550 this.aria_valuenow = aria_valuenow;
20552 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20567 * @class Roo.bootstrap.TabGroup
20568 * @extends Roo.bootstrap.Column
20569 * Bootstrap Column class
20570 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20571 * @cfg {Boolean} carousel true to make the group behave like a carousel
20572 * @cfg {Boolean} bullets show bullets for the panels
20573 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20574 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20575 * @cfg {Boolean} showarrow (true|false) show arrow default true
20578 * Create a new TabGroup
20579 * @param {Object} config The config object
20582 Roo.bootstrap.TabGroup = function(config){
20583 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20585 this.navId = Roo.id();
20588 Roo.bootstrap.TabGroup.register(this);
20592 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20595 transition : false,
20600 slideOnTouch : false,
20603 getAutoCreate : function()
20605 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20607 cfg.cls += ' tab-content';
20609 if (this.carousel) {
20610 cfg.cls += ' carousel slide';
20613 cls : 'carousel-inner',
20617 if(this.bullets && !Roo.isTouch){
20620 cls : 'carousel-bullets',
20624 if(this.bullets_cls){
20625 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20632 cfg.cn[0].cn.push(bullets);
20635 if(this.showarrow){
20636 cfg.cn[0].cn.push({
20638 class : 'carousel-arrow',
20642 class : 'carousel-prev',
20646 class : 'fa fa-chevron-left'
20652 class : 'carousel-next',
20656 class : 'fa fa-chevron-right'
20669 initEvents: function()
20671 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20672 // this.el.on("touchstart", this.onTouchStart, this);
20675 if(this.autoslide){
20678 this.slideFn = window.setInterval(function() {
20679 _this.showPanelNext();
20683 if(this.showarrow){
20684 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20685 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20691 // onTouchStart : function(e, el, o)
20693 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20697 // this.showPanelNext();
20701 getChildContainer : function()
20703 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20707 * register a Navigation item
20708 * @param {Roo.bootstrap.NavItem} the navitem to add
20710 register : function(item)
20712 this.tabs.push( item);
20713 item.navId = this.navId; // not really needed..
20718 getActivePanel : function()
20721 Roo.each(this.tabs, function(t) {
20731 getPanelByName : function(n)
20734 Roo.each(this.tabs, function(t) {
20735 if (t.tabId == n) {
20743 indexOfPanel : function(p)
20746 Roo.each(this.tabs, function(t,i) {
20747 if (t.tabId == p.tabId) {
20756 * show a specific panel
20757 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20758 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20760 showPanel : function (pan)
20762 if(this.transition || typeof(pan) == 'undefined'){
20763 Roo.log("waiting for the transitionend");
20767 if (typeof(pan) == 'number') {
20768 pan = this.tabs[pan];
20771 if (typeof(pan) == 'string') {
20772 pan = this.getPanelByName(pan);
20775 var cur = this.getActivePanel();
20778 Roo.log('pan or acitve pan is undefined');
20782 if (pan.tabId == this.getActivePanel().tabId) {
20786 if (false === cur.fireEvent('beforedeactivate')) {
20790 if(this.bullets > 0 && !Roo.isTouch){
20791 this.setActiveBullet(this.indexOfPanel(pan));
20794 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20796 //class="carousel-item carousel-item-next carousel-item-left"
20798 this.transition = true;
20799 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20800 var lr = dir == 'next' ? 'left' : 'right';
20801 pan.el.addClass(dir); // or prev
20802 pan.el.addClass('carousel-item-' + dir); // or prev
20803 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20804 cur.el.addClass(lr); // or right
20805 pan.el.addClass(lr);
20806 cur.el.addClass('carousel-item-' +lr); // or right
20807 pan.el.addClass('carousel-item-' +lr);
20811 cur.el.on('transitionend', function() {
20812 Roo.log("trans end?");
20814 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20815 pan.setActive(true);
20817 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20818 cur.setActive(false);
20820 _this.transition = false;
20822 }, this, { single: true } );
20827 cur.setActive(false);
20828 pan.setActive(true);
20833 showPanelNext : function()
20835 var i = this.indexOfPanel(this.getActivePanel());
20837 if (i >= this.tabs.length - 1 && !this.autoslide) {
20841 if (i >= this.tabs.length - 1 && this.autoslide) {
20845 this.showPanel(this.tabs[i+1]);
20848 showPanelPrev : function()
20850 var i = this.indexOfPanel(this.getActivePanel());
20852 if (i < 1 && !this.autoslide) {
20856 if (i < 1 && this.autoslide) {
20857 i = this.tabs.length;
20860 this.showPanel(this.tabs[i-1]);
20864 addBullet: function()
20866 if(!this.bullets || Roo.isTouch){
20869 var ctr = this.el.select('.carousel-bullets',true).first();
20870 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20871 var bullet = ctr.createChild({
20872 cls : 'bullet bullet-' + i
20873 },ctr.dom.lastChild);
20878 bullet.on('click', (function(e, el, o, ii, t){
20880 e.preventDefault();
20882 this.showPanel(ii);
20884 if(this.autoslide && this.slideFn){
20885 clearInterval(this.slideFn);
20886 this.slideFn = window.setInterval(function() {
20887 _this.showPanelNext();
20891 }).createDelegate(this, [i, bullet], true));
20896 setActiveBullet : function(i)
20902 Roo.each(this.el.select('.bullet', true).elements, function(el){
20903 el.removeClass('selected');
20906 var bullet = this.el.select('.bullet-' + i, true).first();
20912 bullet.addClass('selected');
20923 Roo.apply(Roo.bootstrap.TabGroup, {
20927 * register a Navigation Group
20928 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20930 register : function(navgrp)
20932 this.groups[navgrp.navId] = navgrp;
20936 * fetch a Navigation Group based on the navigation ID
20937 * if one does not exist , it will get created.
20938 * @param {string} the navgroup to add
20939 * @returns {Roo.bootstrap.NavGroup} the navgroup
20941 get: function(navId) {
20942 if (typeof(this.groups[navId]) == 'undefined') {
20943 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20945 return this.groups[navId] ;
20960 * @class Roo.bootstrap.TabPanel
20961 * @extends Roo.bootstrap.Component
20962 * Bootstrap TabPanel class
20963 * @cfg {Boolean} active panel active
20964 * @cfg {String} html panel content
20965 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20966 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20967 * @cfg {String} href click to link..
20968 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20972 * Create a new TabPanel
20973 * @param {Object} config The config object
20976 Roo.bootstrap.TabPanel = function(config){
20977 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20981 * Fires when the active status changes
20982 * @param {Roo.bootstrap.TabPanel} this
20983 * @param {Boolean} state the new state
20988 * @event beforedeactivate
20989 * Fires before a tab is de-activated - can be used to do validation on a form.
20990 * @param {Roo.bootstrap.TabPanel} this
20991 * @return {Boolean} false if there is an error
20994 'beforedeactivate': true
20997 this.tabId = this.tabId || Roo.id();
21001 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21008 touchSlide : false,
21009 getAutoCreate : function(){
21014 // item is needed for carousel - not sure if it has any effect otherwise
21015 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21016 html: this.html || ''
21020 cfg.cls += ' active';
21024 cfg.tabId = this.tabId;
21032 initEvents: function()
21034 var p = this.parent();
21036 this.navId = this.navId || p.navId;
21038 if (typeof(this.navId) != 'undefined') {
21039 // not really needed.. but just in case.. parent should be a NavGroup.
21040 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21044 var i = tg.tabs.length - 1;
21046 if(this.active && tg.bullets > 0 && i < tg.bullets){
21047 tg.setActiveBullet(i);
21051 this.el.on('click', this.onClick, this);
21053 if(Roo.isTouch && this.touchSlide){
21054 this.el.on("touchstart", this.onTouchStart, this);
21055 this.el.on("touchmove", this.onTouchMove, this);
21056 this.el.on("touchend", this.onTouchEnd, this);
21061 onRender : function(ct, position)
21063 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21066 setActive : function(state)
21068 Roo.log("panel - set active " + this.tabId + "=" + state);
21070 this.active = state;
21072 this.el.removeClass('active');
21074 } else if (!this.el.hasClass('active')) {
21075 this.el.addClass('active');
21078 this.fireEvent('changed', this, state);
21081 onClick : function(e)
21083 e.preventDefault();
21085 if(!this.href.length){
21089 window.location.href = this.href;
21098 onTouchStart : function(e)
21100 this.swiping = false;
21102 this.startX = e.browserEvent.touches[0].clientX;
21103 this.startY = e.browserEvent.touches[0].clientY;
21106 onTouchMove : function(e)
21108 this.swiping = true;
21110 this.endX = e.browserEvent.touches[0].clientX;
21111 this.endY = e.browserEvent.touches[0].clientY;
21114 onTouchEnd : function(e)
21121 var tabGroup = this.parent();
21123 if(this.endX > this.startX){ // swiping right
21124 tabGroup.showPanelPrev();
21128 if(this.startX > this.endX){ // swiping left
21129 tabGroup.showPanelNext();
21148 * @class Roo.bootstrap.DateField
21149 * @extends Roo.bootstrap.Input
21150 * Bootstrap DateField class
21151 * @cfg {Number} weekStart default 0
21152 * @cfg {String} viewMode default empty, (months|years)
21153 * @cfg {String} minViewMode default empty, (months|years)
21154 * @cfg {Number} startDate default -Infinity
21155 * @cfg {Number} endDate default Infinity
21156 * @cfg {Boolean} todayHighlight default false
21157 * @cfg {Boolean} todayBtn default false
21158 * @cfg {Boolean} calendarWeeks default false
21159 * @cfg {Object} daysOfWeekDisabled default empty
21160 * @cfg {Boolean} singleMode default false (true | false)
21162 * @cfg {Boolean} keyboardNavigation default true
21163 * @cfg {String} language default en
21166 * Create a new DateField
21167 * @param {Object} config The config object
21170 Roo.bootstrap.DateField = function(config){
21171 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21175 * Fires when this field show.
21176 * @param {Roo.bootstrap.DateField} this
21177 * @param {Mixed} date The date value
21182 * Fires when this field hide.
21183 * @param {Roo.bootstrap.DateField} this
21184 * @param {Mixed} date The date value
21189 * Fires when select a date.
21190 * @param {Roo.bootstrap.DateField} this
21191 * @param {Mixed} date The date value
21195 * @event beforeselect
21196 * Fires when before select a date.
21197 * @param {Roo.bootstrap.DateField} this
21198 * @param {Mixed} date The date value
21200 beforeselect : true
21204 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21207 * @cfg {String} format
21208 * The default date format string which can be overriden for localization support. The format must be
21209 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21213 * @cfg {String} altFormats
21214 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21215 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21217 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21225 todayHighlight : false,
21231 keyboardNavigation: true,
21233 calendarWeeks: false,
21235 startDate: -Infinity,
21239 daysOfWeekDisabled: [],
21243 singleMode : false,
21245 UTCDate: function()
21247 return new Date(Date.UTC.apply(Date, arguments));
21250 UTCToday: function()
21252 var today = new Date();
21253 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21256 getDate: function() {
21257 var d = this.getUTCDate();
21258 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21261 getUTCDate: function() {
21265 setDate: function(d) {
21266 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21269 setUTCDate: function(d) {
21271 this.setValue(this.formatDate(this.date));
21274 onRender: function(ct, position)
21277 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21279 this.language = this.language || 'en';
21280 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21281 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21283 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21284 this.format = this.format || 'm/d/y';
21285 this.isInline = false;
21286 this.isInput = true;
21287 this.component = this.el.select('.add-on', true).first() || false;
21288 this.component = (this.component && this.component.length === 0) ? false : this.component;
21289 this.hasInput = this.component && this.inputEl().length;
21291 if (typeof(this.minViewMode === 'string')) {
21292 switch (this.minViewMode) {
21294 this.minViewMode = 1;
21297 this.minViewMode = 2;
21300 this.minViewMode = 0;
21305 if (typeof(this.viewMode === 'string')) {
21306 switch (this.viewMode) {
21319 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21321 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21323 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21325 this.picker().on('mousedown', this.onMousedown, this);
21326 this.picker().on('click', this.onClick, this);
21328 this.picker().addClass('datepicker-dropdown');
21330 this.startViewMode = this.viewMode;
21332 if(this.singleMode){
21333 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21334 v.setVisibilityMode(Roo.Element.DISPLAY);
21338 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21339 v.setStyle('width', '189px');
21343 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21344 if(!this.calendarWeeks){
21349 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21350 v.attr('colspan', function(i, val){
21351 return parseInt(val) + 1;
21356 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21358 this.setStartDate(this.startDate);
21359 this.setEndDate(this.endDate);
21361 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21368 if(this.isInline) {
21373 picker : function()
21375 return this.pickerEl;
21376 // return this.el.select('.datepicker', true).first();
21379 fillDow: function()
21381 var dowCnt = this.weekStart;
21390 if(this.calendarWeeks){
21398 while (dowCnt < this.weekStart + 7) {
21402 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21406 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21409 fillMonths: function()
21412 var months = this.picker().select('>.datepicker-months td', true).first();
21414 months.dom.innerHTML = '';
21420 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21423 months.createChild(month);
21430 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;
21432 if (this.date < this.startDate) {
21433 this.viewDate = new Date(this.startDate);
21434 } else if (this.date > this.endDate) {
21435 this.viewDate = new Date(this.endDate);
21437 this.viewDate = new Date(this.date);
21445 var d = new Date(this.viewDate),
21446 year = d.getUTCFullYear(),
21447 month = d.getUTCMonth(),
21448 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21449 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21450 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21451 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21452 currentDate = this.date && this.date.valueOf(),
21453 today = this.UTCToday();
21455 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21457 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21459 // this.picker.select('>tfoot th.today').
21460 // .text(dates[this.language].today)
21461 // .toggle(this.todayBtn !== false);
21463 this.updateNavArrows();
21466 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21468 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21470 prevMonth.setUTCDate(day);
21472 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21474 var nextMonth = new Date(prevMonth);
21476 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21478 nextMonth = nextMonth.valueOf();
21480 var fillMonths = false;
21482 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21484 while(prevMonth.valueOf() <= nextMonth) {
21487 if (prevMonth.getUTCDay() === this.weekStart) {
21489 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21497 if(this.calendarWeeks){
21498 // ISO 8601: First week contains first thursday.
21499 // ISO also states week starts on Monday, but we can be more abstract here.
21501 // Start of current week: based on weekstart/current date
21502 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21503 // Thursday of this week
21504 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21505 // First Thursday of year, year from thursday
21506 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21507 // Calendar week: ms between thursdays, div ms per day, div 7 days
21508 calWeek = (th - yth) / 864e5 / 7 + 1;
21510 fillMonths.cn.push({
21518 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21520 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21523 if (this.todayHighlight &&
21524 prevMonth.getUTCFullYear() == today.getFullYear() &&
21525 prevMonth.getUTCMonth() == today.getMonth() &&
21526 prevMonth.getUTCDate() == today.getDate()) {
21527 clsName += ' today';
21530 if (currentDate && prevMonth.valueOf() === currentDate) {
21531 clsName += ' active';
21534 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21535 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21536 clsName += ' disabled';
21539 fillMonths.cn.push({
21541 cls: 'day ' + clsName,
21542 html: prevMonth.getDate()
21545 prevMonth.setDate(prevMonth.getDate()+1);
21548 var currentYear = this.date && this.date.getUTCFullYear();
21549 var currentMonth = this.date && this.date.getUTCMonth();
21551 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21553 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21554 v.removeClass('active');
21556 if(currentYear === year && k === currentMonth){
21557 v.addClass('active');
21560 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21561 v.addClass('disabled');
21567 year = parseInt(year/10, 10) * 10;
21569 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21571 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21574 for (var i = -1; i < 11; i++) {
21575 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21577 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21585 showMode: function(dir)
21588 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21591 Roo.each(this.picker().select('>div',true).elements, function(v){
21592 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21595 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21600 if(this.isInline) {
21604 this.picker().removeClass(['bottom', 'top']);
21606 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21608 * place to the top of element!
21612 this.picker().addClass('top');
21613 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21618 this.picker().addClass('bottom');
21620 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21623 parseDate : function(value)
21625 if(!value || value instanceof Date){
21628 var v = Date.parseDate(value, this.format);
21629 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21630 v = Date.parseDate(value, 'Y-m-d');
21632 if(!v && this.altFormats){
21633 if(!this.altFormatsArray){
21634 this.altFormatsArray = this.altFormats.split("|");
21636 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21637 v = Date.parseDate(value, this.altFormatsArray[i]);
21643 formatDate : function(date, fmt)
21645 return (!date || !(date instanceof Date)) ?
21646 date : date.dateFormat(fmt || this.format);
21649 onFocus : function()
21651 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21655 onBlur : function()
21657 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21659 var d = this.inputEl().getValue();
21666 showPopup : function()
21668 this.picker().show();
21672 this.fireEvent('showpopup', this, this.date);
21675 hidePopup : function()
21677 if(this.isInline) {
21680 this.picker().hide();
21681 this.viewMode = this.startViewMode;
21684 this.fireEvent('hidepopup', this, this.date);
21688 onMousedown: function(e)
21690 e.stopPropagation();
21691 e.preventDefault();
21696 Roo.bootstrap.DateField.superclass.keyup.call(this);
21700 setValue: function(v)
21702 if(this.fireEvent('beforeselect', this, v) !== false){
21703 var d = new Date(this.parseDate(v) ).clearTime();
21705 if(isNaN(d.getTime())){
21706 this.date = this.viewDate = '';
21707 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21711 v = this.formatDate(d);
21713 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21715 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21719 this.fireEvent('select', this, this.date);
21723 getValue: function()
21725 return this.formatDate(this.date);
21728 fireKey: function(e)
21730 if (!this.picker().isVisible()){
21731 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21737 var dateChanged = false,
21739 newDate, newViewDate;
21744 e.preventDefault();
21748 if (!this.keyboardNavigation) {
21751 dir = e.keyCode == 37 ? -1 : 1;
21754 newDate = this.moveYear(this.date, dir);
21755 newViewDate = this.moveYear(this.viewDate, dir);
21756 } else if (e.shiftKey){
21757 newDate = this.moveMonth(this.date, dir);
21758 newViewDate = this.moveMonth(this.viewDate, dir);
21760 newDate = new Date(this.date);
21761 newDate.setUTCDate(this.date.getUTCDate() + dir);
21762 newViewDate = new Date(this.viewDate);
21763 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21765 if (this.dateWithinRange(newDate)){
21766 this.date = newDate;
21767 this.viewDate = newViewDate;
21768 this.setValue(this.formatDate(this.date));
21770 e.preventDefault();
21771 dateChanged = true;
21776 if (!this.keyboardNavigation) {
21779 dir = e.keyCode == 38 ? -1 : 1;
21781 newDate = this.moveYear(this.date, dir);
21782 newViewDate = this.moveYear(this.viewDate, dir);
21783 } else if (e.shiftKey){
21784 newDate = this.moveMonth(this.date, dir);
21785 newViewDate = this.moveMonth(this.viewDate, dir);
21787 newDate = new Date(this.date);
21788 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21789 newViewDate = new Date(this.viewDate);
21790 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21792 if (this.dateWithinRange(newDate)){
21793 this.date = newDate;
21794 this.viewDate = newViewDate;
21795 this.setValue(this.formatDate(this.date));
21797 e.preventDefault();
21798 dateChanged = true;
21802 this.setValue(this.formatDate(this.date));
21804 e.preventDefault();
21807 this.setValue(this.formatDate(this.date));
21821 onClick: function(e)
21823 e.stopPropagation();
21824 e.preventDefault();
21826 var target = e.getTarget();
21828 if(target.nodeName.toLowerCase() === 'i'){
21829 target = Roo.get(target).dom.parentNode;
21832 var nodeName = target.nodeName;
21833 var className = target.className;
21834 var html = target.innerHTML;
21835 //Roo.log(nodeName);
21837 switch(nodeName.toLowerCase()) {
21839 switch(className) {
21845 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21846 switch(this.viewMode){
21848 this.viewDate = this.moveMonth(this.viewDate, dir);
21852 this.viewDate = this.moveYear(this.viewDate, dir);
21858 var date = new Date();
21859 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21861 this.setValue(this.formatDate(this.date));
21868 if (className.indexOf('disabled') < 0) {
21869 this.viewDate.setUTCDate(1);
21870 if (className.indexOf('month') > -1) {
21871 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21873 var year = parseInt(html, 10) || 0;
21874 this.viewDate.setUTCFullYear(year);
21878 if(this.singleMode){
21879 this.setValue(this.formatDate(this.viewDate));
21890 //Roo.log(className);
21891 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21892 var day = parseInt(html, 10) || 1;
21893 var year = (this.viewDate || new Date()).getUTCFullYear(),
21894 month = (this.viewDate || new Date()).getUTCMonth();
21896 if (className.indexOf('old') > -1) {
21903 } else if (className.indexOf('new') > -1) {
21911 //Roo.log([year,month,day]);
21912 this.date = this.UTCDate(year, month, day,0,0,0,0);
21913 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21915 //Roo.log(this.formatDate(this.date));
21916 this.setValue(this.formatDate(this.date));
21923 setStartDate: function(startDate)
21925 this.startDate = startDate || -Infinity;
21926 if (this.startDate !== -Infinity) {
21927 this.startDate = this.parseDate(this.startDate);
21930 this.updateNavArrows();
21933 setEndDate: function(endDate)
21935 this.endDate = endDate || Infinity;
21936 if (this.endDate !== Infinity) {
21937 this.endDate = this.parseDate(this.endDate);
21940 this.updateNavArrows();
21943 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21945 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21946 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21947 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21949 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21950 return parseInt(d, 10);
21953 this.updateNavArrows();
21956 updateNavArrows: function()
21958 if(this.singleMode){
21962 var d = new Date(this.viewDate),
21963 year = d.getUTCFullYear(),
21964 month = d.getUTCMonth();
21966 Roo.each(this.picker().select('.prev', true).elements, function(v){
21968 switch (this.viewMode) {
21971 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21977 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21984 Roo.each(this.picker().select('.next', true).elements, function(v){
21986 switch (this.viewMode) {
21989 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21995 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22003 moveMonth: function(date, dir)
22008 var new_date = new Date(date.valueOf()),
22009 day = new_date.getUTCDate(),
22010 month = new_date.getUTCMonth(),
22011 mag = Math.abs(dir),
22013 dir = dir > 0 ? 1 : -1;
22016 // If going back one month, make sure month is not current month
22017 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22019 return new_date.getUTCMonth() == month;
22021 // If going forward one month, make sure month is as expected
22022 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22024 return new_date.getUTCMonth() != new_month;
22026 new_month = month + dir;
22027 new_date.setUTCMonth(new_month);
22028 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22029 if (new_month < 0 || new_month > 11) {
22030 new_month = (new_month + 12) % 12;
22033 // For magnitudes >1, move one month at a time...
22034 for (var i=0; i<mag; i++) {
22035 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22036 new_date = this.moveMonth(new_date, dir);
22038 // ...then reset the day, keeping it in the new month
22039 new_month = new_date.getUTCMonth();
22040 new_date.setUTCDate(day);
22042 return new_month != new_date.getUTCMonth();
22045 // Common date-resetting loop -- if date is beyond end of month, make it
22048 new_date.setUTCDate(--day);
22049 new_date.setUTCMonth(new_month);
22054 moveYear: function(date, dir)
22056 return this.moveMonth(date, dir*12);
22059 dateWithinRange: function(date)
22061 return date >= this.startDate && date <= this.endDate;
22067 this.picker().remove();
22070 validateValue : function(value)
22072 if(this.getVisibilityEl().hasClass('hidden')){
22076 if(value.length < 1) {
22077 if(this.allowBlank){
22083 if(value.length < this.minLength){
22086 if(value.length > this.maxLength){
22090 var vt = Roo.form.VTypes;
22091 if(!vt[this.vtype](value, this)){
22095 if(typeof this.validator == "function"){
22096 var msg = this.validator(value);
22102 if(this.regex && !this.regex.test(value)){
22106 if(typeof(this.parseDate(value)) == 'undefined'){
22110 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22114 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22124 this.date = this.viewDate = '';
22126 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22131 Roo.apply(Roo.bootstrap.DateField, {
22142 html: '<i class="fa fa-arrow-left"/>'
22152 html: '<i class="fa fa-arrow-right"/>'
22194 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22195 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22196 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22197 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22198 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22211 navFnc: 'FullYear',
22216 navFnc: 'FullYear',
22221 Roo.apply(Roo.bootstrap.DateField, {
22225 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22229 cls: 'datepicker-days',
22233 cls: 'table-condensed',
22235 Roo.bootstrap.DateField.head,
22239 Roo.bootstrap.DateField.footer
22246 cls: 'datepicker-months',
22250 cls: 'table-condensed',
22252 Roo.bootstrap.DateField.head,
22253 Roo.bootstrap.DateField.content,
22254 Roo.bootstrap.DateField.footer
22261 cls: 'datepicker-years',
22265 cls: 'table-condensed',
22267 Roo.bootstrap.DateField.head,
22268 Roo.bootstrap.DateField.content,
22269 Roo.bootstrap.DateField.footer
22288 * @class Roo.bootstrap.TimeField
22289 * @extends Roo.bootstrap.Input
22290 * Bootstrap DateField class
22294 * Create a new TimeField
22295 * @param {Object} config The config object
22298 Roo.bootstrap.TimeField = function(config){
22299 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22303 * Fires when this field show.
22304 * @param {Roo.bootstrap.DateField} thisthis
22305 * @param {Mixed} date The date value
22310 * Fires when this field hide.
22311 * @param {Roo.bootstrap.DateField} this
22312 * @param {Mixed} date The date value
22317 * Fires when select a date.
22318 * @param {Roo.bootstrap.DateField} this
22319 * @param {Mixed} date The date value
22325 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22328 * @cfg {String} format
22329 * The default time format string which can be overriden for localization support. The format must be
22330 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22334 getAutoCreate : function()
22336 this.after = '<i class="fa far fa-clock"></i>';
22337 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22341 onRender: function(ct, position)
22344 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22346 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22348 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22350 this.pop = this.picker().select('>.datepicker-time',true).first();
22351 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22353 this.picker().on('mousedown', this.onMousedown, this);
22354 this.picker().on('click', this.onClick, this);
22356 this.picker().addClass('datepicker-dropdown');
22361 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22362 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22363 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22364 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22365 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22366 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22370 fireKey: function(e){
22371 if (!this.picker().isVisible()){
22372 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22378 e.preventDefault();
22386 this.onTogglePeriod();
22389 this.onIncrementMinutes();
22392 this.onDecrementMinutes();
22401 onClick: function(e) {
22402 e.stopPropagation();
22403 e.preventDefault();
22406 picker : function()
22408 return this.pickerEl;
22411 fillTime: function()
22413 var time = this.pop.select('tbody', true).first();
22415 time.dom.innerHTML = '';
22430 cls: 'hours-up fa fas fa-chevron-up'
22450 cls: 'minutes-up fa fas fa-chevron-up'
22471 cls: 'timepicker-hour',
22486 cls: 'timepicker-minute',
22501 cls: 'btn btn-primary period',
22523 cls: 'hours-down fa fas fa-chevron-down'
22543 cls: 'minutes-down fa fas fa-chevron-down'
22561 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22568 var hours = this.time.getHours();
22569 var minutes = this.time.getMinutes();
22582 hours = hours - 12;
22586 hours = '0' + hours;
22590 minutes = '0' + minutes;
22593 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22594 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22595 this.pop.select('button', true).first().dom.innerHTML = period;
22601 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22603 var cls = ['bottom'];
22605 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22612 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22616 //this.picker().setXY(20000,20000);
22617 this.picker().addClass(cls.join('-'));
22621 Roo.each(cls, function(c){
22626 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22627 //_this.picker().setTop(_this.inputEl().getHeight());
22631 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22633 //_this.picker().setTop(0 - _this.picker().getHeight());
22638 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22642 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22650 onFocus : function()
22652 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22656 onBlur : function()
22658 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22664 this.picker().show();
22669 this.fireEvent('show', this, this.date);
22674 this.picker().hide();
22677 this.fireEvent('hide', this, this.date);
22680 setTime : function()
22683 this.setValue(this.time.format(this.format));
22685 this.fireEvent('select', this, this.date);
22690 onMousedown: function(e){
22691 e.stopPropagation();
22692 e.preventDefault();
22695 onIncrementHours: function()
22697 Roo.log('onIncrementHours');
22698 this.time = this.time.add(Date.HOUR, 1);
22703 onDecrementHours: function()
22705 Roo.log('onDecrementHours');
22706 this.time = this.time.add(Date.HOUR, -1);
22710 onIncrementMinutes: function()
22712 Roo.log('onIncrementMinutes');
22713 this.time = this.time.add(Date.MINUTE, 1);
22717 onDecrementMinutes: function()
22719 Roo.log('onDecrementMinutes');
22720 this.time = this.time.add(Date.MINUTE, -1);
22724 onTogglePeriod: function()
22726 Roo.log('onTogglePeriod');
22727 this.time = this.time.add(Date.HOUR, 12);
22735 Roo.apply(Roo.bootstrap.TimeField, {
22739 cls: 'datepicker dropdown-menu',
22743 cls: 'datepicker-time',
22747 cls: 'table-condensed',
22776 cls: 'btn btn-info ok',
22804 * @class Roo.bootstrap.MonthField
22805 * @extends Roo.bootstrap.Input
22806 * Bootstrap MonthField class
22808 * @cfg {String} language default en
22811 * Create a new MonthField
22812 * @param {Object} config The config object
22815 Roo.bootstrap.MonthField = function(config){
22816 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22821 * Fires when this field show.
22822 * @param {Roo.bootstrap.MonthField} this
22823 * @param {Mixed} date The date value
22828 * Fires when this field hide.
22829 * @param {Roo.bootstrap.MonthField} this
22830 * @param {Mixed} date The date value
22835 * Fires when select a date.
22836 * @param {Roo.bootstrap.MonthField} this
22837 * @param {String} oldvalue The old value
22838 * @param {String} newvalue The new value
22844 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22846 onRender: function(ct, position)
22849 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22851 this.language = this.language || 'en';
22852 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22853 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22855 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22856 this.isInline = false;
22857 this.isInput = true;
22858 this.component = this.el.select('.add-on', true).first() || false;
22859 this.component = (this.component && this.component.length === 0) ? false : this.component;
22860 this.hasInput = this.component && this.inputEL().length;
22862 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22864 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22866 this.picker().on('mousedown', this.onMousedown, this);
22867 this.picker().on('click', this.onClick, this);
22869 this.picker().addClass('datepicker-dropdown');
22871 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22872 v.setStyle('width', '189px');
22879 if(this.isInline) {
22885 setValue: function(v, suppressEvent)
22887 var o = this.getValue();
22889 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22893 if(suppressEvent !== true){
22894 this.fireEvent('select', this, o, v);
22899 getValue: function()
22904 onClick: function(e)
22906 e.stopPropagation();
22907 e.preventDefault();
22909 var target = e.getTarget();
22911 if(target.nodeName.toLowerCase() === 'i'){
22912 target = Roo.get(target).dom.parentNode;
22915 var nodeName = target.nodeName;
22916 var className = target.className;
22917 var html = target.innerHTML;
22919 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22923 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22925 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22931 picker : function()
22933 return this.pickerEl;
22936 fillMonths: function()
22939 var months = this.picker().select('>.datepicker-months td', true).first();
22941 months.dom.innerHTML = '';
22947 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22950 months.createChild(month);
22959 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22960 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22963 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22964 e.removeClass('active');
22966 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22967 e.addClass('active');
22974 if(this.isInline) {
22978 this.picker().removeClass(['bottom', 'top']);
22980 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22982 * place to the top of element!
22986 this.picker().addClass('top');
22987 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22992 this.picker().addClass('bottom');
22994 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22997 onFocus : function()
22999 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23003 onBlur : function()
23005 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23007 var d = this.inputEl().getValue();
23016 this.picker().show();
23017 this.picker().select('>.datepicker-months', true).first().show();
23021 this.fireEvent('show', this, this.date);
23026 if(this.isInline) {
23029 this.picker().hide();
23030 this.fireEvent('hide', this, this.date);
23034 onMousedown: function(e)
23036 e.stopPropagation();
23037 e.preventDefault();
23042 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23046 fireKey: function(e)
23048 if (!this.picker().isVisible()){
23049 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23060 e.preventDefault();
23064 dir = e.keyCode == 37 ? -1 : 1;
23066 this.vIndex = this.vIndex + dir;
23068 if(this.vIndex < 0){
23072 if(this.vIndex > 11){
23076 if(isNaN(this.vIndex)){
23080 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23086 dir = e.keyCode == 38 ? -1 : 1;
23088 this.vIndex = this.vIndex + dir * 4;
23090 if(this.vIndex < 0){
23094 if(this.vIndex > 11){
23098 if(isNaN(this.vIndex)){
23102 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23107 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23108 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23112 e.preventDefault();
23115 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23116 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23132 this.picker().remove();
23137 Roo.apply(Roo.bootstrap.MonthField, {
23156 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23157 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23162 Roo.apply(Roo.bootstrap.MonthField, {
23166 cls: 'datepicker dropdown-menu roo-dynamic',
23170 cls: 'datepicker-months',
23174 cls: 'table-condensed',
23176 Roo.bootstrap.DateField.content
23196 * @class Roo.bootstrap.CheckBox
23197 * @extends Roo.bootstrap.Input
23198 * Bootstrap CheckBox class
23200 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23201 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23202 * @cfg {String} boxLabel The text that appears beside the checkbox
23203 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23204 * @cfg {Boolean} checked initnal the element
23205 * @cfg {Boolean} inline inline the element (default false)
23206 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23207 * @cfg {String} tooltip label tooltip
23210 * Create a new CheckBox
23211 * @param {Object} config The config object
23214 Roo.bootstrap.CheckBox = function(config){
23215 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23220 * Fires when the element is checked or unchecked.
23221 * @param {Roo.bootstrap.CheckBox} this This input
23222 * @param {Boolean} checked The new checked value
23227 * Fires when the element is click.
23228 * @param {Roo.bootstrap.CheckBox} this This input
23235 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23237 inputType: 'checkbox',
23246 // checkbox success does not make any sense really..
23251 getAutoCreate : function()
23253 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23259 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23262 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23268 type : this.inputType,
23269 value : this.inputValue,
23270 cls : 'roo-' + this.inputType, //'form-box',
23271 placeholder : this.placeholder || ''
23275 if(this.inputType != 'radio'){
23279 cls : 'roo-hidden-value',
23280 value : this.checked ? this.inputValue : this.valueOff
23285 if (this.weight) { // Validity check?
23286 cfg.cls += " " + this.inputType + "-" + this.weight;
23289 if (this.disabled) {
23290 input.disabled=true;
23294 input.checked = this.checked;
23299 input.name = this.name;
23301 if(this.inputType != 'radio'){
23302 hidden.name = this.name;
23303 input.name = '_hidden_' + this.name;
23308 input.cls += ' input-' + this.size;
23313 ['xs','sm','md','lg'].map(function(size){
23314 if (settings[size]) {
23315 cfg.cls += ' col-' + size + '-' + settings[size];
23319 var inputblock = input;
23321 if (this.before || this.after) {
23324 cls : 'input-group',
23329 inputblock.cn.push({
23331 cls : 'input-group-addon',
23336 inputblock.cn.push(input);
23338 if(this.inputType != 'radio'){
23339 inputblock.cn.push(hidden);
23343 inputblock.cn.push({
23345 cls : 'input-group-addon',
23351 var boxLabelCfg = false;
23357 //'for': id, // box label is handled by onclick - so no for...
23359 html: this.boxLabel
23362 boxLabelCfg.tooltip = this.tooltip;
23368 if (align ==='left' && this.fieldLabel.length) {
23369 // Roo.log("left and has label");
23374 cls : 'control-label',
23375 html : this.fieldLabel
23386 cfg.cn[1].cn.push(boxLabelCfg);
23389 if(this.labelWidth > 12){
23390 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23393 if(this.labelWidth < 13 && this.labelmd == 0){
23394 this.labelmd = this.labelWidth;
23397 if(this.labellg > 0){
23398 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23399 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23402 if(this.labelmd > 0){
23403 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23404 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23407 if(this.labelsm > 0){
23408 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23409 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23412 if(this.labelxs > 0){
23413 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23414 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23417 } else if ( this.fieldLabel.length) {
23418 // Roo.log(" label");
23422 tag: this.boxLabel ? 'span' : 'label',
23424 cls: 'control-label box-input-label',
23425 //cls : 'input-group-addon',
23426 html : this.fieldLabel
23433 cfg.cn.push(boxLabelCfg);
23438 // Roo.log(" no label && no align");
23439 cfg.cn = [ inputblock ] ;
23441 cfg.cn.push(boxLabelCfg);
23449 if(this.inputType != 'radio'){
23450 cfg.cn.push(hidden);
23458 * return the real input element.
23460 inputEl: function ()
23462 return this.el.select('input.roo-' + this.inputType,true).first();
23464 hiddenEl: function ()
23466 return this.el.select('input.roo-hidden-value',true).first();
23469 labelEl: function()
23471 return this.el.select('label.control-label',true).first();
23473 /* depricated... */
23477 return this.labelEl();
23480 boxLabelEl: function()
23482 return this.el.select('label.box-label',true).first();
23485 initEvents : function()
23487 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23489 this.inputEl().on('click', this.onClick, this);
23491 if (this.boxLabel) {
23492 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23495 this.startValue = this.getValue();
23498 Roo.bootstrap.CheckBox.register(this);
23502 onClick : function(e)
23504 if(this.fireEvent('click', this, e) !== false){
23505 this.setChecked(!this.checked);
23510 setChecked : function(state,suppressEvent)
23512 this.startValue = this.getValue();
23514 if(this.inputType == 'radio'){
23516 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23517 e.dom.checked = false;
23520 this.inputEl().dom.checked = true;
23522 this.inputEl().dom.value = this.inputValue;
23524 if(suppressEvent !== true){
23525 this.fireEvent('check', this, true);
23533 this.checked = state;
23535 this.inputEl().dom.checked = state;
23538 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23540 if(suppressEvent !== true){
23541 this.fireEvent('check', this, state);
23547 getValue : function()
23549 if(this.inputType == 'radio'){
23550 return this.getGroupValue();
23553 return this.hiddenEl().dom.value;
23557 getGroupValue : function()
23559 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23563 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23566 setValue : function(v,suppressEvent)
23568 if(this.inputType == 'radio'){
23569 this.setGroupValue(v, suppressEvent);
23573 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23578 setGroupValue : function(v, suppressEvent)
23580 this.startValue = this.getValue();
23582 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23583 e.dom.checked = false;
23585 if(e.dom.value == v){
23586 e.dom.checked = true;
23590 if(suppressEvent !== true){
23591 this.fireEvent('check', this, true);
23599 validate : function()
23601 if(this.getVisibilityEl().hasClass('hidden')){
23607 (this.inputType == 'radio' && this.validateRadio()) ||
23608 (this.inputType == 'checkbox' && this.validateCheckbox())
23614 this.markInvalid();
23618 validateRadio : function()
23620 if(this.getVisibilityEl().hasClass('hidden')){
23624 if(this.allowBlank){
23630 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23631 if(!e.dom.checked){
23643 validateCheckbox : function()
23646 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23647 //return (this.getValue() == this.inputValue) ? true : false;
23650 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23658 for(var i in group){
23659 if(group[i].el.isVisible(true)){
23667 for(var i in group){
23672 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23679 * Mark this field as valid
23681 markValid : function()
23685 this.fireEvent('valid', this);
23687 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23690 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23697 if(this.inputType == 'radio'){
23698 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23699 var fg = e.findParent('.form-group', false, true);
23700 if (Roo.bootstrap.version == 3) {
23701 fg.removeClass([_this.invalidClass, _this.validClass]);
23702 fg.addClass(_this.validClass);
23704 fg.removeClass(['is-valid', 'is-invalid']);
23705 fg.addClass('is-valid');
23713 var fg = this.el.findParent('.form-group', false, true);
23714 if (Roo.bootstrap.version == 3) {
23715 fg.removeClass([this.invalidClass, this.validClass]);
23716 fg.addClass(this.validClass);
23718 fg.removeClass(['is-valid', 'is-invalid']);
23719 fg.addClass('is-valid');
23724 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23730 for(var i in group){
23731 var fg = group[i].el.findParent('.form-group', false, true);
23732 if (Roo.bootstrap.version == 3) {
23733 fg.removeClass([this.invalidClass, this.validClass]);
23734 fg.addClass(this.validClass);
23736 fg.removeClass(['is-valid', 'is-invalid']);
23737 fg.addClass('is-valid');
23743 * Mark this field as invalid
23744 * @param {String} msg The validation message
23746 markInvalid : function(msg)
23748 if(this.allowBlank){
23754 this.fireEvent('invalid', this, msg);
23756 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23759 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23763 label.markInvalid();
23766 if(this.inputType == 'radio'){
23768 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23769 var fg = e.findParent('.form-group', false, true);
23770 if (Roo.bootstrap.version == 3) {
23771 fg.removeClass([_this.invalidClass, _this.validClass]);
23772 fg.addClass(_this.invalidClass);
23774 fg.removeClass(['is-invalid', 'is-valid']);
23775 fg.addClass('is-invalid');
23783 var fg = this.el.findParent('.form-group', false, true);
23784 if (Roo.bootstrap.version == 3) {
23785 fg.removeClass([_this.invalidClass, _this.validClass]);
23786 fg.addClass(_this.invalidClass);
23788 fg.removeClass(['is-invalid', 'is-valid']);
23789 fg.addClass('is-invalid');
23794 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23800 for(var i in group){
23801 var fg = group[i].el.findParent('.form-group', false, true);
23802 if (Roo.bootstrap.version == 3) {
23803 fg.removeClass([_this.invalidClass, _this.validClass]);
23804 fg.addClass(_this.invalidClass);
23806 fg.removeClass(['is-invalid', 'is-valid']);
23807 fg.addClass('is-invalid');
23813 clearInvalid : function()
23815 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23817 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23819 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23821 if (label && label.iconEl) {
23822 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23823 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23827 disable : function()
23829 if(this.inputType != 'radio'){
23830 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23837 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23838 _this.getActionEl().addClass(this.disabledClass);
23839 e.dom.disabled = true;
23843 this.disabled = true;
23844 this.fireEvent("disable", this);
23848 enable : function()
23850 if(this.inputType != 'radio'){
23851 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23858 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23859 _this.getActionEl().removeClass(this.disabledClass);
23860 e.dom.disabled = false;
23864 this.disabled = false;
23865 this.fireEvent("enable", this);
23869 setBoxLabel : function(v)
23874 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23880 Roo.apply(Roo.bootstrap.CheckBox, {
23885 * register a CheckBox Group
23886 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23888 register : function(checkbox)
23890 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23891 this.groups[checkbox.groupId] = {};
23894 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23898 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23902 * fetch a CheckBox Group based on the group ID
23903 * @param {string} the group ID
23904 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23906 get: function(groupId) {
23907 if (typeof(this.groups[groupId]) == 'undefined') {
23911 return this.groups[groupId] ;
23924 * @class Roo.bootstrap.Radio
23925 * @extends Roo.bootstrap.Component
23926 * Bootstrap Radio class
23927 * @cfg {String} boxLabel - the label associated
23928 * @cfg {String} value - the value of radio
23931 * Create a new Radio
23932 * @param {Object} config The config object
23934 Roo.bootstrap.Radio = function(config){
23935 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23939 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23945 getAutoCreate : function()
23949 cls : 'form-group radio',
23954 html : this.boxLabel
23962 initEvents : function()
23964 this.parent().register(this);
23966 this.el.on('click', this.onClick, this);
23970 onClick : function(e)
23972 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23973 this.setChecked(true);
23977 setChecked : function(state, suppressEvent)
23979 this.parent().setValue(this.value, suppressEvent);
23983 setBoxLabel : function(v)
23988 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24003 * @class Roo.bootstrap.SecurePass
24004 * @extends Roo.bootstrap.Input
24005 * Bootstrap SecurePass class
24009 * Create a new SecurePass
24010 * @param {Object} config The config object
24013 Roo.bootstrap.SecurePass = function (config) {
24014 // these go here, so the translation tool can replace them..
24016 PwdEmpty: "Please type a password, and then retype it to confirm.",
24017 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24018 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24019 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24020 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24021 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24022 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24023 TooWeak: "Your password is Too Weak."
24025 this.meterLabel = "Password strength:";
24026 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24027 this.meterClass = [
24028 "roo-password-meter-tooweak",
24029 "roo-password-meter-weak",
24030 "roo-password-meter-medium",
24031 "roo-password-meter-strong",
24032 "roo-password-meter-grey"
24037 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24040 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24042 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24044 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24045 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24046 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24047 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24048 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24049 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24050 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24060 * @cfg {String/Object} Label for the strength meter (defaults to
24061 * 'Password strength:')
24066 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24067 * ['Weak', 'Medium', 'Strong'])
24070 pwdStrengths: false,
24083 initEvents: function ()
24085 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24087 if (this.el.is('input[type=password]') && Roo.isSafari) {
24088 this.el.on('keydown', this.SafariOnKeyDown, this);
24091 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24094 onRender: function (ct, position)
24096 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24097 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24098 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24100 this.trigger.createChild({
24105 cls: 'roo-password-meter-grey col-xs-12',
24108 //width: this.meterWidth + 'px'
24112 cls: 'roo-password-meter-text'
24118 if (this.hideTrigger) {
24119 this.trigger.setDisplayed(false);
24121 this.setSize(this.width || '', this.height || '');
24124 onDestroy: function ()
24126 if (this.trigger) {
24127 this.trigger.removeAllListeners();
24128 this.trigger.remove();
24131 this.wrap.remove();
24133 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24136 checkStrength: function ()
24138 var pwd = this.inputEl().getValue();
24139 if (pwd == this._lastPwd) {
24144 if (this.ClientSideStrongPassword(pwd)) {
24146 } else if (this.ClientSideMediumPassword(pwd)) {
24148 } else if (this.ClientSideWeakPassword(pwd)) {
24154 Roo.log('strength1: ' + strength);
24156 //var pm = this.trigger.child('div/div/div').dom;
24157 var pm = this.trigger.child('div/div');
24158 pm.removeClass(this.meterClass);
24159 pm.addClass(this.meterClass[strength]);
24162 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24164 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24166 this._lastPwd = pwd;
24170 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24172 this._lastPwd = '';
24174 var pm = this.trigger.child('div/div');
24175 pm.removeClass(this.meterClass);
24176 pm.addClass('roo-password-meter-grey');
24179 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24182 this.inputEl().dom.type='password';
24185 validateValue: function (value)
24187 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24190 if (value.length == 0) {
24191 if (this.allowBlank) {
24192 this.clearInvalid();
24196 this.markInvalid(this.errors.PwdEmpty);
24197 this.errorMsg = this.errors.PwdEmpty;
24205 if (!value.match(/[\x21-\x7e]+/)) {
24206 this.markInvalid(this.errors.PwdBadChar);
24207 this.errorMsg = this.errors.PwdBadChar;
24210 if (value.length < 6) {
24211 this.markInvalid(this.errors.PwdShort);
24212 this.errorMsg = this.errors.PwdShort;
24215 if (value.length > 16) {
24216 this.markInvalid(this.errors.PwdLong);
24217 this.errorMsg = this.errors.PwdLong;
24221 if (this.ClientSideStrongPassword(value)) {
24223 } else if (this.ClientSideMediumPassword(value)) {
24225 } else if (this.ClientSideWeakPassword(value)) {
24232 if (strength < 2) {
24233 //this.markInvalid(this.errors.TooWeak);
24234 this.errorMsg = this.errors.TooWeak;
24239 console.log('strength2: ' + strength);
24241 //var pm = this.trigger.child('div/div/div').dom;
24243 var pm = this.trigger.child('div/div');
24244 pm.removeClass(this.meterClass);
24245 pm.addClass(this.meterClass[strength]);
24247 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24249 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24251 this.errorMsg = '';
24255 CharacterSetChecks: function (type)
24258 this.fResult = false;
24261 isctype: function (character, type)
24264 case this.kCapitalLetter:
24265 if (character >= 'A' && character <= 'Z') {
24270 case this.kSmallLetter:
24271 if (character >= 'a' && character <= 'z') {
24277 if (character >= '0' && character <= '9') {
24282 case this.kPunctuation:
24283 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24294 IsLongEnough: function (pwd, size)
24296 return !(pwd == null || isNaN(size) || pwd.length < size);
24299 SpansEnoughCharacterSets: function (word, nb)
24301 if (!this.IsLongEnough(word, nb))
24306 var characterSetChecks = new Array(
24307 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24308 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24311 for (var index = 0; index < word.length; ++index) {
24312 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24313 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24314 characterSetChecks[nCharSet].fResult = true;
24321 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24322 if (characterSetChecks[nCharSet].fResult) {
24327 if (nCharSets < nb) {
24333 ClientSideStrongPassword: function (pwd)
24335 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24338 ClientSideMediumPassword: function (pwd)
24340 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24343 ClientSideWeakPassword: function (pwd)
24345 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24348 })//<script type="text/javascript">
24351 * Based Ext JS Library 1.1.1
24352 * Copyright(c) 2006-2007, Ext JS, LLC.
24358 * @class Roo.HtmlEditorCore
24359 * @extends Roo.Component
24360 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24362 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24365 Roo.HtmlEditorCore = function(config){
24368 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24373 * @event initialize
24374 * Fires when the editor is fully initialized (including the iframe)
24375 * @param {Roo.HtmlEditorCore} this
24380 * Fires when the editor is first receives the focus. Any insertion must wait
24381 * until after this event.
24382 * @param {Roo.HtmlEditorCore} this
24386 * @event beforesync
24387 * Fires before the textarea is updated with content from the editor iframe. Return false
24388 * to cancel the sync.
24389 * @param {Roo.HtmlEditorCore} this
24390 * @param {String} html
24394 * @event beforepush
24395 * Fires before the iframe editor is updated with content from the textarea. Return false
24396 * to cancel the push.
24397 * @param {Roo.HtmlEditorCore} this
24398 * @param {String} html
24403 * Fires when the textarea is updated with content from the editor iframe.
24404 * @param {Roo.HtmlEditorCore} this
24405 * @param {String} html
24410 * Fires when the iframe editor is updated with content from the textarea.
24411 * @param {Roo.HtmlEditorCore} this
24412 * @param {String} html
24417 * @event editorevent
24418 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24419 * @param {Roo.HtmlEditorCore} this
24425 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24427 // defaults : white / black...
24428 this.applyBlacklists();
24435 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24439 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24445 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24450 * @cfg {Number} height (in pixels)
24454 * @cfg {Number} width (in pixels)
24459 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24462 stylesheets: false,
24467 // private properties
24468 validationEvent : false,
24470 initialized : false,
24472 sourceEditMode : false,
24473 onFocus : Roo.emptyFn,
24475 hideMode:'offsets',
24479 // blacklist + whitelisted elements..
24486 * Protected method that will not generally be called directly. It
24487 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24488 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24490 getDocMarkup : function(){
24494 // inherit styels from page...??
24495 if (this.stylesheets === false) {
24497 Roo.get(document.head).select('style').each(function(node) {
24498 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24501 Roo.get(document.head).select('link').each(function(node) {
24502 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24505 } else if (!this.stylesheets.length) {
24507 st = '<style type="text/css">' +
24508 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24511 for (var i in this.stylesheets) {
24512 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24517 st += '<style type="text/css">' +
24518 'IMG { cursor: pointer } ' +
24521 var cls = 'roo-htmleditor-body';
24523 if(this.bodyCls.length){
24524 cls += ' ' + this.bodyCls;
24527 return '<html><head>' + st +
24528 //<style type="text/css">' +
24529 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24531 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24535 onRender : function(ct, position)
24538 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24539 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24542 this.el.dom.style.border = '0 none';
24543 this.el.dom.setAttribute('tabIndex', -1);
24544 this.el.addClass('x-hidden hide');
24548 if(Roo.isIE){ // fix IE 1px bogus margin
24549 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24553 this.frameId = Roo.id();
24557 var iframe = this.owner.wrap.createChild({
24559 cls: 'form-control', // bootstrap..
24561 name: this.frameId,
24562 frameBorder : 'no',
24563 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24568 this.iframe = iframe.dom;
24570 this.assignDocWin();
24572 this.doc.designMode = 'on';
24575 this.doc.write(this.getDocMarkup());
24579 var task = { // must defer to wait for browser to be ready
24581 //console.log("run task?" + this.doc.readyState);
24582 this.assignDocWin();
24583 if(this.doc.body || this.doc.readyState == 'complete'){
24585 this.doc.designMode="on";
24589 Roo.TaskMgr.stop(task);
24590 this.initEditor.defer(10, this);
24597 Roo.TaskMgr.start(task);
24602 onResize : function(w, h)
24604 Roo.log('resize: ' +w + ',' + h );
24605 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24609 if(typeof w == 'number'){
24611 this.iframe.style.width = w + 'px';
24613 if(typeof h == 'number'){
24615 this.iframe.style.height = h + 'px';
24617 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24624 * Toggles the editor between standard and source edit mode.
24625 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24627 toggleSourceEdit : function(sourceEditMode){
24629 this.sourceEditMode = sourceEditMode === true;
24631 if(this.sourceEditMode){
24633 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24636 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24637 //this.iframe.className = '';
24640 //this.setSize(this.owner.wrap.getSize());
24641 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24648 * Protected method that will not generally be called directly. If you need/want
24649 * custom HTML cleanup, this is the method you should override.
24650 * @param {String} html The HTML to be cleaned
24651 * return {String} The cleaned HTML
24653 cleanHtml : function(html){
24654 html = String(html);
24655 if(html.length > 5){
24656 if(Roo.isSafari){ // strip safari nonsense
24657 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24660 if(html == ' '){
24667 * HTML Editor -> Textarea
24668 * Protected method that will not generally be called directly. Syncs the contents
24669 * of the editor iframe with the textarea.
24671 syncValue : function(){
24672 if(this.initialized){
24673 var bd = (this.doc.body || this.doc.documentElement);
24674 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24675 var html = bd.innerHTML;
24677 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24678 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24680 html = '<div style="'+m[0]+'">' + html + '</div>';
24683 html = this.cleanHtml(html);
24684 // fix up the special chars.. normaly like back quotes in word...
24685 // however we do not want to do this with chinese..
24686 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24688 var cc = match.charCodeAt();
24690 // Get the character value, handling surrogate pairs
24691 if (match.length == 2) {
24692 // It's a surrogate pair, calculate the Unicode code point
24693 var high = match.charCodeAt(0) - 0xD800;
24694 var low = match.charCodeAt(1) - 0xDC00;
24695 cc = (high * 0x400) + low + 0x10000;
24697 (cc >= 0x4E00 && cc < 0xA000 ) ||
24698 (cc >= 0x3400 && cc < 0x4E00 ) ||
24699 (cc >= 0xf900 && cc < 0xfb00 )
24704 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24705 return "&#" + cc + ";";
24712 if(this.owner.fireEvent('beforesync', this, html) !== false){
24713 this.el.dom.value = html;
24714 this.owner.fireEvent('sync', this, html);
24720 * Protected method that will not generally be called directly. Pushes the value of the textarea
24721 * into the iframe editor.
24723 pushValue : function(){
24724 if(this.initialized){
24725 var v = this.el.dom.value.trim();
24727 // if(v.length < 1){
24731 if(this.owner.fireEvent('beforepush', this, v) !== false){
24732 var d = (this.doc.body || this.doc.documentElement);
24734 this.cleanUpPaste();
24735 this.el.dom.value = d.innerHTML;
24736 this.owner.fireEvent('push', this, v);
24742 deferFocus : function(){
24743 this.focus.defer(10, this);
24747 focus : function(){
24748 if(this.win && !this.sourceEditMode){
24755 assignDocWin: function()
24757 var iframe = this.iframe;
24760 this.doc = iframe.contentWindow.document;
24761 this.win = iframe.contentWindow;
24763 // if (!Roo.get(this.frameId)) {
24766 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24767 // this.win = Roo.get(this.frameId).dom.contentWindow;
24769 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24773 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24774 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24779 initEditor : function(){
24780 //console.log("INIT EDITOR");
24781 this.assignDocWin();
24785 this.doc.designMode="on";
24787 this.doc.write(this.getDocMarkup());
24790 var dbody = (this.doc.body || this.doc.documentElement);
24791 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24792 // this copies styles from the containing element into thsi one..
24793 // not sure why we need all of this..
24794 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24796 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24797 //ss['background-attachment'] = 'fixed'; // w3c
24798 dbody.bgProperties = 'fixed'; // ie
24799 //Roo.DomHelper.applyStyles(dbody, ss);
24800 Roo.EventManager.on(this.doc, {
24801 //'mousedown': this.onEditorEvent,
24802 'mouseup': this.onEditorEvent,
24803 'dblclick': this.onEditorEvent,
24804 'click': this.onEditorEvent,
24805 'keyup': this.onEditorEvent,
24810 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24812 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24813 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24815 this.initialized = true;
24817 this.owner.fireEvent('initialize', this);
24822 onDestroy : function(){
24828 //for (var i =0; i < this.toolbars.length;i++) {
24829 // // fixme - ask toolbars for heights?
24830 // this.toolbars[i].onDestroy();
24833 //this.wrap.dom.innerHTML = '';
24834 //this.wrap.remove();
24839 onFirstFocus : function(){
24841 this.assignDocWin();
24844 this.activated = true;
24847 if(Roo.isGecko){ // prevent silly gecko errors
24849 var s = this.win.getSelection();
24850 if(!s.focusNode || s.focusNode.nodeType != 3){
24851 var r = s.getRangeAt(0);
24852 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24857 this.execCmd('useCSS', true);
24858 this.execCmd('styleWithCSS', false);
24861 this.owner.fireEvent('activate', this);
24865 adjustFont: function(btn){
24866 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24867 //if(Roo.isSafari){ // safari
24870 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24871 if(Roo.isSafari){ // safari
24872 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24873 v = (v < 10) ? 10 : v;
24874 v = (v > 48) ? 48 : v;
24875 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24880 v = Math.max(1, v+adjust);
24882 this.execCmd('FontSize', v );
24885 onEditorEvent : function(e)
24887 this.owner.fireEvent('editorevent', this, e);
24888 // this.updateToolbar();
24889 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24892 insertTag : function(tg)
24894 // could be a bit smarter... -> wrap the current selected tRoo..
24895 if (tg.toLowerCase() == 'span' ||
24896 tg.toLowerCase() == 'code' ||
24897 tg.toLowerCase() == 'sup' ||
24898 tg.toLowerCase() == 'sub'
24901 range = this.createRange(this.getSelection());
24902 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24903 wrappingNode.appendChild(range.extractContents());
24904 range.insertNode(wrappingNode);
24911 this.execCmd("formatblock", tg);
24915 insertText : function(txt)
24919 var range = this.createRange();
24920 range.deleteContents();
24921 //alert(Sender.getAttribute('label'));
24923 range.insertNode(this.doc.createTextNode(txt));
24929 * Executes a Midas editor command on the editor document and performs necessary focus and
24930 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24931 * @param {String} cmd The Midas command
24932 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24934 relayCmd : function(cmd, value){
24936 this.execCmd(cmd, value);
24937 this.owner.fireEvent('editorevent', this);
24938 //this.updateToolbar();
24939 this.owner.deferFocus();
24943 * Executes a Midas editor command directly on the editor document.
24944 * For visual commands, you should use {@link #relayCmd} instead.
24945 * <b>This should only be called after the editor is initialized.</b>
24946 * @param {String} cmd The Midas command
24947 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24949 execCmd : function(cmd, value){
24950 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24957 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24959 * @param {String} text | dom node..
24961 insertAtCursor : function(text)
24964 if(!this.activated){
24970 var r = this.doc.selection.createRange();
24981 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24985 // from jquery ui (MIT licenced)
24987 var win = this.win;
24989 if (win.getSelection && win.getSelection().getRangeAt) {
24990 range = win.getSelection().getRangeAt(0);
24991 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24992 range.insertNode(node);
24993 } else if (win.document.selection && win.document.selection.createRange) {
24994 // no firefox support
24995 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24996 win.document.selection.createRange().pasteHTML(txt);
24998 // no firefox support
24999 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25000 this.execCmd('InsertHTML', txt);
25009 mozKeyPress : function(e){
25011 var c = e.getCharCode(), cmd;
25014 c = String.fromCharCode(c).toLowerCase();
25028 this.cleanUpPaste.defer(100, this);
25036 e.preventDefault();
25044 fixKeys : function(){ // load time branching for fastest keydown performance
25046 return function(e){
25047 var k = e.getKey(), r;
25050 r = this.doc.selection.createRange();
25053 r.pasteHTML('    ');
25060 r = this.doc.selection.createRange();
25062 var target = r.parentElement();
25063 if(!target || target.tagName.toLowerCase() != 'li'){
25065 r.pasteHTML('<br />');
25071 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25072 this.cleanUpPaste.defer(100, this);
25078 }else if(Roo.isOpera){
25079 return function(e){
25080 var k = e.getKey();
25084 this.execCmd('InsertHTML','    ');
25087 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25088 this.cleanUpPaste.defer(100, this);
25093 }else if(Roo.isSafari){
25094 return function(e){
25095 var k = e.getKey();
25099 this.execCmd('InsertText','\t');
25103 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25104 this.cleanUpPaste.defer(100, this);
25112 getAllAncestors: function()
25114 var p = this.getSelectedNode();
25117 a.push(p); // push blank onto stack..
25118 p = this.getParentElement();
25122 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25126 a.push(this.doc.body);
25130 lastSelNode : false,
25133 getSelection : function()
25135 this.assignDocWin();
25136 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25139 getSelectedNode: function()
25141 // this may only work on Gecko!!!
25143 // should we cache this!!!!
25148 var range = this.createRange(this.getSelection()).cloneRange();
25151 var parent = range.parentElement();
25153 var testRange = range.duplicate();
25154 testRange.moveToElementText(parent);
25155 if (testRange.inRange(range)) {
25158 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25161 parent = parent.parentElement;
25166 // is ancestor a text element.
25167 var ac = range.commonAncestorContainer;
25168 if (ac.nodeType == 3) {
25169 ac = ac.parentNode;
25172 var ar = ac.childNodes;
25175 var other_nodes = [];
25176 var has_other_nodes = false;
25177 for (var i=0;i<ar.length;i++) {
25178 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25181 // fullly contained node.
25183 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25188 // probably selected..
25189 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25190 other_nodes.push(ar[i]);
25194 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25199 has_other_nodes = true;
25201 if (!nodes.length && other_nodes.length) {
25202 nodes= other_nodes;
25204 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25210 createRange: function(sel)
25212 // this has strange effects when using with
25213 // top toolbar - not sure if it's a great idea.
25214 //this.editor.contentWindow.focus();
25215 if (typeof sel != "undefined") {
25217 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25219 return this.doc.createRange();
25222 return this.doc.createRange();
25225 getParentElement: function()
25228 this.assignDocWin();
25229 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25231 var range = this.createRange(sel);
25234 var p = range.commonAncestorContainer;
25235 while (p.nodeType == 3) { // text node
25246 * Range intersection.. the hard stuff...
25250 * [ -- selected range --- ]
25254 * if end is before start or hits it. fail.
25255 * if start is after end or hits it fail.
25257 * if either hits (but other is outside. - then it's not
25263 // @see http://www.thismuchiknow.co.uk/?p=64.
25264 rangeIntersectsNode : function(range, node)
25266 var nodeRange = node.ownerDocument.createRange();
25268 nodeRange.selectNode(node);
25270 nodeRange.selectNodeContents(node);
25273 var rangeStartRange = range.cloneRange();
25274 rangeStartRange.collapse(true);
25276 var rangeEndRange = range.cloneRange();
25277 rangeEndRange.collapse(false);
25279 var nodeStartRange = nodeRange.cloneRange();
25280 nodeStartRange.collapse(true);
25282 var nodeEndRange = nodeRange.cloneRange();
25283 nodeEndRange.collapse(false);
25285 return rangeStartRange.compareBoundaryPoints(
25286 Range.START_TO_START, nodeEndRange) == -1 &&
25287 rangeEndRange.compareBoundaryPoints(
25288 Range.START_TO_START, nodeStartRange) == 1;
25292 rangeCompareNode : function(range, node)
25294 var nodeRange = node.ownerDocument.createRange();
25296 nodeRange.selectNode(node);
25298 nodeRange.selectNodeContents(node);
25302 range.collapse(true);
25304 nodeRange.collapse(true);
25306 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25307 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25309 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25311 var nodeIsBefore = ss == 1;
25312 var nodeIsAfter = ee == -1;
25314 if (nodeIsBefore && nodeIsAfter) {
25317 if (!nodeIsBefore && nodeIsAfter) {
25318 return 1; //right trailed.
25321 if (nodeIsBefore && !nodeIsAfter) {
25322 return 2; // left trailed.
25328 // private? - in a new class?
25329 cleanUpPaste : function()
25331 // cleans up the whole document..
25332 Roo.log('cleanuppaste');
25334 this.cleanUpChildren(this.doc.body);
25335 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25336 if (clean != this.doc.body.innerHTML) {
25337 this.doc.body.innerHTML = clean;
25342 cleanWordChars : function(input) {// change the chars to hex code
25343 var he = Roo.HtmlEditorCore;
25345 var output = input;
25346 Roo.each(he.swapCodes, function(sw) {
25347 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25349 output = output.replace(swapper, sw[1]);
25356 cleanUpChildren : function (n)
25358 if (!n.childNodes.length) {
25361 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25362 this.cleanUpChild(n.childNodes[i]);
25369 cleanUpChild : function (node)
25372 //console.log(node);
25373 if (node.nodeName == "#text") {
25374 // clean up silly Windows -- stuff?
25377 if (node.nodeName == "#comment") {
25378 node.parentNode.removeChild(node);
25379 // clean up silly Windows -- stuff?
25382 var lcname = node.tagName.toLowerCase();
25383 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25384 // whitelist of tags..
25386 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25388 node.parentNode.removeChild(node);
25393 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25395 // spans with no attributes - just remove them..
25396 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25397 remove_keep_children = true;
25400 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25401 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25403 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25404 // remove_keep_children = true;
25407 if (remove_keep_children) {
25408 this.cleanUpChildren(node);
25409 // inserts everything just before this node...
25410 while (node.childNodes.length) {
25411 var cn = node.childNodes[0];
25412 node.removeChild(cn);
25413 node.parentNode.insertBefore(cn, node);
25415 node.parentNode.removeChild(node);
25419 if (!node.attributes || !node.attributes.length) {
25424 this.cleanUpChildren(node);
25428 function cleanAttr(n,v)
25431 if (v.match(/^\./) || v.match(/^\//)) {
25434 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25437 if (v.match(/^#/)) {
25440 if (v.match(/^\{/)) { // allow template editing.
25443 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25444 node.removeAttribute(n);
25448 var cwhite = this.cwhite;
25449 var cblack = this.cblack;
25451 function cleanStyle(n,v)
25453 if (v.match(/expression/)) { //XSS?? should we even bother..
25454 node.removeAttribute(n);
25458 var parts = v.split(/;/);
25461 Roo.each(parts, function(p) {
25462 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25466 var l = p.split(':').shift().replace(/\s+/g,'');
25467 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25469 if ( cwhite.length && cblack.indexOf(l) > -1) {
25470 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25471 //node.removeAttribute(n);
25475 // only allow 'c whitelisted system attributes'
25476 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25477 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25478 //node.removeAttribute(n);
25488 if (clean.length) {
25489 node.setAttribute(n, clean.join(';'));
25491 node.removeAttribute(n);
25497 for (var i = node.attributes.length-1; i > -1 ; i--) {
25498 var a = node.attributes[i];
25501 if (a.name.toLowerCase().substr(0,2)=='on') {
25502 node.removeAttribute(a.name);
25505 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25506 node.removeAttribute(a.name);
25509 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25510 cleanAttr(a.name,a.value); // fixme..
25513 if (a.name == 'style') {
25514 cleanStyle(a.name,a.value);
25517 /// clean up MS crap..
25518 // tecnically this should be a list of valid class'es..
25521 if (a.name == 'class') {
25522 if (a.value.match(/^Mso/)) {
25523 node.removeAttribute('class');
25526 if (a.value.match(/^body$/)) {
25527 node.removeAttribute('class');
25538 this.cleanUpChildren(node);
25544 * Clean up MS wordisms...
25546 cleanWord : function(node)
25549 this.cleanWord(this.doc.body);
25554 node.nodeName == 'SPAN' &&
25555 !node.hasAttributes() &&
25556 node.childNodes.length == 1 &&
25557 node.firstChild.nodeName == "#text"
25559 var textNode = node.firstChild;
25560 node.removeChild(textNode);
25561 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25562 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25564 node.parentNode.insertBefore(textNode, node);
25565 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25566 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25568 node.parentNode.removeChild(node);
25571 if (node.nodeName == "#text") {
25572 // clean up silly Windows -- stuff?
25575 if (node.nodeName == "#comment") {
25576 node.parentNode.removeChild(node);
25577 // clean up silly Windows -- stuff?
25581 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25582 node.parentNode.removeChild(node);
25585 //Roo.log(node.tagName);
25586 // remove - but keep children..
25587 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25588 //Roo.log('-- removed');
25589 while (node.childNodes.length) {
25590 var cn = node.childNodes[0];
25591 node.removeChild(cn);
25592 node.parentNode.insertBefore(cn, node);
25593 // move node to parent - and clean it..
25594 this.cleanWord(cn);
25596 node.parentNode.removeChild(node);
25597 /// no need to iterate chidlren = it's got none..
25598 //this.iterateChildren(node, this.cleanWord);
25602 if (node.className.length) {
25604 var cn = node.className.split(/\W+/);
25606 Roo.each(cn, function(cls) {
25607 if (cls.match(/Mso[a-zA-Z]+/)) {
25612 node.className = cna.length ? cna.join(' ') : '';
25614 node.removeAttribute("class");
25618 if (node.hasAttribute("lang")) {
25619 node.removeAttribute("lang");
25622 if (node.hasAttribute("style")) {
25624 var styles = node.getAttribute("style").split(";");
25626 Roo.each(styles, function(s) {
25627 if (!s.match(/:/)) {
25630 var kv = s.split(":");
25631 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25634 // what ever is left... we allow.
25637 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25638 if (!nstyle.length) {
25639 node.removeAttribute('style');
25642 this.iterateChildren(node, this.cleanWord);
25648 * iterateChildren of a Node, calling fn each time, using this as the scole..
25649 * @param {DomNode} node node to iterate children of.
25650 * @param {Function} fn method of this class to call on each item.
25652 iterateChildren : function(node, fn)
25654 if (!node.childNodes.length) {
25657 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25658 fn.call(this, node.childNodes[i])
25664 * cleanTableWidths.
25666 * Quite often pasting from word etc.. results in tables with column and widths.
25667 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25670 cleanTableWidths : function(node)
25675 this.cleanTableWidths(this.doc.body);
25680 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25683 Roo.log(node.tagName);
25684 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25685 this.iterateChildren(node, this.cleanTableWidths);
25688 if (node.hasAttribute('width')) {
25689 node.removeAttribute('width');
25693 if (node.hasAttribute("style")) {
25696 var styles = node.getAttribute("style").split(";");
25698 Roo.each(styles, function(s) {
25699 if (!s.match(/:/)) {
25702 var kv = s.split(":");
25703 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25706 // what ever is left... we allow.
25709 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25710 if (!nstyle.length) {
25711 node.removeAttribute('style');
25715 this.iterateChildren(node, this.cleanTableWidths);
25723 domToHTML : function(currentElement, depth, nopadtext) {
25725 depth = depth || 0;
25726 nopadtext = nopadtext || false;
25728 if (!currentElement) {
25729 return this.domToHTML(this.doc.body);
25732 //Roo.log(currentElement);
25734 var allText = false;
25735 var nodeName = currentElement.nodeName;
25736 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25738 if (nodeName == '#text') {
25740 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25745 if (nodeName != 'BODY') {
25748 // Prints the node tagName, such as <A>, <IMG>, etc
25751 for(i = 0; i < currentElement.attributes.length;i++) {
25753 var aname = currentElement.attributes.item(i).name;
25754 if (!currentElement.attributes.item(i).value.length) {
25757 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25760 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25769 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25772 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25777 // Traverse the tree
25779 var currentElementChild = currentElement.childNodes.item(i);
25780 var allText = true;
25781 var innerHTML = '';
25783 while (currentElementChild) {
25784 // Formatting code (indent the tree so it looks nice on the screen)
25785 var nopad = nopadtext;
25786 if (lastnode == 'SPAN') {
25790 if (currentElementChild.nodeName == '#text') {
25791 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25792 toadd = nopadtext ? toadd : toadd.trim();
25793 if (!nopad && toadd.length > 80) {
25794 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25796 innerHTML += toadd;
25799 currentElementChild = currentElement.childNodes.item(i);
25805 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25807 // Recursively traverse the tree structure of the child node
25808 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25809 lastnode = currentElementChild.nodeName;
25811 currentElementChild=currentElement.childNodes.item(i);
25817 // The remaining code is mostly for formatting the tree
25818 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25823 ret+= "</"+tagName+">";
25829 applyBlacklists : function()
25831 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25832 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25836 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25837 if (b.indexOf(tag) > -1) {
25840 this.white.push(tag);
25844 Roo.each(w, function(tag) {
25845 if (b.indexOf(tag) > -1) {
25848 if (this.white.indexOf(tag) > -1) {
25851 this.white.push(tag);
25856 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25857 if (w.indexOf(tag) > -1) {
25860 this.black.push(tag);
25864 Roo.each(b, function(tag) {
25865 if (w.indexOf(tag) > -1) {
25868 if (this.black.indexOf(tag) > -1) {
25871 this.black.push(tag);
25876 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25877 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25881 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25882 if (b.indexOf(tag) > -1) {
25885 this.cwhite.push(tag);
25889 Roo.each(w, function(tag) {
25890 if (b.indexOf(tag) > -1) {
25893 if (this.cwhite.indexOf(tag) > -1) {
25896 this.cwhite.push(tag);
25901 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25902 if (w.indexOf(tag) > -1) {
25905 this.cblack.push(tag);
25909 Roo.each(b, function(tag) {
25910 if (w.indexOf(tag) > -1) {
25913 if (this.cblack.indexOf(tag) > -1) {
25916 this.cblack.push(tag);
25921 setStylesheets : function(stylesheets)
25923 if(typeof(stylesheets) == 'string'){
25924 Roo.get(this.iframe.contentDocument.head).createChild({
25926 rel : 'stylesheet',
25935 Roo.each(stylesheets, function(s) {
25940 Roo.get(_this.iframe.contentDocument.head).createChild({
25942 rel : 'stylesheet',
25951 removeStylesheets : function()
25955 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25960 setStyle : function(style)
25962 Roo.get(this.iframe.contentDocument.head).createChild({
25971 // hide stuff that is not compatible
25985 * @event specialkey
25989 * @cfg {String} fieldClass @hide
25992 * @cfg {String} focusClass @hide
25995 * @cfg {String} autoCreate @hide
25998 * @cfg {String} inputType @hide
26001 * @cfg {String} invalidClass @hide
26004 * @cfg {String} invalidText @hide
26007 * @cfg {String} msgFx @hide
26010 * @cfg {String} validateOnBlur @hide
26014 Roo.HtmlEditorCore.white = [
26015 'area', 'br', 'img', 'input', 'hr', 'wbr',
26017 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26018 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26019 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26020 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26021 'table', 'ul', 'xmp',
26023 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26026 'dir', 'menu', 'ol', 'ul', 'dl',
26032 Roo.HtmlEditorCore.black = [
26033 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26035 'base', 'basefont', 'bgsound', 'blink', 'body',
26036 'frame', 'frameset', 'head', 'html', 'ilayer',
26037 'iframe', 'layer', 'link', 'meta', 'object',
26038 'script', 'style' ,'title', 'xml' // clean later..
26040 Roo.HtmlEditorCore.clean = [
26041 'script', 'style', 'title', 'xml'
26043 Roo.HtmlEditorCore.remove = [
26048 Roo.HtmlEditorCore.ablack = [
26052 Roo.HtmlEditorCore.aclean = [
26053 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26057 Roo.HtmlEditorCore.pwhite= [
26058 'http', 'https', 'mailto'
26061 // white listed style attributes.
26062 Roo.HtmlEditorCore.cwhite= [
26063 // 'text-align', /// default is to allow most things..
26069 // black listed style attributes.
26070 Roo.HtmlEditorCore.cblack= [
26071 // 'font-size' -- this can be set by the project
26075 Roo.HtmlEditorCore.swapCodes =[
26076 [ 8211, "–" ],
26077 [ 8212, "—" ],
26094 * @class Roo.bootstrap.HtmlEditor
26095 * @extends Roo.bootstrap.TextArea
26096 * Bootstrap HtmlEditor class
26099 * Create a new HtmlEditor
26100 * @param {Object} config The config object
26103 Roo.bootstrap.HtmlEditor = function(config){
26104 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26105 if (!this.toolbars) {
26106 this.toolbars = [];
26109 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26112 * @event initialize
26113 * Fires when the editor is fully initialized (including the iframe)
26114 * @param {HtmlEditor} this
26119 * Fires when the editor is first receives the focus. Any insertion must wait
26120 * until after this event.
26121 * @param {HtmlEditor} this
26125 * @event beforesync
26126 * Fires before the textarea is updated with content from the editor iframe. Return false
26127 * to cancel the sync.
26128 * @param {HtmlEditor} this
26129 * @param {String} html
26133 * @event beforepush
26134 * Fires before the iframe editor is updated with content from the textarea. Return false
26135 * to cancel the push.
26136 * @param {HtmlEditor} this
26137 * @param {String} html
26142 * Fires when the textarea is updated with content from the editor iframe.
26143 * @param {HtmlEditor} this
26144 * @param {String} html
26149 * Fires when the iframe editor is updated with content from the textarea.
26150 * @param {HtmlEditor} this
26151 * @param {String} html
26155 * @event editmodechange
26156 * Fires when the editor switches edit modes
26157 * @param {HtmlEditor} this
26158 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26160 editmodechange: true,
26162 * @event editorevent
26163 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26164 * @param {HtmlEditor} this
26168 * @event firstfocus
26169 * Fires when on first focus - needed by toolbars..
26170 * @param {HtmlEditor} this
26175 * Auto save the htmlEditor value as a file into Events
26176 * @param {HtmlEditor} this
26180 * @event savedpreview
26181 * preview the saved version of htmlEditor
26182 * @param {HtmlEditor} this
26189 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26193 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26198 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26203 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26208 * @cfg {Number} height (in pixels)
26212 * @cfg {Number} width (in pixels)
26217 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26220 stylesheets: false,
26225 // private properties
26226 validationEvent : false,
26228 initialized : false,
26231 onFocus : Roo.emptyFn,
26233 hideMode:'offsets',
26235 tbContainer : false,
26239 toolbarContainer :function() {
26240 return this.wrap.select('.x-html-editor-tb',true).first();
26244 * Protected method that will not generally be called directly. It
26245 * is called when the editor creates its toolbar. Override this method if you need to
26246 * add custom toolbar buttons.
26247 * @param {HtmlEditor} editor
26249 createToolbar : function(){
26250 Roo.log('renewing');
26251 Roo.log("create toolbars");
26253 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26254 this.toolbars[0].render(this.toolbarContainer());
26258 // if (!editor.toolbars || !editor.toolbars.length) {
26259 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26262 // for (var i =0 ; i < editor.toolbars.length;i++) {
26263 // editor.toolbars[i] = Roo.factory(
26264 // typeof(editor.toolbars[i]) == 'string' ?
26265 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26266 // Roo.bootstrap.HtmlEditor);
26267 // editor.toolbars[i].init(editor);
26273 onRender : function(ct, position)
26275 // Roo.log("Call onRender: " + this.xtype);
26277 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26279 this.wrap = this.inputEl().wrap({
26280 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26283 this.editorcore.onRender(ct, position);
26285 if (this.resizable) {
26286 this.resizeEl = new Roo.Resizable(this.wrap, {
26290 minHeight : this.height,
26291 height: this.height,
26292 handles : this.resizable,
26295 resize : function(r, w, h) {
26296 _t.onResize(w,h); // -something
26302 this.createToolbar(this);
26305 if(!this.width && this.resizable){
26306 this.setSize(this.wrap.getSize());
26308 if (this.resizeEl) {
26309 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26310 // should trigger onReize..
26316 onResize : function(w, h)
26318 Roo.log('resize: ' +w + ',' + h );
26319 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26323 if(this.inputEl() ){
26324 if(typeof w == 'number'){
26325 var aw = w - this.wrap.getFrameWidth('lr');
26326 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26329 if(typeof h == 'number'){
26330 var tbh = -11; // fixme it needs to tool bar size!
26331 for (var i =0; i < this.toolbars.length;i++) {
26332 // fixme - ask toolbars for heights?
26333 tbh += this.toolbars[i].el.getHeight();
26334 //if (this.toolbars[i].footer) {
26335 // tbh += this.toolbars[i].footer.el.getHeight();
26343 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26344 ah -= 5; // knock a few pixes off for look..
26345 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26349 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26350 this.editorcore.onResize(ew,eh);
26355 * Toggles the editor between standard and source edit mode.
26356 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26358 toggleSourceEdit : function(sourceEditMode)
26360 this.editorcore.toggleSourceEdit(sourceEditMode);
26362 if(this.editorcore.sourceEditMode){
26363 Roo.log('editor - showing textarea');
26366 // Roo.log(this.syncValue());
26368 this.inputEl().removeClass(['hide', 'x-hidden']);
26369 this.inputEl().dom.removeAttribute('tabIndex');
26370 this.inputEl().focus();
26372 Roo.log('editor - hiding textarea');
26374 // Roo.log(this.pushValue());
26377 this.inputEl().addClass(['hide', 'x-hidden']);
26378 this.inputEl().dom.setAttribute('tabIndex', -1);
26379 //this.deferFocus();
26382 if(this.resizable){
26383 this.setSize(this.wrap.getSize());
26386 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26389 // private (for BoxComponent)
26390 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26392 // private (for BoxComponent)
26393 getResizeEl : function(){
26397 // private (for BoxComponent)
26398 getPositionEl : function(){
26403 initEvents : function(){
26404 this.originalValue = this.getValue();
26408 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26411 // markInvalid : Roo.emptyFn,
26413 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26416 // clearInvalid : Roo.emptyFn,
26418 setValue : function(v){
26419 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26420 this.editorcore.pushValue();
26425 deferFocus : function(){
26426 this.focus.defer(10, this);
26430 focus : function(){
26431 this.editorcore.focus();
26437 onDestroy : function(){
26443 for (var i =0; i < this.toolbars.length;i++) {
26444 // fixme - ask toolbars for heights?
26445 this.toolbars[i].onDestroy();
26448 this.wrap.dom.innerHTML = '';
26449 this.wrap.remove();
26454 onFirstFocus : function(){
26455 //Roo.log("onFirstFocus");
26456 this.editorcore.onFirstFocus();
26457 for (var i =0; i < this.toolbars.length;i++) {
26458 this.toolbars[i].onFirstFocus();
26464 syncValue : function()
26466 this.editorcore.syncValue();
26469 pushValue : function()
26471 this.editorcore.pushValue();
26475 // hide stuff that is not compatible
26489 * @event specialkey
26493 * @cfg {String} fieldClass @hide
26496 * @cfg {String} focusClass @hide
26499 * @cfg {String} autoCreate @hide
26502 * @cfg {String} inputType @hide
26506 * @cfg {String} invalidText @hide
26509 * @cfg {String} msgFx @hide
26512 * @cfg {String} validateOnBlur @hide
26521 Roo.namespace('Roo.bootstrap.htmleditor');
26523 * @class Roo.bootstrap.HtmlEditorToolbar1
26529 new Roo.bootstrap.HtmlEditor({
26532 new Roo.bootstrap.HtmlEditorToolbar1({
26533 disable : { fonts: 1 , format: 1, ..., ... , ...],
26539 * @cfg {Object} disable List of elements to disable..
26540 * @cfg {Array} btns List of additional buttons.
26544 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26547 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26550 Roo.apply(this, config);
26552 // default disabled, based on 'good practice'..
26553 this.disable = this.disable || {};
26554 Roo.applyIf(this.disable, {
26557 specialElements : true
26559 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26561 this.editor = config.editor;
26562 this.editorcore = config.editor.editorcore;
26564 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26566 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26567 // dont call parent... till later.
26569 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26574 editorcore : false,
26579 "h1","h2","h3","h4","h5","h6",
26581 "abbr", "acronym", "address", "cite", "samp", "var",
26585 onRender : function(ct, position)
26587 // Roo.log("Call onRender: " + this.xtype);
26589 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26591 this.el.dom.style.marginBottom = '0';
26593 var editorcore = this.editorcore;
26594 var editor= this.editor;
26597 var btn = function(id,cmd , toggle, handler, html){
26599 var event = toggle ? 'toggle' : 'click';
26604 xns: Roo.bootstrap,
26608 enableToggle:toggle !== false,
26610 pressed : toggle ? false : null,
26613 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26614 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26620 // var cb_box = function...
26625 xns: Roo.bootstrap,
26630 xns: Roo.bootstrap,
26634 Roo.each(this.formats, function(f) {
26635 style.menu.items.push({
26637 xns: Roo.bootstrap,
26638 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26643 editorcore.insertTag(this.tagname);
26650 children.push(style);
26652 btn('bold',false,true);
26653 btn('italic',false,true);
26654 btn('align-left', 'justifyleft',true);
26655 btn('align-center', 'justifycenter',true);
26656 btn('align-right' , 'justifyright',true);
26657 btn('link', false, false, function(btn) {
26658 //Roo.log("create link?");
26659 var url = prompt(this.createLinkText, this.defaultLinkValue);
26660 if(url && url != 'http:/'+'/'){
26661 this.editorcore.relayCmd('createlink', url);
26664 btn('list','insertunorderedlist',true);
26665 btn('pencil', false,true, function(btn){
26667 this.toggleSourceEdit(btn.pressed);
26670 if (this.editor.btns.length > 0) {
26671 for (var i = 0; i<this.editor.btns.length; i++) {
26672 children.push(this.editor.btns[i]);
26680 xns: Roo.bootstrap,
26685 xns: Roo.bootstrap,
26690 cog.menu.items.push({
26692 xns: Roo.bootstrap,
26693 html : Clean styles,
26698 editorcore.insertTag(this.tagname);
26707 this.xtype = 'NavSimplebar';
26709 for(var i=0;i< children.length;i++) {
26711 this.buttons.add(this.addxtypeChild(children[i]));
26715 editor.on('editorevent', this.updateToolbar, this);
26717 onBtnClick : function(id)
26719 this.editorcore.relayCmd(id);
26720 this.editorcore.focus();
26724 * Protected method that will not generally be called directly. It triggers
26725 * a toolbar update by reading the markup state of the current selection in the editor.
26727 updateToolbar: function(){
26729 if(!this.editorcore.activated){
26730 this.editor.onFirstFocus(); // is this neeed?
26734 var btns = this.buttons;
26735 var doc = this.editorcore.doc;
26736 btns.get('bold').setActive(doc.queryCommandState('bold'));
26737 btns.get('italic').setActive(doc.queryCommandState('italic'));
26738 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26740 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26741 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26742 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26744 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26745 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26748 var ans = this.editorcore.getAllAncestors();
26749 if (this.formatCombo) {
26752 var store = this.formatCombo.store;
26753 this.formatCombo.setValue("");
26754 for (var i =0; i < ans.length;i++) {
26755 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26757 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26765 // hides menus... - so this cant be on a menu...
26766 Roo.bootstrap.MenuMgr.hideAll();
26768 Roo.bootstrap.MenuMgr.hideAll();
26769 //this.editorsyncValue();
26771 onFirstFocus: function() {
26772 this.buttons.each(function(item){
26776 toggleSourceEdit : function(sourceEditMode){
26779 if(sourceEditMode){
26780 Roo.log("disabling buttons");
26781 this.buttons.each( function(item){
26782 if(item.cmd != 'pencil'){
26788 Roo.log("enabling buttons");
26789 if(this.editorcore.initialized){
26790 this.buttons.each( function(item){
26796 Roo.log("calling toggole on editor");
26797 // tell the editor that it's been pressed..
26798 this.editor.toggleSourceEdit(sourceEditMode);
26812 * @class Roo.bootstrap.Markdown
26813 * @extends Roo.bootstrap.TextArea
26814 * Bootstrap Showdown editable area
26815 * @cfg {string} content
26818 * Create a new Showdown
26821 Roo.bootstrap.Markdown = function(config){
26822 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26826 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26830 initEvents : function()
26833 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26834 this.markdownEl = this.el.createChild({
26835 cls : 'roo-markdown-area'
26837 this.inputEl().addClass('d-none');
26838 if (this.getValue() == '') {
26839 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26842 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26844 this.markdownEl.on('click', this.toggleTextEdit, this);
26845 this.on('blur', this.toggleTextEdit, this);
26846 this.on('specialkey', this.resizeTextArea, this);
26849 toggleTextEdit : function()
26851 var sh = this.markdownEl.getHeight();
26852 this.inputEl().addClass('d-none');
26853 this.markdownEl.addClass('d-none');
26854 if (!this.editing) {
26856 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26857 this.inputEl().removeClass('d-none');
26858 this.inputEl().focus();
26859 this.editing = true;
26862 // show showdown...
26863 this.updateMarkdown();
26864 this.markdownEl.removeClass('d-none');
26865 this.editing = false;
26868 updateMarkdown : function()
26870 if (this.getValue() == '') {
26871 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26875 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26878 resizeTextArea: function () {
26881 Roo.log([sh, this.getValue().split("\n").length * 30]);
26882 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26884 setValue : function(val)
26886 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26887 if (!this.editing) {
26888 this.updateMarkdown();
26894 if (!this.editing) {
26895 this.toggleTextEdit();
26903 * @class Roo.bootstrap.Table.AbstractSelectionModel
26904 * @extends Roo.util.Observable
26905 * Abstract base class for grid SelectionModels. It provides the interface that should be
26906 * implemented by descendant classes. This class should not be directly instantiated.
26909 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26910 this.locked = false;
26911 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26915 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26916 /** @ignore Called by the grid automatically. Do not call directly. */
26917 init : function(grid){
26923 * Locks the selections.
26926 this.locked = true;
26930 * Unlocks the selections.
26932 unlock : function(){
26933 this.locked = false;
26937 * Returns true if the selections are locked.
26938 * @return {Boolean}
26940 isLocked : function(){
26941 return this.locked;
26945 initEvents : function ()
26951 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26952 * @class Roo.bootstrap.Table.RowSelectionModel
26953 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26954 * It supports multiple selections and keyboard selection/navigation.
26956 * @param {Object} config
26959 Roo.bootstrap.Table.RowSelectionModel = function(config){
26960 Roo.apply(this, config);
26961 this.selections = new Roo.util.MixedCollection(false, function(o){
26966 this.lastActive = false;
26970 * @event selectionchange
26971 * Fires when the selection changes
26972 * @param {SelectionModel} this
26974 "selectionchange" : true,
26976 * @event afterselectionchange
26977 * Fires after the selection changes (eg. by key press or clicking)
26978 * @param {SelectionModel} this
26980 "afterselectionchange" : true,
26982 * @event beforerowselect
26983 * Fires when a row is selected being selected, return false to cancel.
26984 * @param {SelectionModel} this
26985 * @param {Number} rowIndex The selected index
26986 * @param {Boolean} keepExisting False if other selections will be cleared
26988 "beforerowselect" : true,
26991 * Fires when a row is selected.
26992 * @param {SelectionModel} this
26993 * @param {Number} rowIndex The selected index
26994 * @param {Roo.data.Record} r The record
26996 "rowselect" : true,
26998 * @event rowdeselect
26999 * Fires when a row is deselected.
27000 * @param {SelectionModel} this
27001 * @param {Number} rowIndex The selected index
27003 "rowdeselect" : true
27005 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27006 this.locked = false;
27009 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27011 * @cfg {Boolean} singleSelect
27012 * True to allow selection of only one row at a time (defaults to false)
27014 singleSelect : false,
27017 initEvents : function()
27020 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27021 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27022 //}else{ // allow click to work like normal
27023 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27025 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27026 this.grid.on("rowclick", this.handleMouseDown, this);
27028 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27029 "up" : function(e){
27031 this.selectPrevious(e.shiftKey);
27032 }else if(this.last !== false && this.lastActive !== false){
27033 var last = this.last;
27034 this.selectRange(this.last, this.lastActive-1);
27035 this.grid.getView().focusRow(this.lastActive);
27036 if(last !== false){
27040 this.selectFirstRow();
27042 this.fireEvent("afterselectionchange", this);
27044 "down" : function(e){
27046 this.selectNext(e.shiftKey);
27047 }else if(this.last !== false && this.lastActive !== false){
27048 var last = this.last;
27049 this.selectRange(this.last, this.lastActive+1);
27050 this.grid.getView().focusRow(this.lastActive);
27051 if(last !== false){
27055 this.selectFirstRow();
27057 this.fireEvent("afterselectionchange", this);
27061 this.grid.store.on('load', function(){
27062 this.selections.clear();
27065 var view = this.grid.view;
27066 view.on("refresh", this.onRefresh, this);
27067 view.on("rowupdated", this.onRowUpdated, this);
27068 view.on("rowremoved", this.onRemove, this);
27073 onRefresh : function()
27075 var ds = this.grid.store, i, v = this.grid.view;
27076 var s = this.selections;
27077 s.each(function(r){
27078 if((i = ds.indexOfId(r.id)) != -1){
27087 onRemove : function(v, index, r){
27088 this.selections.remove(r);
27092 onRowUpdated : function(v, index, r){
27093 if(this.isSelected(r)){
27094 v.onRowSelect(index);
27100 * @param {Array} records The records to select
27101 * @param {Boolean} keepExisting (optional) True to keep existing selections
27103 selectRecords : function(records, keepExisting)
27106 this.clearSelections();
27108 var ds = this.grid.store;
27109 for(var i = 0, len = records.length; i < len; i++){
27110 this.selectRow(ds.indexOf(records[i]), true);
27115 * Gets the number of selected rows.
27118 getCount : function(){
27119 return this.selections.length;
27123 * Selects the first row in the grid.
27125 selectFirstRow : function(){
27130 * Select the last row.
27131 * @param {Boolean} keepExisting (optional) True to keep existing selections
27133 selectLastRow : function(keepExisting){
27134 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27135 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27139 * Selects the row immediately following the last selected row.
27140 * @param {Boolean} keepExisting (optional) True to keep existing selections
27142 selectNext : function(keepExisting)
27144 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27145 this.selectRow(this.last+1, keepExisting);
27146 this.grid.getView().focusRow(this.last);
27151 * Selects the row that precedes the last selected row.
27152 * @param {Boolean} keepExisting (optional) True to keep existing selections
27154 selectPrevious : function(keepExisting){
27156 this.selectRow(this.last-1, keepExisting);
27157 this.grid.getView().focusRow(this.last);
27162 * Returns the selected records
27163 * @return {Array} Array of selected records
27165 getSelections : function(){
27166 return [].concat(this.selections.items);
27170 * Returns the first selected record.
27173 getSelected : function(){
27174 return this.selections.itemAt(0);
27179 * Clears all selections.
27181 clearSelections : function(fast)
27187 var ds = this.grid.store;
27188 var s = this.selections;
27189 s.each(function(r){
27190 this.deselectRow(ds.indexOfId(r.id));
27194 this.selections.clear();
27201 * Selects all rows.
27203 selectAll : function(){
27207 this.selections.clear();
27208 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27209 this.selectRow(i, true);
27214 * Returns True if there is a selection.
27215 * @return {Boolean}
27217 hasSelection : function(){
27218 return this.selections.length > 0;
27222 * Returns True if the specified row is selected.
27223 * @param {Number/Record} record The record or index of the record to check
27224 * @return {Boolean}
27226 isSelected : function(index){
27227 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27228 return (r && this.selections.key(r.id) ? true : false);
27232 * Returns True if the specified record id is selected.
27233 * @param {String} id The id of record to check
27234 * @return {Boolean}
27236 isIdSelected : function(id){
27237 return (this.selections.key(id) ? true : false);
27242 handleMouseDBClick : function(e, t){
27246 handleMouseDown : function(e, t)
27248 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27249 if(this.isLocked() || rowIndex < 0 ){
27252 if(e.shiftKey && this.last !== false){
27253 var last = this.last;
27254 this.selectRange(last, rowIndex, e.ctrlKey);
27255 this.last = last; // reset the last
27259 var isSelected = this.isSelected(rowIndex);
27260 //Roo.log("select row:" + rowIndex);
27262 this.deselectRow(rowIndex);
27264 this.selectRow(rowIndex, true);
27268 if(e.button !== 0 && isSelected){
27269 alert('rowIndex 2: ' + rowIndex);
27270 view.focusRow(rowIndex);
27271 }else if(e.ctrlKey && isSelected){
27272 this.deselectRow(rowIndex);
27273 }else if(!isSelected){
27274 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27275 view.focusRow(rowIndex);
27279 this.fireEvent("afterselectionchange", this);
27282 handleDragableRowClick : function(grid, rowIndex, e)
27284 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27285 this.selectRow(rowIndex, false);
27286 grid.view.focusRow(rowIndex);
27287 this.fireEvent("afterselectionchange", this);
27292 * Selects multiple rows.
27293 * @param {Array} rows Array of the indexes of the row to select
27294 * @param {Boolean} keepExisting (optional) True to keep existing selections
27296 selectRows : function(rows, keepExisting){
27298 this.clearSelections();
27300 for(var i = 0, len = rows.length; i < len; i++){
27301 this.selectRow(rows[i], true);
27306 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27307 * @param {Number} startRow The index of the first row in the range
27308 * @param {Number} endRow The index of the last row in the range
27309 * @param {Boolean} keepExisting (optional) True to retain existing selections
27311 selectRange : function(startRow, endRow, keepExisting){
27316 this.clearSelections();
27318 if(startRow <= endRow){
27319 for(var i = startRow; i <= endRow; i++){
27320 this.selectRow(i, true);
27323 for(var i = startRow; i >= endRow; i--){
27324 this.selectRow(i, true);
27330 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27331 * @param {Number} startRow The index of the first row in the range
27332 * @param {Number} endRow The index of the last row in the range
27334 deselectRange : function(startRow, endRow, preventViewNotify){
27338 for(var i = startRow; i <= endRow; i++){
27339 this.deselectRow(i, preventViewNotify);
27345 * @param {Number} row The index of the row to select
27346 * @param {Boolean} keepExisting (optional) True to keep existing selections
27348 selectRow : function(index, keepExisting, preventViewNotify)
27350 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27353 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27354 if(!keepExisting || this.singleSelect){
27355 this.clearSelections();
27358 var r = this.grid.store.getAt(index);
27359 //console.log('selectRow - record id :' + r.id);
27361 this.selections.add(r);
27362 this.last = this.lastActive = index;
27363 if(!preventViewNotify){
27364 var proxy = new Roo.Element(
27365 this.grid.getRowDom(index)
27367 proxy.addClass('bg-info info');
27369 this.fireEvent("rowselect", this, index, r);
27370 this.fireEvent("selectionchange", this);
27376 * @param {Number} row The index of the row to deselect
27378 deselectRow : function(index, preventViewNotify)
27383 if(this.last == index){
27386 if(this.lastActive == index){
27387 this.lastActive = false;
27390 var r = this.grid.store.getAt(index);
27395 this.selections.remove(r);
27396 //.console.log('deselectRow - record id :' + r.id);
27397 if(!preventViewNotify){
27399 var proxy = new Roo.Element(
27400 this.grid.getRowDom(index)
27402 proxy.removeClass('bg-info info');
27404 this.fireEvent("rowdeselect", this, index);
27405 this.fireEvent("selectionchange", this);
27409 restoreLast : function(){
27411 this.last = this._last;
27416 acceptsNav : function(row, col, cm){
27417 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27421 onEditorKey : function(field, e){
27422 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27427 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27429 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27431 }else if(k == e.ENTER && !e.ctrlKey){
27435 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27437 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27439 }else if(k == e.ESC){
27443 g.startEditing(newCell[0], newCell[1]);
27449 * Ext JS Library 1.1.1
27450 * Copyright(c) 2006-2007, Ext JS, LLC.
27452 * Originally Released Under LGPL - original licence link has changed is not relivant.
27455 * <script type="text/javascript">
27459 * @class Roo.bootstrap.PagingToolbar
27460 * @extends Roo.bootstrap.NavSimplebar
27461 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27463 * Create a new PagingToolbar
27464 * @param {Object} config The config object
27465 * @param {Roo.data.Store} store
27467 Roo.bootstrap.PagingToolbar = function(config)
27469 // old args format still supported... - xtype is prefered..
27470 // created from xtype...
27472 this.ds = config.dataSource;
27474 if (config.store && !this.ds) {
27475 this.store= Roo.factory(config.store, Roo.data);
27476 this.ds = this.store;
27477 this.ds.xmodule = this.xmodule || false;
27480 this.toolbarItems = [];
27481 if (config.items) {
27482 this.toolbarItems = config.items;
27485 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27490 this.bind(this.ds);
27493 if (Roo.bootstrap.version == 4) {
27494 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27496 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27501 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27503 * @cfg {Roo.data.Store} dataSource
27504 * The underlying data store providing the paged data
27507 * @cfg {String/HTMLElement/Element} container
27508 * container The id or element that will contain the toolbar
27511 * @cfg {Boolean} displayInfo
27512 * True to display the displayMsg (defaults to false)
27515 * @cfg {Number} pageSize
27516 * The number of records to display per page (defaults to 20)
27520 * @cfg {String} displayMsg
27521 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27523 displayMsg : 'Displaying {0} - {1} of {2}',
27525 * @cfg {String} emptyMsg
27526 * The message to display when no records are found (defaults to "No data to display")
27528 emptyMsg : 'No data to display',
27530 * Customizable piece of the default paging text (defaults to "Page")
27533 beforePageText : "Page",
27535 * Customizable piece of the default paging text (defaults to "of %0")
27538 afterPageText : "of {0}",
27540 * Customizable piece of the default paging text (defaults to "First Page")
27543 firstText : "First Page",
27545 * Customizable piece of the default paging text (defaults to "Previous Page")
27548 prevText : "Previous Page",
27550 * Customizable piece of the default paging text (defaults to "Next Page")
27553 nextText : "Next Page",
27555 * Customizable piece of the default paging text (defaults to "Last Page")
27558 lastText : "Last Page",
27560 * Customizable piece of the default paging text (defaults to "Refresh")
27563 refreshText : "Refresh",
27567 onRender : function(ct, position)
27569 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27570 this.navgroup.parentId = this.id;
27571 this.navgroup.onRender(this.el, null);
27572 // add the buttons to the navgroup
27574 if(this.displayInfo){
27575 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27576 this.displayEl = this.el.select('.x-paging-info', true).first();
27577 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27578 // this.displayEl = navel.el.select('span',true).first();
27584 Roo.each(_this.buttons, function(e){ // this might need to use render????
27585 Roo.factory(e).render(_this.el);
27589 Roo.each(_this.toolbarItems, function(e) {
27590 _this.navgroup.addItem(e);
27594 this.first = this.navgroup.addItem({
27595 tooltip: this.firstText,
27596 cls: "prev btn-outline-secondary",
27597 html : ' <i class="fa fa-step-backward"></i>',
27599 preventDefault: true,
27600 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27603 this.prev = this.navgroup.addItem({
27604 tooltip: this.prevText,
27605 cls: "prev btn-outline-secondary",
27606 html : ' <i class="fa fa-backward"></i>',
27608 preventDefault: true,
27609 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27611 //this.addSeparator();
27614 var field = this.navgroup.addItem( {
27616 cls : 'x-paging-position btn-outline-secondary',
27618 html : this.beforePageText +
27619 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27620 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27623 this.field = field.el.select('input', true).first();
27624 this.field.on("keydown", this.onPagingKeydown, this);
27625 this.field.on("focus", function(){this.dom.select();});
27628 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27629 //this.field.setHeight(18);
27630 //this.addSeparator();
27631 this.next = this.navgroup.addItem({
27632 tooltip: this.nextText,
27633 cls: "next btn-outline-secondary",
27634 html : ' <i class="fa fa-forward"></i>',
27636 preventDefault: true,
27637 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27639 this.last = this.navgroup.addItem({
27640 tooltip: this.lastText,
27641 html : ' <i class="fa fa-step-forward"></i>',
27642 cls: "next btn-outline-secondary",
27644 preventDefault: true,
27645 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27647 //this.addSeparator();
27648 this.loading = this.navgroup.addItem({
27649 tooltip: this.refreshText,
27650 cls: "btn-outline-secondary",
27651 html : ' <i class="fa fa-refresh"></i>',
27652 preventDefault: true,
27653 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27659 updateInfo : function(){
27660 if(this.displayEl){
27661 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27662 var msg = count == 0 ?
27666 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27668 this.displayEl.update(msg);
27673 onLoad : function(ds, r, o)
27675 this.cursor = o.params && o.params.start ? o.params.start : 0;
27677 var d = this.getPageData(),
27682 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27683 this.field.dom.value = ap;
27684 this.first.setDisabled(ap == 1);
27685 this.prev.setDisabled(ap == 1);
27686 this.next.setDisabled(ap == ps);
27687 this.last.setDisabled(ap == ps);
27688 this.loading.enable();
27693 getPageData : function(){
27694 var total = this.ds.getTotalCount();
27697 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27698 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27703 onLoadError : function(){
27704 this.loading.enable();
27708 onPagingKeydown : function(e){
27709 var k = e.getKey();
27710 var d = this.getPageData();
27712 var v = this.field.dom.value, pageNum;
27713 if(!v || isNaN(pageNum = parseInt(v, 10))){
27714 this.field.dom.value = d.activePage;
27717 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27718 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27721 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))
27723 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27724 this.field.dom.value = pageNum;
27725 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27728 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27730 var v = this.field.dom.value, pageNum;
27731 var increment = (e.shiftKey) ? 10 : 1;
27732 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27735 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27736 this.field.dom.value = d.activePage;
27739 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27741 this.field.dom.value = parseInt(v, 10) + increment;
27742 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27743 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27750 beforeLoad : function(){
27752 this.loading.disable();
27757 onClick : function(which){
27766 ds.load({params:{start: 0, limit: this.pageSize}});
27769 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27772 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27775 var total = ds.getTotalCount();
27776 var extra = total % this.pageSize;
27777 var lastStart = extra ? (total - extra) : total-this.pageSize;
27778 ds.load({params:{start: lastStart, limit: this.pageSize}});
27781 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27787 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27788 * @param {Roo.data.Store} store The data store to unbind
27790 unbind : function(ds){
27791 ds.un("beforeload", this.beforeLoad, this);
27792 ds.un("load", this.onLoad, this);
27793 ds.un("loadexception", this.onLoadError, this);
27794 ds.un("remove", this.updateInfo, this);
27795 ds.un("add", this.updateInfo, this);
27796 this.ds = undefined;
27800 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27801 * @param {Roo.data.Store} store The data store to bind
27803 bind : function(ds){
27804 ds.on("beforeload", this.beforeLoad, this);
27805 ds.on("load", this.onLoad, this);
27806 ds.on("loadexception", this.onLoadError, this);
27807 ds.on("remove", this.updateInfo, this);
27808 ds.on("add", this.updateInfo, this);
27819 * @class Roo.bootstrap.MessageBar
27820 * @extends Roo.bootstrap.Component
27821 * Bootstrap MessageBar class
27822 * @cfg {String} html contents of the MessageBar
27823 * @cfg {String} weight (info | success | warning | danger) default info
27824 * @cfg {String} beforeClass insert the bar before the given class
27825 * @cfg {Boolean} closable (true | false) default false
27826 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27829 * Create a new Element
27830 * @param {Object} config The config object
27833 Roo.bootstrap.MessageBar = function(config){
27834 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27837 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27843 beforeClass: 'bootstrap-sticky-wrap',
27845 getAutoCreate : function(){
27849 cls: 'alert alert-dismissable alert-' + this.weight,
27854 html: this.html || ''
27860 cfg.cls += ' alert-messages-fixed';
27874 onRender : function(ct, position)
27876 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27879 var cfg = Roo.apply({}, this.getAutoCreate());
27883 cfg.cls += ' ' + this.cls;
27886 cfg.style = this.style;
27888 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27890 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27893 this.el.select('>button.close').on('click', this.hide, this);
27899 if (!this.rendered) {
27905 this.fireEvent('show', this);
27911 if (!this.rendered) {
27917 this.fireEvent('hide', this);
27920 update : function()
27922 // var e = this.el.dom.firstChild;
27924 // if(this.closable){
27925 // e = e.nextSibling;
27928 // e.data = this.html || '';
27930 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27946 * @class Roo.bootstrap.Graph
27947 * @extends Roo.bootstrap.Component
27948 * Bootstrap Graph class
27952 @cfg {String} graphtype bar | vbar | pie
27953 @cfg {number} g_x coodinator | centre x (pie)
27954 @cfg {number} g_y coodinator | centre y (pie)
27955 @cfg {number} g_r radius (pie)
27956 @cfg {number} g_height height of the chart (respected by all elements in the set)
27957 @cfg {number} g_width width of the chart (respected by all elements in the set)
27958 @cfg {Object} title The title of the chart
27961 -opts (object) options for the chart
27963 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27964 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27966 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.
27967 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27969 o stretch (boolean)
27971 -opts (object) options for the pie
27974 o startAngle (number)
27975 o endAngle (number)
27979 * Create a new Input
27980 * @param {Object} config The config object
27983 Roo.bootstrap.Graph = function(config){
27984 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27990 * The img click event for the img.
27991 * @param {Roo.EventObject} e
27997 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28008 //g_colors: this.colors,
28015 getAutoCreate : function(){
28026 onRender : function(ct,position){
28029 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28031 if (typeof(Raphael) == 'undefined') {
28032 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28036 this.raphael = Raphael(this.el.dom);
28038 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28039 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28040 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28041 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28043 r.text(160, 10, "Single Series Chart").attr(txtattr);
28044 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28045 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28046 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28048 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28049 r.barchart(330, 10, 300, 220, data1);
28050 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28051 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28054 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28055 // r.barchart(30, 30, 560, 250, xdata, {
28056 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28057 // axis : "0 0 1 1",
28058 // axisxlabels : xdata
28059 // //yvalues : cols,
28062 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28064 // this.load(null,xdata,{
28065 // axis : "0 0 1 1",
28066 // axisxlabels : xdata
28071 load : function(graphtype,xdata,opts)
28073 this.raphael.clear();
28075 graphtype = this.graphtype;
28080 var r = this.raphael,
28081 fin = function () {
28082 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28084 fout = function () {
28085 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28087 pfin = function() {
28088 this.sector.stop();
28089 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28092 this.label[0].stop();
28093 this.label[0].attr({ r: 7.5 });
28094 this.label[1].attr({ "font-weight": 800 });
28097 pfout = function() {
28098 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28101 this.label[0].animate({ r: 5 }, 500, "bounce");
28102 this.label[1].attr({ "font-weight": 400 });
28108 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28111 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28114 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28115 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28117 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28124 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28129 setTitle: function(o)
28134 initEvents: function() {
28137 this.el.on('click', this.onClick, this);
28141 onClick : function(e)
28143 Roo.log('img onclick');
28144 this.fireEvent('click', this, e);
28156 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28159 * @class Roo.bootstrap.dash.NumberBox
28160 * @extends Roo.bootstrap.Component
28161 * Bootstrap NumberBox class
28162 * @cfg {String} headline Box headline
28163 * @cfg {String} content Box content
28164 * @cfg {String} icon Box icon
28165 * @cfg {String} footer Footer text
28166 * @cfg {String} fhref Footer href
28169 * Create a new NumberBox
28170 * @param {Object} config The config object
28174 Roo.bootstrap.dash.NumberBox = function(config){
28175 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28179 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28188 getAutoCreate : function(){
28192 cls : 'small-box ',
28200 cls : 'roo-headline',
28201 html : this.headline
28205 cls : 'roo-content',
28206 html : this.content
28220 cls : 'ion ' + this.icon
28229 cls : 'small-box-footer',
28230 href : this.fhref || '#',
28234 cfg.cn.push(footer);
28241 onRender : function(ct,position){
28242 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28249 setHeadline: function (value)
28251 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28254 setFooter: function (value, href)
28256 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28259 this.el.select('a.small-box-footer',true).first().attr('href', href);
28264 setContent: function (value)
28266 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28269 initEvents: function()
28283 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28286 * @class Roo.bootstrap.dash.TabBox
28287 * @extends Roo.bootstrap.Component
28288 * Bootstrap TabBox class
28289 * @cfg {String} title Title of the TabBox
28290 * @cfg {String} icon Icon of the TabBox
28291 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28292 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28295 * Create a new TabBox
28296 * @param {Object} config The config object
28300 Roo.bootstrap.dash.TabBox = function(config){
28301 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28306 * When a pane is added
28307 * @param {Roo.bootstrap.dash.TabPane} pane
28311 * @event activatepane
28312 * When a pane is activated
28313 * @param {Roo.bootstrap.dash.TabPane} pane
28315 "activatepane" : true
28323 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28328 tabScrollable : false,
28330 getChildContainer : function()
28332 return this.el.select('.tab-content', true).first();
28335 getAutoCreate : function(){
28339 cls: 'pull-left header',
28347 cls: 'fa ' + this.icon
28353 cls: 'nav nav-tabs pull-right',
28359 if(this.tabScrollable){
28366 cls: 'nav nav-tabs pull-right',
28377 cls: 'nav-tabs-custom',
28382 cls: 'tab-content no-padding',
28390 initEvents : function()
28392 //Roo.log('add add pane handler');
28393 this.on('addpane', this.onAddPane, this);
28396 * Updates the box title
28397 * @param {String} html to set the title to.
28399 setTitle : function(value)
28401 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28403 onAddPane : function(pane)
28405 this.panes.push(pane);
28406 //Roo.log('addpane');
28408 // tabs are rendere left to right..
28409 if(!this.showtabs){
28413 var ctr = this.el.select('.nav-tabs', true).first();
28416 var existing = ctr.select('.nav-tab',true);
28417 var qty = existing.getCount();;
28420 var tab = ctr.createChild({
28422 cls : 'nav-tab' + (qty ? '' : ' active'),
28430 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28433 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28435 pane.el.addClass('active');
28440 onTabClick : function(ev,un,ob,pane)
28442 //Roo.log('tab - prev default');
28443 ev.preventDefault();
28446 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28447 pane.tab.addClass('active');
28448 //Roo.log(pane.title);
28449 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28450 // technically we should have a deactivate event.. but maybe add later.
28451 // and it should not de-activate the selected tab...
28452 this.fireEvent('activatepane', pane);
28453 pane.el.addClass('active');
28454 pane.fireEvent('activate');
28459 getActivePane : function()
28462 Roo.each(this.panes, function(p) {
28463 if(p.el.hasClass('active')){
28484 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28486 * @class Roo.bootstrap.TabPane
28487 * @extends Roo.bootstrap.Component
28488 * Bootstrap TabPane class
28489 * @cfg {Boolean} active (false | true) Default false
28490 * @cfg {String} title title of panel
28494 * Create a new TabPane
28495 * @param {Object} config The config object
28498 Roo.bootstrap.dash.TabPane = function(config){
28499 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28505 * When a pane is activated
28506 * @param {Roo.bootstrap.dash.TabPane} pane
28513 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28518 // the tabBox that this is attached to.
28521 getAutoCreate : function()
28529 cfg.cls += ' active';
28534 initEvents : function()
28536 //Roo.log('trigger add pane handler');
28537 this.parent().fireEvent('addpane', this)
28541 * Updates the tab title
28542 * @param {String} html to set the title to.
28544 setTitle: function(str)
28550 this.tab.select('a', true).first().dom.innerHTML = str;
28567 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28570 * @class Roo.bootstrap.menu.Menu
28571 * @extends Roo.bootstrap.Component
28572 * Bootstrap Menu class - container for Menu
28573 * @cfg {String} html Text of the menu
28574 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28575 * @cfg {String} icon Font awesome icon
28576 * @cfg {String} pos Menu align to (top | bottom) default bottom
28580 * Create a new Menu
28581 * @param {Object} config The config object
28585 Roo.bootstrap.menu.Menu = function(config){
28586 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28590 * @event beforeshow
28591 * Fires before this menu is displayed
28592 * @param {Roo.bootstrap.menu.Menu} this
28596 * @event beforehide
28597 * Fires before this menu is hidden
28598 * @param {Roo.bootstrap.menu.Menu} this
28603 * Fires after this menu is displayed
28604 * @param {Roo.bootstrap.menu.Menu} this
28609 * Fires after this menu is hidden
28610 * @param {Roo.bootstrap.menu.Menu} this
28615 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28616 * @param {Roo.bootstrap.menu.Menu} this
28617 * @param {Roo.EventObject} e
28624 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28628 weight : 'default',
28633 getChildContainer : function() {
28634 if(this.isSubMenu){
28638 return this.el.select('ul.dropdown-menu', true).first();
28641 getAutoCreate : function()
28646 cls : 'roo-menu-text',
28654 cls : 'fa ' + this.icon
28665 cls : 'dropdown-button btn btn-' + this.weight,
28670 cls : 'dropdown-toggle btn btn-' + this.weight,
28680 cls : 'dropdown-menu'
28686 if(this.pos == 'top'){
28687 cfg.cls += ' dropup';
28690 if(this.isSubMenu){
28693 cls : 'dropdown-menu'
28700 onRender : function(ct, position)
28702 this.isSubMenu = ct.hasClass('dropdown-submenu');
28704 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28707 initEvents : function()
28709 if(this.isSubMenu){
28713 this.hidden = true;
28715 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28716 this.triggerEl.on('click', this.onTriggerPress, this);
28718 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28719 this.buttonEl.on('click', this.onClick, this);
28725 if(this.isSubMenu){
28729 return this.el.select('ul.dropdown-menu', true).first();
28732 onClick : function(e)
28734 this.fireEvent("click", this, e);
28737 onTriggerPress : function(e)
28739 if (this.isVisible()) {
28746 isVisible : function(){
28747 return !this.hidden;
28752 this.fireEvent("beforeshow", this);
28754 this.hidden = false;
28755 this.el.addClass('open');
28757 Roo.get(document).on("mouseup", this.onMouseUp, this);
28759 this.fireEvent("show", this);
28766 this.fireEvent("beforehide", this);
28768 this.hidden = true;
28769 this.el.removeClass('open');
28771 Roo.get(document).un("mouseup", this.onMouseUp);
28773 this.fireEvent("hide", this);
28776 onMouseUp : function()
28790 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28793 * @class Roo.bootstrap.menu.Item
28794 * @extends Roo.bootstrap.Component
28795 * Bootstrap MenuItem class
28796 * @cfg {Boolean} submenu (true | false) default false
28797 * @cfg {String} html text of the item
28798 * @cfg {String} href the link
28799 * @cfg {Boolean} disable (true | false) default false
28800 * @cfg {Boolean} preventDefault (true | false) default true
28801 * @cfg {String} icon Font awesome icon
28802 * @cfg {String} pos Submenu align to (left | right) default right
28806 * Create a new Item
28807 * @param {Object} config The config object
28811 Roo.bootstrap.menu.Item = function(config){
28812 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28816 * Fires when the mouse is hovering over this menu
28817 * @param {Roo.bootstrap.menu.Item} this
28818 * @param {Roo.EventObject} e
28823 * Fires when the mouse exits this menu
28824 * @param {Roo.bootstrap.menu.Item} this
28825 * @param {Roo.EventObject} e
28831 * The raw click event for the entire grid.
28832 * @param {Roo.EventObject} e
28838 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28843 preventDefault: true,
28848 getAutoCreate : function()
28853 cls : 'roo-menu-item-text',
28861 cls : 'fa ' + this.icon
28870 href : this.href || '#',
28877 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28881 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28883 if(this.pos == 'left'){
28884 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28891 initEvents : function()
28893 this.el.on('mouseover', this.onMouseOver, this);
28894 this.el.on('mouseout', this.onMouseOut, this);
28896 this.el.select('a', true).first().on('click', this.onClick, this);
28900 onClick : function(e)
28902 if(this.preventDefault){
28903 e.preventDefault();
28906 this.fireEvent("click", this, e);
28909 onMouseOver : function(e)
28911 if(this.submenu && this.pos == 'left'){
28912 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28915 this.fireEvent("mouseover", this, e);
28918 onMouseOut : function(e)
28920 this.fireEvent("mouseout", this, e);
28932 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28935 * @class Roo.bootstrap.menu.Separator
28936 * @extends Roo.bootstrap.Component
28937 * Bootstrap Separator class
28940 * Create a new Separator
28941 * @param {Object} config The config object
28945 Roo.bootstrap.menu.Separator = function(config){
28946 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28949 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28951 getAutoCreate : function(){
28972 * @class Roo.bootstrap.Tooltip
28973 * Bootstrap Tooltip class
28974 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28975 * to determine which dom element triggers the tooltip.
28977 * It needs to add support for additional attributes like tooltip-position
28980 * Create a new Toolti
28981 * @param {Object} config The config object
28984 Roo.bootstrap.Tooltip = function(config){
28985 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28987 this.alignment = Roo.bootstrap.Tooltip.alignment;
28989 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28990 this.alignment = config.alignment;
28995 Roo.apply(Roo.bootstrap.Tooltip, {
28997 * @function init initialize tooltip monitoring.
29001 currentTip : false,
29002 currentRegion : false,
29008 Roo.get(document).on('mouseover', this.enter ,this);
29009 Roo.get(document).on('mouseout', this.leave, this);
29012 this.currentTip = new Roo.bootstrap.Tooltip();
29015 enter : function(ev)
29017 var dom = ev.getTarget();
29019 //Roo.log(['enter',dom]);
29020 var el = Roo.fly(dom);
29021 if (this.currentEl) {
29023 //Roo.log(this.currentEl);
29024 //Roo.log(this.currentEl.contains(dom));
29025 if (this.currentEl == el) {
29028 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29034 if (this.currentTip.el) {
29035 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29039 if(!el || el.dom == document){
29045 // you can not look for children, as if el is the body.. then everythign is the child..
29046 if (!el.attr('tooltip')) { //
29047 if (!el.select("[tooltip]").elements.length) {
29050 // is the mouse over this child...?
29051 bindEl = el.select("[tooltip]").first();
29052 var xy = ev.getXY();
29053 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29054 //Roo.log("not in region.");
29057 //Roo.log("child element over..");
29060 this.currentEl = bindEl;
29061 this.currentTip.bind(bindEl);
29062 this.currentRegion = Roo.lib.Region.getRegion(dom);
29063 this.currentTip.enter();
29066 leave : function(ev)
29068 var dom = ev.getTarget();
29069 //Roo.log(['leave',dom]);
29070 if (!this.currentEl) {
29075 if (dom != this.currentEl.dom) {
29078 var xy = ev.getXY();
29079 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29082 // only activate leave if mouse cursor is outside... bounding box..
29087 if (this.currentTip) {
29088 this.currentTip.leave();
29090 //Roo.log('clear currentEl');
29091 this.currentEl = false;
29096 'left' : ['r-l', [-2,0], 'right'],
29097 'right' : ['l-r', [2,0], 'left'],
29098 'bottom' : ['t-b', [0,2], 'top'],
29099 'top' : [ 'b-t', [0,-2], 'bottom']
29105 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29110 delay : null, // can be { show : 300 , hide: 500}
29114 hoverState : null, //???
29116 placement : 'bottom',
29120 getAutoCreate : function(){
29127 cls : 'tooltip-arrow arrow'
29130 cls : 'tooltip-inner'
29137 bind : function(el)
29142 initEvents : function()
29144 this.arrowEl = this.el.select('.arrow', true).first();
29145 this.innerEl = this.el.select('.tooltip-inner', true).first();
29148 enter : function () {
29150 if (this.timeout != null) {
29151 clearTimeout(this.timeout);
29154 this.hoverState = 'in';
29155 //Roo.log("enter - show");
29156 if (!this.delay || !this.delay.show) {
29161 this.timeout = setTimeout(function () {
29162 if (_t.hoverState == 'in') {
29165 }, this.delay.show);
29169 clearTimeout(this.timeout);
29171 this.hoverState = 'out';
29172 if (!this.delay || !this.delay.hide) {
29178 this.timeout = setTimeout(function () {
29179 //Roo.log("leave - timeout");
29181 if (_t.hoverState == 'out') {
29183 Roo.bootstrap.Tooltip.currentEl = false;
29188 show : function (msg)
29191 this.render(document.body);
29194 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29196 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29198 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29200 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29201 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29203 var placement = typeof this.placement == 'function' ?
29204 this.placement.call(this, this.el, on_el) :
29207 var autoToken = /\s?auto?\s?/i;
29208 var autoPlace = autoToken.test(placement);
29210 placement = placement.replace(autoToken, '') || 'top';
29214 //this.el.setXY([0,0]);
29216 //this.el.dom.style.display='block';
29218 //this.el.appendTo(on_el);
29220 var p = this.getPosition();
29221 var box = this.el.getBox();
29227 var align = this.alignment[placement];
29229 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29231 if(placement == 'top' || placement == 'bottom'){
29233 placement = 'right';
29236 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29237 placement = 'left';
29240 var scroll = Roo.select('body', true).first().getScroll();
29242 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29246 align = this.alignment[placement];
29248 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29252 this.el.alignTo(this.bindEl, align[0],align[1]);
29253 //var arrow = this.el.select('.arrow',true).first();
29254 //arrow.set(align[2],
29256 this.el.addClass(placement);
29257 this.el.addClass("bs-tooltip-"+ placement);
29259 this.el.addClass('in fade show');
29261 this.hoverState = null;
29263 if (this.el.hasClass('fade')) {
29278 //this.el.setXY([0,0]);
29279 this.el.removeClass(['show', 'in']);
29295 * @class Roo.bootstrap.LocationPicker
29296 * @extends Roo.bootstrap.Component
29297 * Bootstrap LocationPicker class
29298 * @cfg {Number} latitude Position when init default 0
29299 * @cfg {Number} longitude Position when init default 0
29300 * @cfg {Number} zoom default 15
29301 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29302 * @cfg {Boolean} mapTypeControl default false
29303 * @cfg {Boolean} disableDoubleClickZoom default false
29304 * @cfg {Boolean} scrollwheel default true
29305 * @cfg {Boolean} streetViewControl default false
29306 * @cfg {Number} radius default 0
29307 * @cfg {String} locationName
29308 * @cfg {Boolean} draggable default true
29309 * @cfg {Boolean} enableAutocomplete default false
29310 * @cfg {Boolean} enableReverseGeocode default true
29311 * @cfg {String} markerTitle
29314 * Create a new LocationPicker
29315 * @param {Object} config The config object
29319 Roo.bootstrap.LocationPicker = function(config){
29321 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29326 * Fires when the picker initialized.
29327 * @param {Roo.bootstrap.LocationPicker} this
29328 * @param {Google Location} location
29332 * @event positionchanged
29333 * Fires when the picker position changed.
29334 * @param {Roo.bootstrap.LocationPicker} this
29335 * @param {Google Location} location
29337 positionchanged : true,
29340 * Fires when the map resize.
29341 * @param {Roo.bootstrap.LocationPicker} this
29346 * Fires when the map show.
29347 * @param {Roo.bootstrap.LocationPicker} this
29352 * Fires when the map hide.
29353 * @param {Roo.bootstrap.LocationPicker} this
29358 * Fires when click the map.
29359 * @param {Roo.bootstrap.LocationPicker} this
29360 * @param {Map event} e
29364 * @event mapRightClick
29365 * Fires when right click the map.
29366 * @param {Roo.bootstrap.LocationPicker} this
29367 * @param {Map event} e
29369 mapRightClick : true,
29371 * @event markerClick
29372 * Fires when click the marker.
29373 * @param {Roo.bootstrap.LocationPicker} this
29374 * @param {Map event} e
29376 markerClick : true,
29378 * @event markerRightClick
29379 * Fires when right click the marker.
29380 * @param {Roo.bootstrap.LocationPicker} this
29381 * @param {Map event} e
29383 markerRightClick : true,
29385 * @event OverlayViewDraw
29386 * Fires when OverlayView Draw
29387 * @param {Roo.bootstrap.LocationPicker} this
29389 OverlayViewDraw : true,
29391 * @event OverlayViewOnAdd
29392 * Fires when OverlayView Draw
29393 * @param {Roo.bootstrap.LocationPicker} this
29395 OverlayViewOnAdd : true,
29397 * @event OverlayViewOnRemove
29398 * Fires when OverlayView Draw
29399 * @param {Roo.bootstrap.LocationPicker} this
29401 OverlayViewOnRemove : true,
29403 * @event OverlayViewShow
29404 * Fires when OverlayView Draw
29405 * @param {Roo.bootstrap.LocationPicker} this
29406 * @param {Pixel} cpx
29408 OverlayViewShow : true,
29410 * @event OverlayViewHide
29411 * Fires when OverlayView Draw
29412 * @param {Roo.bootstrap.LocationPicker} this
29414 OverlayViewHide : true,
29416 * @event loadexception
29417 * Fires when load google lib failed.
29418 * @param {Roo.bootstrap.LocationPicker} this
29420 loadexception : true
29425 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29427 gMapContext: false,
29433 mapTypeControl: false,
29434 disableDoubleClickZoom: false,
29436 streetViewControl: false,
29440 enableAutocomplete: false,
29441 enableReverseGeocode: true,
29444 getAutoCreate: function()
29449 cls: 'roo-location-picker'
29455 initEvents: function(ct, position)
29457 if(!this.el.getWidth() || this.isApplied()){
29461 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29466 initial: function()
29468 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29469 this.fireEvent('loadexception', this);
29473 if(!this.mapTypeId){
29474 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29477 this.gMapContext = this.GMapContext();
29479 this.initOverlayView();
29481 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29485 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29486 _this.setPosition(_this.gMapContext.marker.position);
29489 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29490 _this.fireEvent('mapClick', this, event);
29494 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29495 _this.fireEvent('mapRightClick', this, event);
29499 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29500 _this.fireEvent('markerClick', this, event);
29504 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29505 _this.fireEvent('markerRightClick', this, event);
29509 this.setPosition(this.gMapContext.location);
29511 this.fireEvent('initial', this, this.gMapContext.location);
29514 initOverlayView: function()
29518 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29522 _this.fireEvent('OverlayViewDraw', _this);
29527 _this.fireEvent('OverlayViewOnAdd', _this);
29530 onRemove: function()
29532 _this.fireEvent('OverlayViewOnRemove', _this);
29535 show: function(cpx)
29537 _this.fireEvent('OverlayViewShow', _this, cpx);
29542 _this.fireEvent('OverlayViewHide', _this);
29548 fromLatLngToContainerPixel: function(event)
29550 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29553 isApplied: function()
29555 return this.getGmapContext() == false ? false : true;
29558 getGmapContext: function()
29560 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29563 GMapContext: function()
29565 var position = new google.maps.LatLng(this.latitude, this.longitude);
29567 var _map = new google.maps.Map(this.el.dom, {
29570 mapTypeId: this.mapTypeId,
29571 mapTypeControl: this.mapTypeControl,
29572 disableDoubleClickZoom: this.disableDoubleClickZoom,
29573 scrollwheel: this.scrollwheel,
29574 streetViewControl: this.streetViewControl,
29575 locationName: this.locationName,
29576 draggable: this.draggable,
29577 enableAutocomplete: this.enableAutocomplete,
29578 enableReverseGeocode: this.enableReverseGeocode
29581 var _marker = new google.maps.Marker({
29582 position: position,
29584 title: this.markerTitle,
29585 draggable: this.draggable
29592 location: position,
29593 radius: this.radius,
29594 locationName: this.locationName,
29595 addressComponents: {
29596 formatted_address: null,
29597 addressLine1: null,
29598 addressLine2: null,
29600 streetNumber: null,
29604 stateOrProvince: null
29607 domContainer: this.el.dom,
29608 geodecoder: new google.maps.Geocoder()
29612 drawCircle: function(center, radius, options)
29614 if (this.gMapContext.circle != null) {
29615 this.gMapContext.circle.setMap(null);
29619 options = Roo.apply({}, options, {
29620 strokeColor: "#0000FF",
29621 strokeOpacity: .35,
29623 fillColor: "#0000FF",
29627 options.map = this.gMapContext.map;
29628 options.radius = radius;
29629 options.center = center;
29630 this.gMapContext.circle = new google.maps.Circle(options);
29631 return this.gMapContext.circle;
29637 setPosition: function(location)
29639 this.gMapContext.location = location;
29640 this.gMapContext.marker.setPosition(location);
29641 this.gMapContext.map.panTo(location);
29642 this.drawCircle(location, this.gMapContext.radius, {});
29646 if (this.gMapContext.settings.enableReverseGeocode) {
29647 this.gMapContext.geodecoder.geocode({
29648 latLng: this.gMapContext.location
29649 }, function(results, status) {
29651 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29652 _this.gMapContext.locationName = results[0].formatted_address;
29653 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29655 _this.fireEvent('positionchanged', this, location);
29662 this.fireEvent('positionchanged', this, location);
29667 google.maps.event.trigger(this.gMapContext.map, "resize");
29669 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29671 this.fireEvent('resize', this);
29674 setPositionByLatLng: function(latitude, longitude)
29676 this.setPosition(new google.maps.LatLng(latitude, longitude));
29679 getCurrentPosition: function()
29682 latitude: this.gMapContext.location.lat(),
29683 longitude: this.gMapContext.location.lng()
29687 getAddressName: function()
29689 return this.gMapContext.locationName;
29692 getAddressComponents: function()
29694 return this.gMapContext.addressComponents;
29697 address_component_from_google_geocode: function(address_components)
29701 for (var i = 0; i < address_components.length; i++) {
29702 var component = address_components[i];
29703 if (component.types.indexOf("postal_code") >= 0) {
29704 result.postalCode = component.short_name;
29705 } else if (component.types.indexOf("street_number") >= 0) {
29706 result.streetNumber = component.short_name;
29707 } else if (component.types.indexOf("route") >= 0) {
29708 result.streetName = component.short_name;
29709 } else if (component.types.indexOf("neighborhood") >= 0) {
29710 result.city = component.short_name;
29711 } else if (component.types.indexOf("locality") >= 0) {
29712 result.city = component.short_name;
29713 } else if (component.types.indexOf("sublocality") >= 0) {
29714 result.district = component.short_name;
29715 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29716 result.stateOrProvince = component.short_name;
29717 } else if (component.types.indexOf("country") >= 0) {
29718 result.country = component.short_name;
29722 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29723 result.addressLine2 = "";
29727 setZoomLevel: function(zoom)
29729 this.gMapContext.map.setZoom(zoom);
29742 this.fireEvent('show', this);
29753 this.fireEvent('hide', this);
29758 Roo.apply(Roo.bootstrap.LocationPicker, {
29760 OverlayView : function(map, options)
29762 options = options || {};
29769 * @class Roo.bootstrap.Alert
29770 * @extends Roo.bootstrap.Component
29771 * Bootstrap Alert class - shows an alert area box
29773 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29774 Enter a valid email address
29777 * @cfg {String} title The title of alert
29778 * @cfg {String} html The content of alert
29779 * @cfg {String} weight ( success | info | warning | danger )
29780 * @cfg {String} faicon font-awesomeicon
29783 * Create a new alert
29784 * @param {Object} config The config object
29788 Roo.bootstrap.Alert = function(config){
29789 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29793 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29800 getAutoCreate : function()
29809 cls : 'roo-alert-icon'
29814 cls : 'roo-alert-title',
29819 cls : 'roo-alert-text',
29826 cfg.cn[0].cls += ' fa ' + this.faicon;
29830 cfg.cls += ' alert-' + this.weight;
29836 initEvents: function()
29838 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29841 setTitle : function(str)
29843 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29846 setText : function(str)
29848 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29851 setWeight : function(weight)
29854 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29857 this.weight = weight;
29859 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29862 setIcon : function(icon)
29865 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29868 this.faicon = icon;
29870 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29891 * @class Roo.bootstrap.UploadCropbox
29892 * @extends Roo.bootstrap.Component
29893 * Bootstrap UploadCropbox class
29894 * @cfg {String} emptyText show when image has been loaded
29895 * @cfg {String} rotateNotify show when image too small to rotate
29896 * @cfg {Number} errorTimeout default 3000
29897 * @cfg {Number} minWidth default 300
29898 * @cfg {Number} minHeight default 300
29899 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29900 * @cfg {Boolean} isDocument (true|false) default false
29901 * @cfg {String} url action url
29902 * @cfg {String} paramName default 'imageUpload'
29903 * @cfg {String} method default POST
29904 * @cfg {Boolean} loadMask (true|false) default true
29905 * @cfg {Boolean} loadingText default 'Loading...'
29908 * Create a new UploadCropbox
29909 * @param {Object} config The config object
29912 Roo.bootstrap.UploadCropbox = function(config){
29913 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29917 * @event beforeselectfile
29918 * Fire before select file
29919 * @param {Roo.bootstrap.UploadCropbox} this
29921 "beforeselectfile" : true,
29924 * Fire after initEvent
29925 * @param {Roo.bootstrap.UploadCropbox} this
29930 * Fire after initEvent
29931 * @param {Roo.bootstrap.UploadCropbox} this
29932 * @param {String} data
29937 * Fire when preparing the file data
29938 * @param {Roo.bootstrap.UploadCropbox} this
29939 * @param {Object} file
29944 * Fire when get exception
29945 * @param {Roo.bootstrap.UploadCropbox} this
29946 * @param {XMLHttpRequest} xhr
29948 "exception" : true,
29950 * @event beforeloadcanvas
29951 * Fire before load the canvas
29952 * @param {Roo.bootstrap.UploadCropbox} this
29953 * @param {String} src
29955 "beforeloadcanvas" : true,
29958 * Fire when trash image
29959 * @param {Roo.bootstrap.UploadCropbox} this
29964 * Fire when download the image
29965 * @param {Roo.bootstrap.UploadCropbox} this
29969 * @event footerbuttonclick
29970 * Fire when footerbuttonclick
29971 * @param {Roo.bootstrap.UploadCropbox} this
29972 * @param {String} type
29974 "footerbuttonclick" : true,
29978 * @param {Roo.bootstrap.UploadCropbox} this
29983 * Fire when rotate the image
29984 * @param {Roo.bootstrap.UploadCropbox} this
29985 * @param {String} pos
29990 * Fire when inspect the file
29991 * @param {Roo.bootstrap.UploadCropbox} this
29992 * @param {Object} file
29997 * Fire when xhr upload the file
29998 * @param {Roo.bootstrap.UploadCropbox} this
29999 * @param {Object} data
30004 * Fire when arrange the file data
30005 * @param {Roo.bootstrap.UploadCropbox} this
30006 * @param {Object} formData
30011 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30014 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30016 emptyText : 'Click to upload image',
30017 rotateNotify : 'Image is too small to rotate',
30018 errorTimeout : 3000,
30032 cropType : 'image/jpeg',
30034 canvasLoaded : false,
30035 isDocument : false,
30037 paramName : 'imageUpload',
30039 loadingText : 'Loading...',
30042 getAutoCreate : function()
30046 cls : 'roo-upload-cropbox',
30050 cls : 'roo-upload-cropbox-selector',
30055 cls : 'roo-upload-cropbox-body',
30056 style : 'cursor:pointer',
30060 cls : 'roo-upload-cropbox-preview'
30064 cls : 'roo-upload-cropbox-thumb'
30068 cls : 'roo-upload-cropbox-empty-notify',
30069 html : this.emptyText
30073 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30074 html : this.rotateNotify
30080 cls : 'roo-upload-cropbox-footer',
30083 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30093 onRender : function(ct, position)
30095 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30097 if (this.buttons.length) {
30099 Roo.each(this.buttons, function(bb) {
30101 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30103 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30109 this.maskEl = this.el;
30113 initEvents : function()
30115 this.urlAPI = (window.createObjectURL && window) ||
30116 (window.URL && URL.revokeObjectURL && URL) ||
30117 (window.webkitURL && webkitURL);
30119 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30120 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30122 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30123 this.selectorEl.hide();
30125 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30126 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30128 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30129 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30130 this.thumbEl.hide();
30132 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30133 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30135 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30136 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30137 this.errorEl.hide();
30139 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30140 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30141 this.footerEl.hide();
30143 this.setThumbBoxSize();
30149 this.fireEvent('initial', this);
30156 window.addEventListener("resize", function() { _this.resize(); } );
30158 this.bodyEl.on('click', this.beforeSelectFile, this);
30161 this.bodyEl.on('touchstart', this.onTouchStart, this);
30162 this.bodyEl.on('touchmove', this.onTouchMove, this);
30163 this.bodyEl.on('touchend', this.onTouchEnd, this);
30167 this.bodyEl.on('mousedown', this.onMouseDown, this);
30168 this.bodyEl.on('mousemove', this.onMouseMove, this);
30169 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30170 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30171 Roo.get(document).on('mouseup', this.onMouseUp, this);
30174 this.selectorEl.on('change', this.onFileSelected, this);
30180 this.baseScale = 1;
30182 this.baseRotate = 1;
30183 this.dragable = false;
30184 this.pinching = false;
30187 this.cropData = false;
30188 this.notifyEl.dom.innerHTML = this.emptyText;
30190 this.selectorEl.dom.value = '';
30194 resize : function()
30196 if(this.fireEvent('resize', this) != false){
30197 this.setThumbBoxPosition();
30198 this.setCanvasPosition();
30202 onFooterButtonClick : function(e, el, o, type)
30205 case 'rotate-left' :
30206 this.onRotateLeft(e);
30208 case 'rotate-right' :
30209 this.onRotateRight(e);
30212 this.beforeSelectFile(e);
30227 this.fireEvent('footerbuttonclick', this, type);
30230 beforeSelectFile : function(e)
30232 e.preventDefault();
30234 if(this.fireEvent('beforeselectfile', this) != false){
30235 this.selectorEl.dom.click();
30239 onFileSelected : function(e)
30241 e.preventDefault();
30243 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30247 var file = this.selectorEl.dom.files[0];
30249 if(this.fireEvent('inspect', this, file) != false){
30250 this.prepare(file);
30255 trash : function(e)
30257 this.fireEvent('trash', this);
30260 download : function(e)
30262 this.fireEvent('download', this);
30265 loadCanvas : function(src)
30267 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30271 this.imageEl = document.createElement('img');
30275 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30277 this.imageEl.src = src;
30281 onLoadCanvas : function()
30283 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30284 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30286 this.bodyEl.un('click', this.beforeSelectFile, this);
30288 this.notifyEl.hide();
30289 this.thumbEl.show();
30290 this.footerEl.show();
30292 this.baseRotateLevel();
30294 if(this.isDocument){
30295 this.setThumbBoxSize();
30298 this.setThumbBoxPosition();
30300 this.baseScaleLevel();
30306 this.canvasLoaded = true;
30309 this.maskEl.unmask();
30314 setCanvasPosition : function()
30316 if(!this.canvasEl){
30320 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30321 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30323 this.previewEl.setLeft(pw);
30324 this.previewEl.setTop(ph);
30328 onMouseDown : function(e)
30332 this.dragable = true;
30333 this.pinching = false;
30335 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30336 this.dragable = false;
30340 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30341 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30345 onMouseMove : function(e)
30349 if(!this.canvasLoaded){
30353 if (!this.dragable){
30357 var minX = Math.ceil(this.thumbEl.getLeft(true));
30358 var minY = Math.ceil(this.thumbEl.getTop(true));
30360 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30361 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30363 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30364 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30366 x = x - this.mouseX;
30367 y = y - this.mouseY;
30369 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30370 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30372 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30373 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30375 this.previewEl.setLeft(bgX);
30376 this.previewEl.setTop(bgY);
30378 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30379 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30382 onMouseUp : function(e)
30386 this.dragable = false;
30389 onMouseWheel : function(e)
30393 this.startScale = this.scale;
30395 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30397 if(!this.zoomable()){
30398 this.scale = this.startScale;
30407 zoomable : function()
30409 var minScale = this.thumbEl.getWidth() / this.minWidth;
30411 if(this.minWidth < this.minHeight){
30412 minScale = this.thumbEl.getHeight() / this.minHeight;
30415 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30416 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30420 (this.rotate == 0 || this.rotate == 180) &&
30422 width > this.imageEl.OriginWidth ||
30423 height > this.imageEl.OriginHeight ||
30424 (width < this.minWidth && height < this.minHeight)
30432 (this.rotate == 90 || this.rotate == 270) &&
30434 width > this.imageEl.OriginWidth ||
30435 height > this.imageEl.OriginHeight ||
30436 (width < this.minHeight && height < this.minWidth)
30443 !this.isDocument &&
30444 (this.rotate == 0 || this.rotate == 180) &&
30446 width < this.minWidth ||
30447 width > this.imageEl.OriginWidth ||
30448 height < this.minHeight ||
30449 height > this.imageEl.OriginHeight
30456 !this.isDocument &&
30457 (this.rotate == 90 || this.rotate == 270) &&
30459 width < this.minHeight ||
30460 width > this.imageEl.OriginWidth ||
30461 height < this.minWidth ||
30462 height > this.imageEl.OriginHeight
30472 onRotateLeft : function(e)
30474 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30476 var minScale = this.thumbEl.getWidth() / this.minWidth;
30478 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30479 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30481 this.startScale = this.scale;
30483 while (this.getScaleLevel() < minScale){
30485 this.scale = this.scale + 1;
30487 if(!this.zoomable()){
30492 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30493 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30498 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30505 this.scale = this.startScale;
30507 this.onRotateFail();
30512 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30514 if(this.isDocument){
30515 this.setThumbBoxSize();
30516 this.setThumbBoxPosition();
30517 this.setCanvasPosition();
30522 this.fireEvent('rotate', this, 'left');
30526 onRotateRight : function(e)
30528 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30530 var minScale = this.thumbEl.getWidth() / this.minWidth;
30532 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30533 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30535 this.startScale = this.scale;
30537 while (this.getScaleLevel() < minScale){
30539 this.scale = this.scale + 1;
30541 if(!this.zoomable()){
30546 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30547 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30552 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30559 this.scale = this.startScale;
30561 this.onRotateFail();
30566 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30568 if(this.isDocument){
30569 this.setThumbBoxSize();
30570 this.setThumbBoxPosition();
30571 this.setCanvasPosition();
30576 this.fireEvent('rotate', this, 'right');
30579 onRotateFail : function()
30581 this.errorEl.show(true);
30585 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30590 this.previewEl.dom.innerHTML = '';
30592 var canvasEl = document.createElement("canvas");
30594 var contextEl = canvasEl.getContext("2d");
30596 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30597 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30598 var center = this.imageEl.OriginWidth / 2;
30600 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30601 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30602 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30603 center = this.imageEl.OriginHeight / 2;
30606 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30608 contextEl.translate(center, center);
30609 contextEl.rotate(this.rotate * Math.PI / 180);
30611 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30613 this.canvasEl = document.createElement("canvas");
30615 this.contextEl = this.canvasEl.getContext("2d");
30617 switch (this.rotate) {
30620 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30621 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30623 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30628 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30629 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30631 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30632 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);
30636 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30641 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30642 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30644 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30645 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);
30649 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);
30654 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30655 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30657 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30658 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30662 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);
30669 this.previewEl.appendChild(this.canvasEl);
30671 this.setCanvasPosition();
30676 if(!this.canvasLoaded){
30680 var imageCanvas = document.createElement("canvas");
30682 var imageContext = imageCanvas.getContext("2d");
30684 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30685 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30687 var center = imageCanvas.width / 2;
30689 imageContext.translate(center, center);
30691 imageContext.rotate(this.rotate * Math.PI / 180);
30693 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30695 var canvas = document.createElement("canvas");
30697 var context = canvas.getContext("2d");
30699 canvas.width = this.minWidth;
30700 canvas.height = this.minHeight;
30702 switch (this.rotate) {
30705 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30706 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30708 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30709 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30711 var targetWidth = this.minWidth - 2 * x;
30712 var targetHeight = this.minHeight - 2 * y;
30716 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30717 scale = targetWidth / width;
30720 if(x > 0 && y == 0){
30721 scale = targetHeight / height;
30724 if(x > 0 && y > 0){
30725 scale = targetWidth / width;
30727 if(width < height){
30728 scale = targetHeight / height;
30732 context.scale(scale, scale);
30734 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30735 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30737 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30738 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30740 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30745 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30746 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30748 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30749 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30751 var targetWidth = this.minWidth - 2 * x;
30752 var targetHeight = this.minHeight - 2 * y;
30756 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30757 scale = targetWidth / width;
30760 if(x > 0 && y == 0){
30761 scale = targetHeight / height;
30764 if(x > 0 && y > 0){
30765 scale = targetWidth / width;
30767 if(width < height){
30768 scale = targetHeight / height;
30772 context.scale(scale, scale);
30774 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30775 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30777 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30778 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30780 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30782 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30787 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30788 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30790 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30791 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30793 var targetWidth = this.minWidth - 2 * x;
30794 var targetHeight = this.minHeight - 2 * y;
30798 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30799 scale = targetWidth / width;
30802 if(x > 0 && y == 0){
30803 scale = targetHeight / height;
30806 if(x > 0 && y > 0){
30807 scale = targetWidth / width;
30809 if(width < height){
30810 scale = targetHeight / height;
30814 context.scale(scale, scale);
30816 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30817 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30819 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30820 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30822 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30823 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30825 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30830 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30831 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30833 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30834 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30836 var targetWidth = this.minWidth - 2 * x;
30837 var targetHeight = this.minHeight - 2 * y;
30841 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30842 scale = targetWidth / width;
30845 if(x > 0 && y == 0){
30846 scale = targetHeight / height;
30849 if(x > 0 && y > 0){
30850 scale = targetWidth / width;
30852 if(width < height){
30853 scale = targetHeight / height;
30857 context.scale(scale, scale);
30859 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30860 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30862 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30863 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30865 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30867 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30874 this.cropData = canvas.toDataURL(this.cropType);
30876 if(this.fireEvent('crop', this, this.cropData) !== false){
30877 this.process(this.file, this.cropData);
30884 setThumbBoxSize : function()
30888 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30889 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30890 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30892 this.minWidth = width;
30893 this.minHeight = height;
30895 if(this.rotate == 90 || this.rotate == 270){
30896 this.minWidth = height;
30897 this.minHeight = width;
30902 width = Math.ceil(this.minWidth * height / this.minHeight);
30904 if(this.minWidth > this.minHeight){
30906 height = Math.ceil(this.minHeight * width / this.minWidth);
30909 this.thumbEl.setStyle({
30910 width : width + 'px',
30911 height : height + 'px'
30918 setThumbBoxPosition : function()
30920 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30921 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30923 this.thumbEl.setLeft(x);
30924 this.thumbEl.setTop(y);
30928 baseRotateLevel : function()
30930 this.baseRotate = 1;
30933 typeof(this.exif) != 'undefined' &&
30934 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30935 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30937 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30940 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30944 baseScaleLevel : function()
30948 if(this.isDocument){
30950 if(this.baseRotate == 6 || this.baseRotate == 8){
30952 height = this.thumbEl.getHeight();
30953 this.baseScale = height / this.imageEl.OriginWidth;
30955 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30956 width = this.thumbEl.getWidth();
30957 this.baseScale = width / this.imageEl.OriginHeight;
30963 height = this.thumbEl.getHeight();
30964 this.baseScale = height / this.imageEl.OriginHeight;
30966 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30967 width = this.thumbEl.getWidth();
30968 this.baseScale = width / this.imageEl.OriginWidth;
30974 if(this.baseRotate == 6 || this.baseRotate == 8){
30976 width = this.thumbEl.getHeight();
30977 this.baseScale = width / this.imageEl.OriginHeight;
30979 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30980 height = this.thumbEl.getWidth();
30981 this.baseScale = height / this.imageEl.OriginHeight;
30984 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30985 height = this.thumbEl.getWidth();
30986 this.baseScale = height / this.imageEl.OriginHeight;
30988 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30989 width = this.thumbEl.getHeight();
30990 this.baseScale = width / this.imageEl.OriginWidth;
30997 width = this.thumbEl.getWidth();
30998 this.baseScale = width / this.imageEl.OriginWidth;
31000 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31001 height = this.thumbEl.getHeight();
31002 this.baseScale = height / this.imageEl.OriginHeight;
31005 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31007 height = this.thumbEl.getHeight();
31008 this.baseScale = height / this.imageEl.OriginHeight;
31010 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31011 width = this.thumbEl.getWidth();
31012 this.baseScale = width / this.imageEl.OriginWidth;
31020 getScaleLevel : function()
31022 return this.baseScale * Math.pow(1.1, this.scale);
31025 onTouchStart : function(e)
31027 if(!this.canvasLoaded){
31028 this.beforeSelectFile(e);
31032 var touches = e.browserEvent.touches;
31038 if(touches.length == 1){
31039 this.onMouseDown(e);
31043 if(touches.length != 2){
31049 for(var i = 0, finger; finger = touches[i]; i++){
31050 coords.push(finger.pageX, finger.pageY);
31053 var x = Math.pow(coords[0] - coords[2], 2);
31054 var y = Math.pow(coords[1] - coords[3], 2);
31056 this.startDistance = Math.sqrt(x + y);
31058 this.startScale = this.scale;
31060 this.pinching = true;
31061 this.dragable = false;
31065 onTouchMove : function(e)
31067 if(!this.pinching && !this.dragable){
31071 var touches = e.browserEvent.touches;
31078 this.onMouseMove(e);
31084 for(var i = 0, finger; finger = touches[i]; i++){
31085 coords.push(finger.pageX, finger.pageY);
31088 var x = Math.pow(coords[0] - coords[2], 2);
31089 var y = Math.pow(coords[1] - coords[3], 2);
31091 this.endDistance = Math.sqrt(x + y);
31093 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31095 if(!this.zoomable()){
31096 this.scale = this.startScale;
31104 onTouchEnd : function(e)
31106 this.pinching = false;
31107 this.dragable = false;
31111 process : function(file, crop)
31114 this.maskEl.mask(this.loadingText);
31117 this.xhr = new XMLHttpRequest();
31119 file.xhr = this.xhr;
31121 this.xhr.open(this.method, this.url, true);
31124 "Accept": "application/json",
31125 "Cache-Control": "no-cache",
31126 "X-Requested-With": "XMLHttpRequest"
31129 for (var headerName in headers) {
31130 var headerValue = headers[headerName];
31132 this.xhr.setRequestHeader(headerName, headerValue);
31138 this.xhr.onload = function()
31140 _this.xhrOnLoad(_this.xhr);
31143 this.xhr.onerror = function()
31145 _this.xhrOnError(_this.xhr);
31148 var formData = new FormData();
31150 formData.append('returnHTML', 'NO');
31153 formData.append('crop', crop);
31156 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31157 formData.append(this.paramName, file, file.name);
31160 if(typeof(file.filename) != 'undefined'){
31161 formData.append('filename', file.filename);
31164 if(typeof(file.mimetype) != 'undefined'){
31165 formData.append('mimetype', file.mimetype);
31168 if(this.fireEvent('arrange', this, formData) != false){
31169 this.xhr.send(formData);
31173 xhrOnLoad : function(xhr)
31176 this.maskEl.unmask();
31179 if (xhr.readyState !== 4) {
31180 this.fireEvent('exception', this, xhr);
31184 var response = Roo.decode(xhr.responseText);
31186 if(!response.success){
31187 this.fireEvent('exception', this, xhr);
31191 var response = Roo.decode(xhr.responseText);
31193 this.fireEvent('upload', this, response);
31197 xhrOnError : function()
31200 this.maskEl.unmask();
31203 Roo.log('xhr on error');
31205 var response = Roo.decode(xhr.responseText);
31211 prepare : function(file)
31214 this.maskEl.mask(this.loadingText);
31220 if(typeof(file) === 'string'){
31221 this.loadCanvas(file);
31225 if(!file || !this.urlAPI){
31230 this.cropType = file.type;
31234 if(this.fireEvent('prepare', this, this.file) != false){
31236 var reader = new FileReader();
31238 reader.onload = function (e) {
31239 if (e.target.error) {
31240 Roo.log(e.target.error);
31244 var buffer = e.target.result,
31245 dataView = new DataView(buffer),
31247 maxOffset = dataView.byteLength - 4,
31251 if (dataView.getUint16(0) === 0xffd8) {
31252 while (offset < maxOffset) {
31253 markerBytes = dataView.getUint16(offset);
31255 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31256 markerLength = dataView.getUint16(offset + 2) + 2;
31257 if (offset + markerLength > dataView.byteLength) {
31258 Roo.log('Invalid meta data: Invalid segment size.');
31262 if(markerBytes == 0xffe1){
31263 _this.parseExifData(
31270 offset += markerLength;
31280 var url = _this.urlAPI.createObjectURL(_this.file);
31282 _this.loadCanvas(url);
31287 reader.readAsArrayBuffer(this.file);
31293 parseExifData : function(dataView, offset, length)
31295 var tiffOffset = offset + 10,
31299 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31300 // No Exif data, might be XMP data instead
31304 // Check for the ASCII code for "Exif" (0x45786966):
31305 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31306 // No Exif data, might be XMP data instead
31309 if (tiffOffset + 8 > dataView.byteLength) {
31310 Roo.log('Invalid Exif data: Invalid segment size.');
31313 // Check for the two null bytes:
31314 if (dataView.getUint16(offset + 8) !== 0x0000) {
31315 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31318 // Check the byte alignment:
31319 switch (dataView.getUint16(tiffOffset)) {
31321 littleEndian = true;
31324 littleEndian = false;
31327 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31330 // Check for the TIFF tag marker (0x002A):
31331 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31332 Roo.log('Invalid Exif data: Missing TIFF marker.');
31335 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31336 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31338 this.parseExifTags(
31341 tiffOffset + dirOffset,
31346 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31351 if (dirOffset + 6 > dataView.byteLength) {
31352 Roo.log('Invalid Exif data: Invalid directory offset.');
31355 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31356 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31357 if (dirEndOffset + 4 > dataView.byteLength) {
31358 Roo.log('Invalid Exif data: Invalid directory size.');
31361 for (i = 0; i < tagsNumber; i += 1) {
31365 dirOffset + 2 + 12 * i, // tag offset
31369 // Return the offset to the next directory:
31370 return dataView.getUint32(dirEndOffset, littleEndian);
31373 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31375 var tag = dataView.getUint16(offset, littleEndian);
31377 this.exif[tag] = this.getExifValue(
31381 dataView.getUint16(offset + 2, littleEndian), // tag type
31382 dataView.getUint32(offset + 4, littleEndian), // tag length
31387 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31389 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31398 Roo.log('Invalid Exif data: Invalid tag type.');
31402 tagSize = tagType.size * length;
31403 // Determine if the value is contained in the dataOffset bytes,
31404 // or if the value at the dataOffset is a pointer to the actual data:
31405 dataOffset = tagSize > 4 ?
31406 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31407 if (dataOffset + tagSize > dataView.byteLength) {
31408 Roo.log('Invalid Exif data: Invalid data offset.');
31411 if (length === 1) {
31412 return tagType.getValue(dataView, dataOffset, littleEndian);
31415 for (i = 0; i < length; i += 1) {
31416 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31419 if (tagType.ascii) {
31421 // Concatenate the chars:
31422 for (i = 0; i < values.length; i += 1) {
31424 // Ignore the terminating NULL byte(s):
31425 if (c === '\u0000') {
31437 Roo.apply(Roo.bootstrap.UploadCropbox, {
31439 'Orientation': 0x0112
31443 1: 0, //'top-left',
31445 3: 180, //'bottom-right',
31446 // 4: 'bottom-left',
31448 6: 90, //'right-top',
31449 // 7: 'right-bottom',
31450 8: 270 //'left-bottom'
31454 // byte, 8-bit unsigned int:
31456 getValue: function (dataView, dataOffset) {
31457 return dataView.getUint8(dataOffset);
31461 // ascii, 8-bit byte:
31463 getValue: function (dataView, dataOffset) {
31464 return String.fromCharCode(dataView.getUint8(dataOffset));
31469 // short, 16 bit int:
31471 getValue: function (dataView, dataOffset, littleEndian) {
31472 return dataView.getUint16(dataOffset, littleEndian);
31476 // long, 32 bit int:
31478 getValue: function (dataView, dataOffset, littleEndian) {
31479 return dataView.getUint32(dataOffset, littleEndian);
31483 // rational = two long values, first is numerator, second is denominator:
31485 getValue: function (dataView, dataOffset, littleEndian) {
31486 return dataView.getUint32(dataOffset, littleEndian) /
31487 dataView.getUint32(dataOffset + 4, littleEndian);
31491 // slong, 32 bit signed int:
31493 getValue: function (dataView, dataOffset, littleEndian) {
31494 return dataView.getInt32(dataOffset, littleEndian);
31498 // srational, two slongs, first is numerator, second is denominator:
31500 getValue: function (dataView, dataOffset, littleEndian) {
31501 return dataView.getInt32(dataOffset, littleEndian) /
31502 dataView.getInt32(dataOffset + 4, littleEndian);
31512 cls : 'btn-group roo-upload-cropbox-rotate-left',
31513 action : 'rotate-left',
31517 cls : 'btn btn-default',
31518 html : '<i class="fa fa-undo"></i>'
31524 cls : 'btn-group roo-upload-cropbox-picture',
31525 action : 'picture',
31529 cls : 'btn btn-default',
31530 html : '<i class="fa fa-picture-o"></i>'
31536 cls : 'btn-group roo-upload-cropbox-rotate-right',
31537 action : 'rotate-right',
31541 cls : 'btn btn-default',
31542 html : '<i class="fa fa-repeat"></i>'
31550 cls : 'btn-group roo-upload-cropbox-rotate-left',
31551 action : 'rotate-left',
31555 cls : 'btn btn-default',
31556 html : '<i class="fa fa-undo"></i>'
31562 cls : 'btn-group roo-upload-cropbox-download',
31563 action : 'download',
31567 cls : 'btn btn-default',
31568 html : '<i class="fa fa-download"></i>'
31574 cls : 'btn-group roo-upload-cropbox-crop',
31579 cls : 'btn btn-default',
31580 html : '<i class="fa fa-crop"></i>'
31586 cls : 'btn-group roo-upload-cropbox-trash',
31591 cls : 'btn btn-default',
31592 html : '<i class="fa fa-trash"></i>'
31598 cls : 'btn-group roo-upload-cropbox-rotate-right',
31599 action : 'rotate-right',
31603 cls : 'btn btn-default',
31604 html : '<i class="fa fa-repeat"></i>'
31612 cls : 'btn-group roo-upload-cropbox-rotate-left',
31613 action : 'rotate-left',
31617 cls : 'btn btn-default',
31618 html : '<i class="fa fa-undo"></i>'
31624 cls : 'btn-group roo-upload-cropbox-rotate-right',
31625 action : 'rotate-right',
31629 cls : 'btn btn-default',
31630 html : '<i class="fa fa-repeat"></i>'
31643 * @class Roo.bootstrap.DocumentManager
31644 * @extends Roo.bootstrap.Component
31645 * Bootstrap DocumentManager class
31646 * @cfg {String} paramName default 'imageUpload'
31647 * @cfg {String} toolTipName default 'filename'
31648 * @cfg {String} method default POST
31649 * @cfg {String} url action url
31650 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31651 * @cfg {Boolean} multiple multiple upload default true
31652 * @cfg {Number} thumbSize default 300
31653 * @cfg {String} fieldLabel
31654 * @cfg {Number} labelWidth default 4
31655 * @cfg {String} labelAlign (left|top) default left
31656 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31657 * @cfg {Number} labellg set the width of label (1-12)
31658 * @cfg {Number} labelmd set the width of label (1-12)
31659 * @cfg {Number} labelsm set the width of label (1-12)
31660 * @cfg {Number} labelxs set the width of label (1-12)
31663 * Create a new DocumentManager
31664 * @param {Object} config The config object
31667 Roo.bootstrap.DocumentManager = function(config){
31668 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31671 this.delegates = [];
31676 * Fire when initial the DocumentManager
31677 * @param {Roo.bootstrap.DocumentManager} this
31682 * inspect selected file
31683 * @param {Roo.bootstrap.DocumentManager} this
31684 * @param {File} file
31689 * Fire when xhr load exception
31690 * @param {Roo.bootstrap.DocumentManager} this
31691 * @param {XMLHttpRequest} xhr
31693 "exception" : true,
31695 * @event afterupload
31696 * Fire when xhr load exception
31697 * @param {Roo.bootstrap.DocumentManager} this
31698 * @param {XMLHttpRequest} xhr
31700 "afterupload" : true,
31703 * prepare the form data
31704 * @param {Roo.bootstrap.DocumentManager} this
31705 * @param {Object} formData
31710 * Fire when remove the file
31711 * @param {Roo.bootstrap.DocumentManager} this
31712 * @param {Object} file
31717 * Fire after refresh the file
31718 * @param {Roo.bootstrap.DocumentManager} this
31723 * Fire after click the image
31724 * @param {Roo.bootstrap.DocumentManager} this
31725 * @param {Object} file
31730 * Fire when upload a image and editable set to true
31731 * @param {Roo.bootstrap.DocumentManager} this
31732 * @param {Object} file
31736 * @event beforeselectfile
31737 * Fire before select file
31738 * @param {Roo.bootstrap.DocumentManager} this
31740 "beforeselectfile" : true,
31743 * Fire before process file
31744 * @param {Roo.bootstrap.DocumentManager} this
31745 * @param {Object} file
31749 * @event previewrendered
31750 * Fire when preview rendered
31751 * @param {Roo.bootstrap.DocumentManager} this
31752 * @param {Object} file
31754 "previewrendered" : true,
31757 "previewResize" : true
31762 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31771 paramName : 'imageUpload',
31772 toolTipName : 'filename',
31775 labelAlign : 'left',
31785 getAutoCreate : function()
31787 var managerWidget = {
31789 cls : 'roo-document-manager',
31793 cls : 'roo-document-manager-selector',
31798 cls : 'roo-document-manager-uploader',
31802 cls : 'roo-document-manager-upload-btn',
31803 html : '<i class="fa fa-plus"></i>'
31814 cls : 'column col-md-12',
31819 if(this.fieldLabel.length){
31824 cls : 'column col-md-12',
31825 html : this.fieldLabel
31829 cls : 'column col-md-12',
31834 if(this.labelAlign == 'left'){
31839 html : this.fieldLabel
31848 if(this.labelWidth > 12){
31849 content[0].style = "width: " + this.labelWidth + 'px';
31852 if(this.labelWidth < 13 && this.labelmd == 0){
31853 this.labelmd = this.labelWidth;
31856 if(this.labellg > 0){
31857 content[0].cls += ' col-lg-' + this.labellg;
31858 content[1].cls += ' col-lg-' + (12 - this.labellg);
31861 if(this.labelmd > 0){
31862 content[0].cls += ' col-md-' + this.labelmd;
31863 content[1].cls += ' col-md-' + (12 - this.labelmd);
31866 if(this.labelsm > 0){
31867 content[0].cls += ' col-sm-' + this.labelsm;
31868 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31871 if(this.labelxs > 0){
31872 content[0].cls += ' col-xs-' + this.labelxs;
31873 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31881 cls : 'row clearfix',
31889 initEvents : function()
31891 this.managerEl = this.el.select('.roo-document-manager', true).first();
31892 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31894 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31895 this.selectorEl.hide();
31898 this.selectorEl.attr('multiple', 'multiple');
31901 this.selectorEl.on('change', this.onFileSelected, this);
31903 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31904 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31906 this.uploader.on('click', this.onUploaderClick, this);
31908 this.renderProgressDialog();
31912 window.addEventListener("resize", function() { _this.refresh(); } );
31914 this.fireEvent('initial', this);
31917 renderProgressDialog : function()
31921 this.progressDialog = new Roo.bootstrap.Modal({
31922 cls : 'roo-document-manager-progress-dialog',
31923 allow_close : false,
31934 btnclick : function() {
31935 _this.uploadCancel();
31941 this.progressDialog.render(Roo.get(document.body));
31943 this.progress = new Roo.bootstrap.Progress({
31944 cls : 'roo-document-manager-progress',
31949 this.progress.render(this.progressDialog.getChildContainer());
31951 this.progressBar = new Roo.bootstrap.ProgressBar({
31952 cls : 'roo-document-manager-progress-bar',
31955 aria_valuemax : 12,
31959 this.progressBar.render(this.progress.getChildContainer());
31962 onUploaderClick : function(e)
31964 e.preventDefault();
31966 if(this.fireEvent('beforeselectfile', this) != false){
31967 this.selectorEl.dom.click();
31972 onFileSelected : function(e)
31974 e.preventDefault();
31976 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31980 Roo.each(this.selectorEl.dom.files, function(file){
31981 if(this.fireEvent('inspect', this, file) != false){
31982 this.files.push(file);
31992 this.selectorEl.dom.value = '';
31994 if(!this.files || !this.files.length){
31998 if(this.boxes > 0 && this.files.length > this.boxes){
31999 this.files = this.files.slice(0, this.boxes);
32002 this.uploader.show();
32004 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32005 this.uploader.hide();
32014 Roo.each(this.files, function(file){
32016 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32017 var f = this.renderPreview(file);
32022 if(file.type.indexOf('image') != -1){
32023 this.delegates.push(
32025 _this.process(file);
32026 }).createDelegate(this)
32034 _this.process(file);
32035 }).createDelegate(this)
32040 this.files = files;
32042 this.delegates = this.delegates.concat(docs);
32044 if(!this.delegates.length){
32049 this.progressBar.aria_valuemax = this.delegates.length;
32056 arrange : function()
32058 if(!this.delegates.length){
32059 this.progressDialog.hide();
32064 var delegate = this.delegates.shift();
32066 this.progressDialog.show();
32068 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32070 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32075 refresh : function()
32077 this.uploader.show();
32079 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32080 this.uploader.hide();
32083 Roo.isTouch ? this.closable(false) : this.closable(true);
32085 this.fireEvent('refresh', this);
32088 onRemove : function(e, el, o)
32090 e.preventDefault();
32092 this.fireEvent('remove', this, o);
32096 remove : function(o)
32100 Roo.each(this.files, function(file){
32101 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32110 this.files = files;
32117 Roo.each(this.files, function(file){
32122 file.target.remove();
32131 onClick : function(e, el, o)
32133 e.preventDefault();
32135 this.fireEvent('click', this, o);
32139 closable : function(closable)
32141 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32143 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32155 xhrOnLoad : function(xhr)
32157 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32161 if (xhr.readyState !== 4) {
32163 this.fireEvent('exception', this, xhr);
32167 var response = Roo.decode(xhr.responseText);
32169 if(!response.success){
32171 this.fireEvent('exception', this, xhr);
32175 var file = this.renderPreview(response.data);
32177 this.files.push(file);
32181 this.fireEvent('afterupload', this, xhr);
32185 xhrOnError : function(xhr)
32187 Roo.log('xhr on error');
32189 var response = Roo.decode(xhr.responseText);
32196 process : function(file)
32198 if(this.fireEvent('process', this, file) !== false){
32199 if(this.editable && file.type.indexOf('image') != -1){
32200 this.fireEvent('edit', this, file);
32204 this.uploadStart(file, false);
32211 uploadStart : function(file, crop)
32213 this.xhr = new XMLHttpRequest();
32215 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32220 file.xhr = this.xhr;
32222 this.managerEl.createChild({
32224 cls : 'roo-document-manager-loading',
32228 tooltip : file.name,
32229 cls : 'roo-document-manager-thumb',
32230 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32236 this.xhr.open(this.method, this.url, true);
32239 "Accept": "application/json",
32240 "Cache-Control": "no-cache",
32241 "X-Requested-With": "XMLHttpRequest"
32244 for (var headerName in headers) {
32245 var headerValue = headers[headerName];
32247 this.xhr.setRequestHeader(headerName, headerValue);
32253 this.xhr.onload = function()
32255 _this.xhrOnLoad(_this.xhr);
32258 this.xhr.onerror = function()
32260 _this.xhrOnError(_this.xhr);
32263 var formData = new FormData();
32265 formData.append('returnHTML', 'NO');
32268 formData.append('crop', crop);
32271 formData.append(this.paramName, file, file.name);
32278 if(this.fireEvent('prepare', this, formData, options) != false){
32280 if(options.manually){
32284 this.xhr.send(formData);
32288 this.uploadCancel();
32291 uploadCancel : function()
32297 this.delegates = [];
32299 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32306 renderPreview : function(file)
32308 if(typeof(file.target) != 'undefined' && file.target){
32312 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32314 var previewEl = this.managerEl.createChild({
32316 cls : 'roo-document-manager-preview',
32320 tooltip : file[this.toolTipName],
32321 cls : 'roo-document-manager-thumb',
32322 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32327 html : '<i class="fa fa-times-circle"></i>'
32332 var close = previewEl.select('button.close', true).first();
32334 close.on('click', this.onRemove, this, file);
32336 file.target = previewEl;
32338 var image = previewEl.select('img', true).first();
32342 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32344 image.on('click', this.onClick, this, file);
32346 this.fireEvent('previewrendered', this, file);
32352 onPreviewLoad : function(file, image)
32354 if(typeof(file.target) == 'undefined' || !file.target){
32358 var width = image.dom.naturalWidth || image.dom.width;
32359 var height = image.dom.naturalHeight || image.dom.height;
32361 if(!this.previewResize) {
32365 if(width > height){
32366 file.target.addClass('wide');
32370 file.target.addClass('tall');
32375 uploadFromSource : function(file, crop)
32377 this.xhr = new XMLHttpRequest();
32379 this.managerEl.createChild({
32381 cls : 'roo-document-manager-loading',
32385 tooltip : file.name,
32386 cls : 'roo-document-manager-thumb',
32387 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32393 this.xhr.open(this.method, this.url, true);
32396 "Accept": "application/json",
32397 "Cache-Control": "no-cache",
32398 "X-Requested-With": "XMLHttpRequest"
32401 for (var headerName in headers) {
32402 var headerValue = headers[headerName];
32404 this.xhr.setRequestHeader(headerName, headerValue);
32410 this.xhr.onload = function()
32412 _this.xhrOnLoad(_this.xhr);
32415 this.xhr.onerror = function()
32417 _this.xhrOnError(_this.xhr);
32420 var formData = new FormData();
32422 formData.append('returnHTML', 'NO');
32424 formData.append('crop', crop);
32426 if(typeof(file.filename) != 'undefined'){
32427 formData.append('filename', file.filename);
32430 if(typeof(file.mimetype) != 'undefined'){
32431 formData.append('mimetype', file.mimetype);
32436 if(this.fireEvent('prepare', this, formData) != false){
32437 this.xhr.send(formData);
32447 * @class Roo.bootstrap.DocumentViewer
32448 * @extends Roo.bootstrap.Component
32449 * Bootstrap DocumentViewer class
32450 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32451 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32454 * Create a new DocumentViewer
32455 * @param {Object} config The config object
32458 Roo.bootstrap.DocumentViewer = function(config){
32459 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32464 * Fire after initEvent
32465 * @param {Roo.bootstrap.DocumentViewer} this
32471 * @param {Roo.bootstrap.DocumentViewer} this
32476 * Fire after download button
32477 * @param {Roo.bootstrap.DocumentViewer} this
32482 * Fire after trash button
32483 * @param {Roo.bootstrap.DocumentViewer} this
32490 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32492 showDownload : true,
32496 getAutoCreate : function()
32500 cls : 'roo-document-viewer',
32504 cls : 'roo-document-viewer-body',
32508 cls : 'roo-document-viewer-thumb',
32512 cls : 'roo-document-viewer-image'
32520 cls : 'roo-document-viewer-footer',
32523 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32527 cls : 'btn-group roo-document-viewer-download',
32531 cls : 'btn btn-default',
32532 html : '<i class="fa fa-download"></i>'
32538 cls : 'btn-group roo-document-viewer-trash',
32542 cls : 'btn btn-default',
32543 html : '<i class="fa fa-trash"></i>'
32556 initEvents : function()
32558 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32559 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32561 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32562 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32564 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32565 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32567 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32568 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32570 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32571 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32573 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32574 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32576 this.bodyEl.on('click', this.onClick, this);
32577 this.downloadBtn.on('click', this.onDownload, this);
32578 this.trashBtn.on('click', this.onTrash, this);
32580 this.downloadBtn.hide();
32581 this.trashBtn.hide();
32583 if(this.showDownload){
32584 this.downloadBtn.show();
32587 if(this.showTrash){
32588 this.trashBtn.show();
32591 if(!this.showDownload && !this.showTrash) {
32592 this.footerEl.hide();
32597 initial : function()
32599 this.fireEvent('initial', this);
32603 onClick : function(e)
32605 e.preventDefault();
32607 this.fireEvent('click', this);
32610 onDownload : function(e)
32612 e.preventDefault();
32614 this.fireEvent('download', this);
32617 onTrash : function(e)
32619 e.preventDefault();
32621 this.fireEvent('trash', this);
32633 * @class Roo.bootstrap.NavProgressBar
32634 * @extends Roo.bootstrap.Component
32635 * Bootstrap NavProgressBar class
32638 * Create a new nav progress bar
32639 * @param {Object} config The config object
32642 Roo.bootstrap.NavProgressBar = function(config){
32643 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32645 this.bullets = this.bullets || [];
32647 // Roo.bootstrap.NavProgressBar.register(this);
32651 * Fires when the active item changes
32652 * @param {Roo.bootstrap.NavProgressBar} this
32653 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32654 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32661 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32666 getAutoCreate : function()
32668 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32672 cls : 'roo-navigation-bar-group',
32676 cls : 'roo-navigation-top-bar'
32680 cls : 'roo-navigation-bullets-bar',
32684 cls : 'roo-navigation-bar'
32691 cls : 'roo-navigation-bottom-bar'
32701 initEvents: function()
32706 onRender : function(ct, position)
32708 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32710 if(this.bullets.length){
32711 Roo.each(this.bullets, function(b){
32720 addItem : function(cfg)
32722 var item = new Roo.bootstrap.NavProgressItem(cfg);
32724 item.parentId = this.id;
32725 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32728 var top = new Roo.bootstrap.Element({
32730 cls : 'roo-navigation-bar-text'
32733 var bottom = new Roo.bootstrap.Element({
32735 cls : 'roo-navigation-bar-text'
32738 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32739 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32741 var topText = new Roo.bootstrap.Element({
32743 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32746 var bottomText = new Roo.bootstrap.Element({
32748 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32751 topText.onRender(top.el, null);
32752 bottomText.onRender(bottom.el, null);
32755 item.bottomEl = bottom;
32758 this.barItems.push(item);
32763 getActive : function()
32765 var active = false;
32767 Roo.each(this.barItems, function(v){
32769 if (!v.isActive()) {
32781 setActiveItem : function(item)
32785 Roo.each(this.barItems, function(v){
32786 if (v.rid == item.rid) {
32790 if (v.isActive()) {
32791 v.setActive(false);
32796 item.setActive(true);
32798 this.fireEvent('changed', this, item, prev);
32801 getBarItem: function(rid)
32805 Roo.each(this.barItems, function(e) {
32806 if (e.rid != rid) {
32817 indexOfItem : function(item)
32821 Roo.each(this.barItems, function(v, i){
32823 if (v.rid != item.rid) {
32834 setActiveNext : function()
32836 var i = this.indexOfItem(this.getActive());
32838 if (i > this.barItems.length) {
32842 this.setActiveItem(this.barItems[i+1]);
32845 setActivePrev : function()
32847 var i = this.indexOfItem(this.getActive());
32853 this.setActiveItem(this.barItems[i-1]);
32856 format : function()
32858 if(!this.barItems.length){
32862 var width = 100 / this.barItems.length;
32864 Roo.each(this.barItems, function(i){
32865 i.el.setStyle('width', width + '%');
32866 i.topEl.el.setStyle('width', width + '%');
32867 i.bottomEl.el.setStyle('width', width + '%');
32876 * Nav Progress Item
32881 * @class Roo.bootstrap.NavProgressItem
32882 * @extends Roo.bootstrap.Component
32883 * Bootstrap NavProgressItem class
32884 * @cfg {String} rid the reference id
32885 * @cfg {Boolean} active (true|false) Is item active default false
32886 * @cfg {Boolean} disabled (true|false) Is item active default false
32887 * @cfg {String} html
32888 * @cfg {String} position (top|bottom) text position default bottom
32889 * @cfg {String} icon show icon instead of number
32892 * Create a new NavProgressItem
32893 * @param {Object} config The config object
32895 Roo.bootstrap.NavProgressItem = function(config){
32896 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32901 * The raw click event for the entire grid.
32902 * @param {Roo.bootstrap.NavProgressItem} this
32903 * @param {Roo.EventObject} e
32910 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32916 position : 'bottom',
32919 getAutoCreate : function()
32921 var iconCls = 'roo-navigation-bar-item-icon';
32923 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32927 cls: 'roo-navigation-bar-item',
32937 cfg.cls += ' active';
32940 cfg.cls += ' disabled';
32946 disable : function()
32948 this.setDisabled(true);
32951 enable : function()
32953 this.setDisabled(false);
32956 initEvents: function()
32958 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32960 this.iconEl.on('click', this.onClick, this);
32963 onClick : function(e)
32965 e.preventDefault();
32971 if(this.fireEvent('click', this, e) === false){
32975 this.parent().setActiveItem(this);
32978 isActive: function ()
32980 return this.active;
32983 setActive : function(state)
32985 if(this.active == state){
32989 this.active = state;
32992 this.el.addClass('active');
32996 this.el.removeClass('active');
33001 setDisabled : function(state)
33003 if(this.disabled == state){
33007 this.disabled = state;
33010 this.el.addClass('disabled');
33014 this.el.removeClass('disabled');
33017 tooltipEl : function()
33019 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33032 * @class Roo.bootstrap.FieldLabel
33033 * @extends Roo.bootstrap.Component
33034 * Bootstrap FieldLabel class
33035 * @cfg {String} html contents of the element
33036 * @cfg {String} tag tag of the element default label
33037 * @cfg {String} cls class of the element
33038 * @cfg {String} target label target
33039 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33040 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33041 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33042 * @cfg {String} iconTooltip default "This field is required"
33043 * @cfg {String} indicatorpos (left|right) default left
33046 * Create a new FieldLabel
33047 * @param {Object} config The config object
33050 Roo.bootstrap.FieldLabel = function(config){
33051 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33056 * Fires after the field has been marked as invalid.
33057 * @param {Roo.form.FieldLabel} this
33058 * @param {String} msg The validation message
33063 * Fires after the field has been validated with no errors.
33064 * @param {Roo.form.FieldLabel} this
33070 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33077 invalidClass : 'has-warning',
33078 validClass : 'has-success',
33079 iconTooltip : 'This field is required',
33080 indicatorpos : 'left',
33082 getAutoCreate : function(){
33085 if (!this.allowBlank) {
33091 cls : 'roo-bootstrap-field-label ' + this.cls,
33096 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33097 tooltip : this.iconTooltip
33106 if(this.indicatorpos == 'right'){
33109 cls : 'roo-bootstrap-field-label ' + this.cls,
33118 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33119 tooltip : this.iconTooltip
33128 initEvents: function()
33130 Roo.bootstrap.Element.superclass.initEvents.call(this);
33132 this.indicator = this.indicatorEl();
33134 if(this.indicator){
33135 this.indicator.removeClass('visible');
33136 this.indicator.addClass('invisible');
33139 Roo.bootstrap.FieldLabel.register(this);
33142 indicatorEl : function()
33144 var indicator = this.el.select('i.roo-required-indicator',true).first();
33155 * Mark this field as valid
33157 markValid : function()
33159 if(this.indicator){
33160 this.indicator.removeClass('visible');
33161 this.indicator.addClass('invisible');
33163 if (Roo.bootstrap.version == 3) {
33164 this.el.removeClass(this.invalidClass);
33165 this.el.addClass(this.validClass);
33167 this.el.removeClass('is-invalid');
33168 this.el.addClass('is-valid');
33172 this.fireEvent('valid', this);
33176 * Mark this field as invalid
33177 * @param {String} msg The validation message
33179 markInvalid : function(msg)
33181 if(this.indicator){
33182 this.indicator.removeClass('invisible');
33183 this.indicator.addClass('visible');
33185 if (Roo.bootstrap.version == 3) {
33186 this.el.removeClass(this.validClass);
33187 this.el.addClass(this.invalidClass);
33189 this.el.removeClass('is-valid');
33190 this.el.addClass('is-invalid');
33194 this.fireEvent('invalid', this, msg);
33200 Roo.apply(Roo.bootstrap.FieldLabel, {
33205 * register a FieldLabel Group
33206 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33208 register : function(label)
33210 if(this.groups.hasOwnProperty(label.target)){
33214 this.groups[label.target] = label;
33218 * fetch a FieldLabel Group based on the target
33219 * @param {string} target
33220 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33222 get: function(target) {
33223 if (typeof(this.groups[target]) == 'undefined') {
33227 return this.groups[target] ;
33236 * page DateSplitField.
33242 * @class Roo.bootstrap.DateSplitField
33243 * @extends Roo.bootstrap.Component
33244 * Bootstrap DateSplitField class
33245 * @cfg {string} fieldLabel - the label associated
33246 * @cfg {Number} labelWidth set the width of label (0-12)
33247 * @cfg {String} labelAlign (top|left)
33248 * @cfg {Boolean} dayAllowBlank (true|false) default false
33249 * @cfg {Boolean} monthAllowBlank (true|false) default false
33250 * @cfg {Boolean} yearAllowBlank (true|false) default false
33251 * @cfg {string} dayPlaceholder
33252 * @cfg {string} monthPlaceholder
33253 * @cfg {string} yearPlaceholder
33254 * @cfg {string} dayFormat default 'd'
33255 * @cfg {string} monthFormat default 'm'
33256 * @cfg {string} yearFormat default 'Y'
33257 * @cfg {Number} labellg set the width of label (1-12)
33258 * @cfg {Number} labelmd set the width of label (1-12)
33259 * @cfg {Number} labelsm set the width of label (1-12)
33260 * @cfg {Number} labelxs set the width of label (1-12)
33264 * Create a new DateSplitField
33265 * @param {Object} config The config object
33268 Roo.bootstrap.DateSplitField = function(config){
33269 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33275 * getting the data of years
33276 * @param {Roo.bootstrap.DateSplitField} this
33277 * @param {Object} years
33282 * getting the data of days
33283 * @param {Roo.bootstrap.DateSplitField} this
33284 * @param {Object} days
33289 * Fires after the field has been marked as invalid.
33290 * @param {Roo.form.Field} this
33291 * @param {String} msg The validation message
33296 * Fires after the field has been validated with no errors.
33297 * @param {Roo.form.Field} this
33303 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33306 labelAlign : 'top',
33308 dayAllowBlank : false,
33309 monthAllowBlank : false,
33310 yearAllowBlank : false,
33311 dayPlaceholder : '',
33312 monthPlaceholder : '',
33313 yearPlaceholder : '',
33317 isFormField : true,
33323 getAutoCreate : function()
33327 cls : 'row roo-date-split-field-group',
33332 cls : 'form-hidden-field roo-date-split-field-group-value',
33338 var labelCls = 'col-md-12';
33339 var contentCls = 'col-md-4';
33341 if(this.fieldLabel){
33345 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33349 html : this.fieldLabel
33354 if(this.labelAlign == 'left'){
33356 if(this.labelWidth > 12){
33357 label.style = "width: " + this.labelWidth + 'px';
33360 if(this.labelWidth < 13 && this.labelmd == 0){
33361 this.labelmd = this.labelWidth;
33364 if(this.labellg > 0){
33365 labelCls = ' col-lg-' + this.labellg;
33366 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33369 if(this.labelmd > 0){
33370 labelCls = ' col-md-' + this.labelmd;
33371 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33374 if(this.labelsm > 0){
33375 labelCls = ' col-sm-' + this.labelsm;
33376 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33379 if(this.labelxs > 0){
33380 labelCls = ' col-xs-' + this.labelxs;
33381 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33385 label.cls += ' ' + labelCls;
33387 cfg.cn.push(label);
33390 Roo.each(['day', 'month', 'year'], function(t){
33393 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33400 inputEl: function ()
33402 return this.el.select('.roo-date-split-field-group-value', true).first();
33405 onRender : function(ct, position)
33409 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33411 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33413 this.dayField = new Roo.bootstrap.ComboBox({
33414 allowBlank : this.dayAllowBlank,
33415 alwaysQuery : true,
33416 displayField : 'value',
33419 forceSelection : true,
33421 placeholder : this.dayPlaceholder,
33422 selectOnFocus : true,
33423 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33424 triggerAction : 'all',
33426 valueField : 'value',
33427 store : new Roo.data.SimpleStore({
33428 data : (function() {
33430 _this.fireEvent('days', _this, days);
33433 fields : [ 'value' ]
33436 select : function (_self, record, index)
33438 _this.setValue(_this.getValue());
33443 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33445 this.monthField = new Roo.bootstrap.MonthField({
33446 after : '<i class=\"fa fa-calendar\"></i>',
33447 allowBlank : this.monthAllowBlank,
33448 placeholder : this.monthPlaceholder,
33451 render : function (_self)
33453 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33454 e.preventDefault();
33458 select : function (_self, oldvalue, newvalue)
33460 _this.setValue(_this.getValue());
33465 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33467 this.yearField = new Roo.bootstrap.ComboBox({
33468 allowBlank : this.yearAllowBlank,
33469 alwaysQuery : true,
33470 displayField : 'value',
33473 forceSelection : true,
33475 placeholder : this.yearPlaceholder,
33476 selectOnFocus : true,
33477 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33478 triggerAction : 'all',
33480 valueField : 'value',
33481 store : new Roo.data.SimpleStore({
33482 data : (function() {
33484 _this.fireEvent('years', _this, years);
33487 fields : [ 'value' ]
33490 select : function (_self, record, index)
33492 _this.setValue(_this.getValue());
33497 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33500 setValue : function(v, format)
33502 this.inputEl.dom.value = v;
33504 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33506 var d = Date.parseDate(v, f);
33513 this.setDay(d.format(this.dayFormat));
33514 this.setMonth(d.format(this.monthFormat));
33515 this.setYear(d.format(this.yearFormat));
33522 setDay : function(v)
33524 this.dayField.setValue(v);
33525 this.inputEl.dom.value = this.getValue();
33530 setMonth : function(v)
33532 this.monthField.setValue(v, true);
33533 this.inputEl.dom.value = this.getValue();
33538 setYear : function(v)
33540 this.yearField.setValue(v);
33541 this.inputEl.dom.value = this.getValue();
33546 getDay : function()
33548 return this.dayField.getValue();
33551 getMonth : function()
33553 return this.monthField.getValue();
33556 getYear : function()
33558 return this.yearField.getValue();
33561 getValue : function()
33563 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33565 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33575 this.inputEl.dom.value = '';
33580 validate : function()
33582 var d = this.dayField.validate();
33583 var m = this.monthField.validate();
33584 var y = this.yearField.validate();
33589 (!this.dayAllowBlank && !d) ||
33590 (!this.monthAllowBlank && !m) ||
33591 (!this.yearAllowBlank && !y)
33596 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33605 this.markInvalid();
33610 markValid : function()
33613 var label = this.el.select('label', true).first();
33614 var icon = this.el.select('i.fa-star', true).first();
33620 this.fireEvent('valid', this);
33624 * Mark this field as invalid
33625 * @param {String} msg The validation message
33627 markInvalid : function(msg)
33630 var label = this.el.select('label', true).first();
33631 var icon = this.el.select('i.fa-star', true).first();
33633 if(label && !icon){
33634 this.el.select('.roo-date-split-field-label', true).createChild({
33636 cls : 'text-danger fa fa-lg fa-star',
33637 tooltip : 'This field is required',
33638 style : 'margin-right:5px;'
33642 this.fireEvent('invalid', this, msg);
33645 clearInvalid : function()
33647 var label = this.el.select('label', true).first();
33648 var icon = this.el.select('i.fa-star', true).first();
33654 this.fireEvent('valid', this);
33657 getName: function()
33667 * http://masonry.desandro.com
33669 * The idea is to render all the bricks based on vertical width...
33671 * The original code extends 'outlayer' - we might need to use that....
33677 * @class Roo.bootstrap.LayoutMasonry
33678 * @extends Roo.bootstrap.Component
33679 * Bootstrap Layout Masonry class
33682 * Create a new Element
33683 * @param {Object} config The config object
33686 Roo.bootstrap.LayoutMasonry = function(config){
33688 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33692 Roo.bootstrap.LayoutMasonry.register(this);
33698 * Fire after layout the items
33699 * @param {Roo.bootstrap.LayoutMasonry} this
33700 * @param {Roo.EventObject} e
33707 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33710 * @cfg {Boolean} isLayoutInstant = no animation?
33712 isLayoutInstant : false, // needed?
33715 * @cfg {Number} boxWidth width of the columns
33720 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33725 * @cfg {Number} padWidth padding below box..
33730 * @cfg {Number} gutter gutter width..
33735 * @cfg {Number} maxCols maximum number of columns
33741 * @cfg {Boolean} isAutoInitial defalut true
33743 isAutoInitial : true,
33748 * @cfg {Boolean} isHorizontal defalut false
33750 isHorizontal : false,
33752 currentSize : null,
33758 bricks: null, //CompositeElement
33762 _isLayoutInited : false,
33764 // isAlternative : false, // only use for vertical layout...
33767 * @cfg {Number} alternativePadWidth padding below box..
33769 alternativePadWidth : 50,
33771 selectedBrick : [],
33773 getAutoCreate : function(){
33775 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33779 cls: 'blog-masonary-wrapper ' + this.cls,
33781 cls : 'mas-boxes masonary'
33788 getChildContainer: function( )
33790 if (this.boxesEl) {
33791 return this.boxesEl;
33794 this.boxesEl = this.el.select('.mas-boxes').first();
33796 return this.boxesEl;
33800 initEvents : function()
33804 if(this.isAutoInitial){
33805 Roo.log('hook children rendered');
33806 this.on('childrenrendered', function() {
33807 Roo.log('children rendered');
33813 initial : function()
33815 this.selectedBrick = [];
33817 this.currentSize = this.el.getBox(true);
33819 Roo.EventManager.onWindowResize(this.resize, this);
33821 if(!this.isAutoInitial){
33829 //this.layout.defer(500,this);
33833 resize : function()
33835 var cs = this.el.getBox(true);
33838 this.currentSize.width == cs.width &&
33839 this.currentSize.x == cs.x &&
33840 this.currentSize.height == cs.height &&
33841 this.currentSize.y == cs.y
33843 Roo.log("no change in with or X or Y");
33847 this.currentSize = cs;
33853 layout : function()
33855 this._resetLayout();
33857 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33859 this.layoutItems( isInstant );
33861 this._isLayoutInited = true;
33863 this.fireEvent('layout', this);
33867 _resetLayout : function()
33869 if(this.isHorizontal){
33870 this.horizontalMeasureColumns();
33874 this.verticalMeasureColumns();
33878 verticalMeasureColumns : function()
33880 this.getContainerWidth();
33882 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33883 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33887 var boxWidth = this.boxWidth + this.padWidth;
33889 if(this.containerWidth < this.boxWidth){
33890 boxWidth = this.containerWidth
33893 var containerWidth = this.containerWidth;
33895 var cols = Math.floor(containerWidth / boxWidth);
33897 this.cols = Math.max( cols, 1 );
33899 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33901 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33903 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33905 this.colWidth = boxWidth + avail - this.padWidth;
33907 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33908 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33911 horizontalMeasureColumns : function()
33913 this.getContainerWidth();
33915 var boxWidth = this.boxWidth;
33917 if(this.containerWidth < boxWidth){
33918 boxWidth = this.containerWidth;
33921 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33923 this.el.setHeight(boxWidth);
33927 getContainerWidth : function()
33929 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33932 layoutItems : function( isInstant )
33934 Roo.log(this.bricks);
33936 var items = Roo.apply([], this.bricks);
33938 if(this.isHorizontal){
33939 this._horizontalLayoutItems( items , isInstant );
33943 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33944 // this._verticalAlternativeLayoutItems( items , isInstant );
33948 this._verticalLayoutItems( items , isInstant );
33952 _verticalLayoutItems : function ( items , isInstant)
33954 if ( !items || !items.length ) {
33959 ['xs', 'xs', 'xs', 'tall'],
33960 ['xs', 'xs', 'tall'],
33961 ['xs', 'xs', 'sm'],
33962 ['xs', 'xs', 'xs'],
33968 ['sm', 'xs', 'xs'],
33972 ['tall', 'xs', 'xs', 'xs'],
33973 ['tall', 'xs', 'xs'],
33985 Roo.each(items, function(item, k){
33987 switch (item.size) {
33988 // these layouts take up a full box,
33999 boxes.push([item]);
34022 var filterPattern = function(box, length)
34030 var pattern = box.slice(0, length);
34034 Roo.each(pattern, function(i){
34035 format.push(i.size);
34038 Roo.each(standard, function(s){
34040 if(String(s) != String(format)){
34049 if(!match && length == 1){
34054 filterPattern(box, length - 1);
34058 queue.push(pattern);
34060 box = box.slice(length, box.length);
34062 filterPattern(box, 4);
34068 Roo.each(boxes, function(box, k){
34074 if(box.length == 1){
34079 filterPattern(box, 4);
34083 this._processVerticalLayoutQueue( queue, isInstant );
34087 // _verticalAlternativeLayoutItems : function( items , isInstant )
34089 // if ( !items || !items.length ) {
34093 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34097 _horizontalLayoutItems : function ( items , isInstant)
34099 if ( !items || !items.length || items.length < 3) {
34105 var eItems = items.slice(0, 3);
34107 items = items.slice(3, items.length);
34110 ['xs', 'xs', 'xs', 'wide'],
34111 ['xs', 'xs', 'wide'],
34112 ['xs', 'xs', 'sm'],
34113 ['xs', 'xs', 'xs'],
34119 ['sm', 'xs', 'xs'],
34123 ['wide', 'xs', 'xs', 'xs'],
34124 ['wide', 'xs', 'xs'],
34137 Roo.each(items, function(item, k){
34139 switch (item.size) {
34150 boxes.push([item]);
34174 var filterPattern = function(box, length)
34182 var pattern = box.slice(0, length);
34186 Roo.each(pattern, function(i){
34187 format.push(i.size);
34190 Roo.each(standard, function(s){
34192 if(String(s) != String(format)){
34201 if(!match && length == 1){
34206 filterPattern(box, length - 1);
34210 queue.push(pattern);
34212 box = box.slice(length, box.length);
34214 filterPattern(box, 4);
34220 Roo.each(boxes, function(box, k){
34226 if(box.length == 1){
34231 filterPattern(box, 4);
34238 var pos = this.el.getBox(true);
34242 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34244 var hit_end = false;
34246 Roo.each(queue, function(box){
34250 Roo.each(box, function(b){
34252 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34262 Roo.each(box, function(b){
34264 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34267 mx = Math.max(mx, b.x);
34271 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34275 Roo.each(box, function(b){
34277 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34291 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34294 /** Sets position of item in DOM
34295 * @param {Element} item
34296 * @param {Number} x - horizontal position
34297 * @param {Number} y - vertical position
34298 * @param {Boolean} isInstant - disables transitions
34300 _processVerticalLayoutQueue : function( queue, isInstant )
34302 var pos = this.el.getBox(true);
34307 for (var i = 0; i < this.cols; i++){
34311 Roo.each(queue, function(box, k){
34313 var col = k % this.cols;
34315 Roo.each(box, function(b,kk){
34317 b.el.position('absolute');
34319 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34320 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34322 if(b.size == 'md-left' || b.size == 'md-right'){
34323 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34324 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34327 b.el.setWidth(width);
34328 b.el.setHeight(height);
34330 b.el.select('iframe',true).setSize(width,height);
34334 for (var i = 0; i < this.cols; i++){
34336 if(maxY[i] < maxY[col]){
34341 col = Math.min(col, i);
34345 x = pos.x + col * (this.colWidth + this.padWidth);
34349 var positions = [];
34351 switch (box.length){
34353 positions = this.getVerticalOneBoxColPositions(x, y, box);
34356 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34359 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34362 positions = this.getVerticalFourBoxColPositions(x, y, box);
34368 Roo.each(box, function(b,kk){
34370 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34372 var sz = b.el.getSize();
34374 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34382 for (var i = 0; i < this.cols; i++){
34383 mY = Math.max(mY, maxY[i]);
34386 this.el.setHeight(mY - pos.y);
34390 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34392 // var pos = this.el.getBox(true);
34395 // var maxX = pos.right;
34397 // var maxHeight = 0;
34399 // Roo.each(items, function(item, k){
34403 // item.el.position('absolute');
34405 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34407 // item.el.setWidth(width);
34409 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34411 // item.el.setHeight(height);
34414 // item.el.setXY([x, y], isInstant ? false : true);
34416 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34419 // y = y + height + this.alternativePadWidth;
34421 // maxHeight = maxHeight + height + this.alternativePadWidth;
34425 // this.el.setHeight(maxHeight);
34429 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34431 var pos = this.el.getBox(true);
34436 var maxX = pos.right;
34438 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34440 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34442 Roo.each(queue, function(box, k){
34444 Roo.each(box, function(b, kk){
34446 b.el.position('absolute');
34448 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34449 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34451 if(b.size == 'md-left' || b.size == 'md-right'){
34452 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34453 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34456 b.el.setWidth(width);
34457 b.el.setHeight(height);
34465 var positions = [];
34467 switch (box.length){
34469 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34472 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34475 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34478 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34484 Roo.each(box, function(b,kk){
34486 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34488 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34496 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34498 Roo.each(eItems, function(b,k){
34500 b.size = (k == 0) ? 'sm' : 'xs';
34501 b.x = (k == 0) ? 2 : 1;
34502 b.y = (k == 0) ? 2 : 1;
34504 b.el.position('absolute');
34506 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34508 b.el.setWidth(width);
34510 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34512 b.el.setHeight(height);
34516 var positions = [];
34519 x : maxX - this.unitWidth * 2 - this.gutter,
34524 x : maxX - this.unitWidth,
34525 y : minY + (this.unitWidth + this.gutter) * 2
34529 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34533 Roo.each(eItems, function(b,k){
34535 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34541 getVerticalOneBoxColPositions : function(x, y, box)
34545 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34547 if(box[0].size == 'md-left'){
34551 if(box[0].size == 'md-right'){
34556 x : x + (this.unitWidth + this.gutter) * rand,
34563 getVerticalTwoBoxColPositions : function(x, y, box)
34567 if(box[0].size == 'xs'){
34571 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34575 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34589 x : x + (this.unitWidth + this.gutter) * 2,
34590 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34597 getVerticalThreeBoxColPositions : function(x, y, box)
34601 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34609 x : x + (this.unitWidth + this.gutter) * 1,
34614 x : x + (this.unitWidth + this.gutter) * 2,
34622 if(box[0].size == 'xs' && box[1].size == 'xs'){
34631 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34635 x : x + (this.unitWidth + this.gutter) * 1,
34649 x : x + (this.unitWidth + this.gutter) * 2,
34654 x : x + (this.unitWidth + this.gutter) * 2,
34655 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34662 getVerticalFourBoxColPositions : function(x, y, box)
34666 if(box[0].size == 'xs'){
34675 y : y + (this.unitHeight + this.gutter) * 1
34680 y : y + (this.unitHeight + this.gutter) * 2
34684 x : x + (this.unitWidth + this.gutter) * 1,
34698 x : x + (this.unitWidth + this.gutter) * 2,
34703 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34704 y : y + (this.unitHeight + this.gutter) * 1
34708 x : x + (this.unitWidth + this.gutter) * 2,
34709 y : y + (this.unitWidth + this.gutter) * 2
34716 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34720 if(box[0].size == 'md-left'){
34722 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34729 if(box[0].size == 'md-right'){
34731 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34732 y : minY + (this.unitWidth + this.gutter) * 1
34738 var rand = Math.floor(Math.random() * (4 - box[0].y));
34741 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34742 y : minY + (this.unitWidth + this.gutter) * rand
34749 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34753 if(box[0].size == 'xs'){
34756 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34761 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34762 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34770 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34775 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34776 y : minY + (this.unitWidth + this.gutter) * 2
34783 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34787 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34790 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34795 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34796 y : minY + (this.unitWidth + this.gutter) * 1
34800 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34801 y : minY + (this.unitWidth + this.gutter) * 2
34808 if(box[0].size == 'xs' && box[1].size == 'xs'){
34811 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34816 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34821 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34822 y : minY + (this.unitWidth + this.gutter) * 1
34830 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34835 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34836 y : minY + (this.unitWidth + this.gutter) * 2
34840 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34841 y : minY + (this.unitWidth + this.gutter) * 2
34848 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34852 if(box[0].size == 'xs'){
34855 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34860 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34865 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),
34870 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34871 y : minY + (this.unitWidth + this.gutter) * 1
34879 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34884 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34885 y : minY + (this.unitWidth + this.gutter) * 2
34889 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34890 y : minY + (this.unitWidth + this.gutter) * 2
34894 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),
34895 y : minY + (this.unitWidth + this.gutter) * 2
34903 * remove a Masonry Brick
34904 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34906 removeBrick : function(brick_id)
34912 for (var i = 0; i<this.bricks.length; i++) {
34913 if (this.bricks[i].id == brick_id) {
34914 this.bricks.splice(i,1);
34915 this.el.dom.removeChild(Roo.get(brick_id).dom);
34922 * adds a Masonry Brick
34923 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34925 addBrick : function(cfg)
34927 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34928 //this.register(cn);
34929 cn.parentId = this.id;
34930 cn.render(this.el);
34935 * register a Masonry Brick
34936 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34939 register : function(brick)
34941 this.bricks.push(brick);
34942 brick.masonryId = this.id;
34946 * clear all the Masonry Brick
34948 clearAll : function()
34951 //this.getChildContainer().dom.innerHTML = "";
34952 this.el.dom.innerHTML = '';
34955 getSelected : function()
34957 if (!this.selectedBrick) {
34961 return this.selectedBrick;
34965 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34969 * register a Masonry Layout
34970 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34973 register : function(layout)
34975 this.groups[layout.id] = layout;
34978 * fetch a Masonry Layout based on the masonry layout ID
34979 * @param {string} the masonry layout to add
34980 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34983 get: function(layout_id) {
34984 if (typeof(this.groups[layout_id]) == 'undefined') {
34987 return this.groups[layout_id] ;
34999 * http://masonry.desandro.com
35001 * The idea is to render all the bricks based on vertical width...
35003 * The original code extends 'outlayer' - we might need to use that....
35009 * @class Roo.bootstrap.LayoutMasonryAuto
35010 * @extends Roo.bootstrap.Component
35011 * Bootstrap Layout Masonry class
35014 * Create a new Element
35015 * @param {Object} config The config object
35018 Roo.bootstrap.LayoutMasonryAuto = function(config){
35019 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35022 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35025 * @cfg {Boolean} isFitWidth - resize the width..
35027 isFitWidth : false, // options..
35029 * @cfg {Boolean} isOriginLeft = left align?
35031 isOriginLeft : true,
35033 * @cfg {Boolean} isOriginTop = top align?
35035 isOriginTop : false,
35037 * @cfg {Boolean} isLayoutInstant = no animation?
35039 isLayoutInstant : false, // needed?
35041 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35043 isResizingContainer : true,
35045 * @cfg {Number} columnWidth width of the columns
35051 * @cfg {Number} maxCols maximum number of columns
35056 * @cfg {Number} padHeight padding below box..
35062 * @cfg {Boolean} isAutoInitial defalut true
35065 isAutoInitial : true,
35071 initialColumnWidth : 0,
35072 currentSize : null,
35074 colYs : null, // array.
35081 bricks: null, //CompositeElement
35082 cols : 0, // array?
35083 // element : null, // wrapped now this.el
35084 _isLayoutInited : null,
35087 getAutoCreate : function(){
35091 cls: 'blog-masonary-wrapper ' + this.cls,
35093 cls : 'mas-boxes masonary'
35100 getChildContainer: function( )
35102 if (this.boxesEl) {
35103 return this.boxesEl;
35106 this.boxesEl = this.el.select('.mas-boxes').first();
35108 return this.boxesEl;
35112 initEvents : function()
35116 if(this.isAutoInitial){
35117 Roo.log('hook children rendered');
35118 this.on('childrenrendered', function() {
35119 Roo.log('children rendered');
35126 initial : function()
35128 this.reloadItems();
35130 this.currentSize = this.el.getBox(true);
35132 /// was window resize... - let's see if this works..
35133 Roo.EventManager.onWindowResize(this.resize, this);
35135 if(!this.isAutoInitial){
35140 this.layout.defer(500,this);
35143 reloadItems: function()
35145 this.bricks = this.el.select('.masonry-brick', true);
35147 this.bricks.each(function(b) {
35148 //Roo.log(b.getSize());
35149 if (!b.attr('originalwidth')) {
35150 b.attr('originalwidth', b.getSize().width);
35155 Roo.log(this.bricks.elements.length);
35158 resize : function()
35161 var cs = this.el.getBox(true);
35163 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35164 Roo.log("no change in with or X");
35167 this.currentSize = cs;
35171 layout : function()
35174 this._resetLayout();
35175 //this._manageStamps();
35177 // don't animate first layout
35178 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35179 this.layoutItems( isInstant );
35181 // flag for initalized
35182 this._isLayoutInited = true;
35185 layoutItems : function( isInstant )
35187 //var items = this._getItemsForLayout( this.items );
35188 // original code supports filtering layout items.. we just ignore it..
35190 this._layoutItems( this.bricks , isInstant );
35192 this._postLayout();
35194 _layoutItems : function ( items , isInstant)
35196 //this.fireEvent( 'layout', this, items );
35199 if ( !items || !items.elements.length ) {
35200 // no items, emit event with empty array
35205 items.each(function(item) {
35206 Roo.log("layout item");
35208 // get x/y object from method
35209 var position = this._getItemLayoutPosition( item );
35211 position.item = item;
35212 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35213 queue.push( position );
35216 this._processLayoutQueue( queue );
35218 /** Sets position of item in DOM
35219 * @param {Element} item
35220 * @param {Number} x - horizontal position
35221 * @param {Number} y - vertical position
35222 * @param {Boolean} isInstant - disables transitions
35224 _processLayoutQueue : function( queue )
35226 for ( var i=0, len = queue.length; i < len; i++ ) {
35227 var obj = queue[i];
35228 obj.item.position('absolute');
35229 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35235 * Any logic you want to do after each layout,
35236 * i.e. size the container
35238 _postLayout : function()
35240 this.resizeContainer();
35243 resizeContainer : function()
35245 if ( !this.isResizingContainer ) {
35248 var size = this._getContainerSize();
35250 this.el.setSize(size.width,size.height);
35251 this.boxesEl.setSize(size.width,size.height);
35257 _resetLayout : function()
35259 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35260 this.colWidth = this.el.getWidth();
35261 //this.gutter = this.el.getWidth();
35263 this.measureColumns();
35269 this.colYs.push( 0 );
35275 measureColumns : function()
35277 this.getContainerWidth();
35278 // if columnWidth is 0, default to outerWidth of first item
35279 if ( !this.columnWidth ) {
35280 var firstItem = this.bricks.first();
35281 Roo.log(firstItem);
35282 this.columnWidth = this.containerWidth;
35283 if (firstItem && firstItem.attr('originalwidth') ) {
35284 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35286 // columnWidth fall back to item of first element
35287 Roo.log("set column width?");
35288 this.initialColumnWidth = this.columnWidth ;
35290 // if first elem has no width, default to size of container
35295 if (this.initialColumnWidth) {
35296 this.columnWidth = this.initialColumnWidth;
35301 // column width is fixed at the top - however if container width get's smaller we should
35304 // this bit calcs how man columns..
35306 var columnWidth = this.columnWidth += this.gutter;
35308 // calculate columns
35309 var containerWidth = this.containerWidth + this.gutter;
35311 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35312 // fix rounding errors, typically with gutters
35313 var excess = columnWidth - containerWidth % columnWidth;
35316 // if overshoot is less than a pixel, round up, otherwise floor it
35317 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35318 cols = Math[ mathMethod ]( cols );
35319 this.cols = Math.max( cols, 1 );
35320 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35322 // padding positioning..
35323 var totalColWidth = this.cols * this.columnWidth;
35324 var padavail = this.containerWidth - totalColWidth;
35325 // so for 2 columns - we need 3 'pads'
35327 var padNeeded = (1+this.cols) * this.padWidth;
35329 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35331 this.columnWidth += padExtra
35332 //this.padWidth = Math.floor(padavail / ( this.cols));
35334 // adjust colum width so that padding is fixed??
35336 // we have 3 columns ... total = width * 3
35337 // we have X left over... that should be used by
35339 //if (this.expandC) {
35347 getContainerWidth : function()
35349 /* // container is parent if fit width
35350 var container = this.isFitWidth ? this.element.parentNode : this.element;
35351 // check that this.size and size are there
35352 // IE8 triggers resize on body size change, so they might not be
35354 var size = getSize( container ); //FIXME
35355 this.containerWidth = size && size.innerWidth; //FIXME
35358 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35362 _getItemLayoutPosition : function( item ) // what is item?
35364 // we resize the item to our columnWidth..
35366 item.setWidth(this.columnWidth);
35367 item.autoBoxAdjust = false;
35369 var sz = item.getSize();
35371 // how many columns does this brick span
35372 var remainder = this.containerWidth % this.columnWidth;
35374 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35375 // round if off by 1 pixel, otherwise use ceil
35376 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35377 colSpan = Math.min( colSpan, this.cols );
35379 // normally this should be '1' as we dont' currently allow multi width columns..
35381 var colGroup = this._getColGroup( colSpan );
35382 // get the minimum Y value from the columns
35383 var minimumY = Math.min.apply( Math, colGroup );
35384 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35386 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35388 // position the brick
35390 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35391 y: this.currentSize.y + minimumY + this.padHeight
35395 // apply setHeight to necessary columns
35396 var setHeight = minimumY + sz.height + this.padHeight;
35397 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35399 var setSpan = this.cols + 1 - colGroup.length;
35400 for ( var i = 0; i < setSpan; i++ ) {
35401 this.colYs[ shortColIndex + i ] = setHeight ;
35408 * @param {Number} colSpan - number of columns the element spans
35409 * @returns {Array} colGroup
35411 _getColGroup : function( colSpan )
35413 if ( colSpan < 2 ) {
35414 // if brick spans only one column, use all the column Ys
35419 // how many different places could this brick fit horizontally
35420 var groupCount = this.cols + 1 - colSpan;
35421 // for each group potential horizontal position
35422 for ( var i = 0; i < groupCount; i++ ) {
35423 // make an array of colY values for that one group
35424 var groupColYs = this.colYs.slice( i, i + colSpan );
35425 // and get the max value of the array
35426 colGroup[i] = Math.max.apply( Math, groupColYs );
35431 _manageStamp : function( stamp )
35433 var stampSize = stamp.getSize();
35434 var offset = stamp.getBox();
35435 // get the columns that this stamp affects
35436 var firstX = this.isOriginLeft ? offset.x : offset.right;
35437 var lastX = firstX + stampSize.width;
35438 var firstCol = Math.floor( firstX / this.columnWidth );
35439 firstCol = Math.max( 0, firstCol );
35441 var lastCol = Math.floor( lastX / this.columnWidth );
35442 // lastCol should not go over if multiple of columnWidth #425
35443 lastCol -= lastX % this.columnWidth ? 0 : 1;
35444 lastCol = Math.min( this.cols - 1, lastCol );
35446 // set colYs to bottom of the stamp
35447 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35450 for ( var i = firstCol; i <= lastCol; i++ ) {
35451 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35456 _getContainerSize : function()
35458 this.maxY = Math.max.apply( Math, this.colYs );
35463 if ( this.isFitWidth ) {
35464 size.width = this._getContainerFitWidth();
35470 _getContainerFitWidth : function()
35472 var unusedCols = 0;
35473 // count unused columns
35476 if ( this.colYs[i] !== 0 ) {
35481 // fit container to columns that have been used
35482 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35485 needsResizeLayout : function()
35487 var previousWidth = this.containerWidth;
35488 this.getContainerWidth();
35489 return previousWidth !== this.containerWidth;
35504 * @class Roo.bootstrap.MasonryBrick
35505 * @extends Roo.bootstrap.Component
35506 * Bootstrap MasonryBrick class
35509 * Create a new MasonryBrick
35510 * @param {Object} config The config object
35513 Roo.bootstrap.MasonryBrick = function(config){
35515 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35517 Roo.bootstrap.MasonryBrick.register(this);
35523 * When a MasonryBrick is clcik
35524 * @param {Roo.bootstrap.MasonryBrick} this
35525 * @param {Roo.EventObject} e
35531 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35534 * @cfg {String} title
35538 * @cfg {String} html
35542 * @cfg {String} bgimage
35546 * @cfg {String} videourl
35550 * @cfg {String} cls
35554 * @cfg {String} href
35558 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35563 * @cfg {String} placetitle (center|bottom)
35568 * @cfg {Boolean} isFitContainer defalut true
35570 isFitContainer : true,
35573 * @cfg {Boolean} preventDefault defalut false
35575 preventDefault : false,
35578 * @cfg {Boolean} inverse defalut false
35580 maskInverse : false,
35582 getAutoCreate : function()
35584 if(!this.isFitContainer){
35585 return this.getSplitAutoCreate();
35588 var cls = 'masonry-brick masonry-brick-full';
35590 if(this.href.length){
35591 cls += ' masonry-brick-link';
35594 if(this.bgimage.length){
35595 cls += ' masonry-brick-image';
35598 if(this.maskInverse){
35599 cls += ' mask-inverse';
35602 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35603 cls += ' enable-mask';
35607 cls += ' masonry-' + this.size + '-brick';
35610 if(this.placetitle.length){
35612 switch (this.placetitle) {
35614 cls += ' masonry-center-title';
35617 cls += ' masonry-bottom-title';
35624 if(!this.html.length && !this.bgimage.length){
35625 cls += ' masonry-center-title';
35628 if(!this.html.length && this.bgimage.length){
35629 cls += ' masonry-bottom-title';
35634 cls += ' ' + this.cls;
35638 tag: (this.href.length) ? 'a' : 'div',
35643 cls: 'masonry-brick-mask'
35647 cls: 'masonry-brick-paragraph',
35653 if(this.href.length){
35654 cfg.href = this.href;
35657 var cn = cfg.cn[1].cn;
35659 if(this.title.length){
35662 cls: 'masonry-brick-title',
35667 if(this.html.length){
35670 cls: 'masonry-brick-text',
35675 if (!this.title.length && !this.html.length) {
35676 cfg.cn[1].cls += ' hide';
35679 if(this.bgimage.length){
35682 cls: 'masonry-brick-image-view',
35687 if(this.videourl.length){
35688 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35689 // youtube support only?
35692 cls: 'masonry-brick-image-view',
35695 allowfullscreen : true
35703 getSplitAutoCreate : function()
35705 var cls = 'masonry-brick masonry-brick-split';
35707 if(this.href.length){
35708 cls += ' masonry-brick-link';
35711 if(this.bgimage.length){
35712 cls += ' masonry-brick-image';
35716 cls += ' masonry-' + this.size + '-brick';
35719 switch (this.placetitle) {
35721 cls += ' masonry-center-title';
35724 cls += ' masonry-bottom-title';
35727 if(!this.bgimage.length){
35728 cls += ' masonry-center-title';
35731 if(this.bgimage.length){
35732 cls += ' masonry-bottom-title';
35738 cls += ' ' + this.cls;
35742 tag: (this.href.length) ? 'a' : 'div',
35747 cls: 'masonry-brick-split-head',
35751 cls: 'masonry-brick-paragraph',
35758 cls: 'masonry-brick-split-body',
35764 if(this.href.length){
35765 cfg.href = this.href;
35768 if(this.title.length){
35769 cfg.cn[0].cn[0].cn.push({
35771 cls: 'masonry-brick-title',
35776 if(this.html.length){
35777 cfg.cn[1].cn.push({
35779 cls: 'masonry-brick-text',
35784 if(this.bgimage.length){
35785 cfg.cn[0].cn.push({
35787 cls: 'masonry-brick-image-view',
35792 if(this.videourl.length){
35793 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35794 // youtube support only?
35795 cfg.cn[0].cn.cn.push({
35797 cls: 'masonry-brick-image-view',
35800 allowfullscreen : true
35807 initEvents: function()
35809 switch (this.size) {
35842 this.el.on('touchstart', this.onTouchStart, this);
35843 this.el.on('touchmove', this.onTouchMove, this);
35844 this.el.on('touchend', this.onTouchEnd, this);
35845 this.el.on('contextmenu', this.onContextMenu, this);
35847 this.el.on('mouseenter' ,this.enter, this);
35848 this.el.on('mouseleave', this.leave, this);
35849 this.el.on('click', this.onClick, this);
35852 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35853 this.parent().bricks.push(this);
35858 onClick: function(e, el)
35860 var time = this.endTimer - this.startTimer;
35861 // Roo.log(e.preventDefault());
35864 e.preventDefault();
35869 if(!this.preventDefault){
35873 e.preventDefault();
35875 if (this.activeClass != '') {
35876 this.selectBrick();
35879 this.fireEvent('click', this, e);
35882 enter: function(e, el)
35884 e.preventDefault();
35886 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35890 if(this.bgimage.length && this.html.length){
35891 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35895 leave: function(e, el)
35897 e.preventDefault();
35899 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35903 if(this.bgimage.length && this.html.length){
35904 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35908 onTouchStart: function(e, el)
35910 // e.preventDefault();
35912 this.touchmoved = false;
35914 if(!this.isFitContainer){
35918 if(!this.bgimage.length || !this.html.length){
35922 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35924 this.timer = new Date().getTime();
35928 onTouchMove: function(e, el)
35930 this.touchmoved = true;
35933 onContextMenu : function(e,el)
35935 e.preventDefault();
35936 e.stopPropagation();
35940 onTouchEnd: function(e, el)
35942 // e.preventDefault();
35944 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35951 if(!this.bgimage.length || !this.html.length){
35953 if(this.href.length){
35954 window.location.href = this.href;
35960 if(!this.isFitContainer){
35964 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35966 window.location.href = this.href;
35969 //selection on single brick only
35970 selectBrick : function() {
35972 if (!this.parentId) {
35976 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35977 var index = m.selectedBrick.indexOf(this.id);
35980 m.selectedBrick.splice(index,1);
35981 this.el.removeClass(this.activeClass);
35985 for(var i = 0; i < m.selectedBrick.length; i++) {
35986 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35987 b.el.removeClass(b.activeClass);
35990 m.selectedBrick = [];
35992 m.selectedBrick.push(this.id);
35993 this.el.addClass(this.activeClass);
35997 isSelected : function(){
35998 return this.el.hasClass(this.activeClass);
36003 Roo.apply(Roo.bootstrap.MasonryBrick, {
36006 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36008 * register a Masonry Brick
36009 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36012 register : function(brick)
36014 //this.groups[brick.id] = brick;
36015 this.groups.add(brick.id, brick);
36018 * fetch a masonry brick based on the masonry brick ID
36019 * @param {string} the masonry brick to add
36020 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36023 get: function(brick_id)
36025 // if (typeof(this.groups[brick_id]) == 'undefined') {
36028 // return this.groups[brick_id] ;
36030 if(this.groups.key(brick_id)) {
36031 return this.groups.key(brick_id);
36049 * @class Roo.bootstrap.Brick
36050 * @extends Roo.bootstrap.Component
36051 * Bootstrap Brick class
36054 * Create a new Brick
36055 * @param {Object} config The config object
36058 Roo.bootstrap.Brick = function(config){
36059 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36065 * When a Brick is click
36066 * @param {Roo.bootstrap.Brick} this
36067 * @param {Roo.EventObject} e
36073 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36076 * @cfg {String} title
36080 * @cfg {String} html
36084 * @cfg {String} bgimage
36088 * @cfg {String} cls
36092 * @cfg {String} href
36096 * @cfg {String} video
36100 * @cfg {Boolean} square
36104 getAutoCreate : function()
36106 var cls = 'roo-brick';
36108 if(this.href.length){
36109 cls += ' roo-brick-link';
36112 if(this.bgimage.length){
36113 cls += ' roo-brick-image';
36116 if(!this.html.length && !this.bgimage.length){
36117 cls += ' roo-brick-center-title';
36120 if(!this.html.length && this.bgimage.length){
36121 cls += ' roo-brick-bottom-title';
36125 cls += ' ' + this.cls;
36129 tag: (this.href.length) ? 'a' : 'div',
36134 cls: 'roo-brick-paragraph',
36140 if(this.href.length){
36141 cfg.href = this.href;
36144 var cn = cfg.cn[0].cn;
36146 if(this.title.length){
36149 cls: 'roo-brick-title',
36154 if(this.html.length){
36157 cls: 'roo-brick-text',
36164 if(this.bgimage.length){
36167 cls: 'roo-brick-image-view',
36175 initEvents: function()
36177 if(this.title.length || this.html.length){
36178 this.el.on('mouseenter' ,this.enter, this);
36179 this.el.on('mouseleave', this.leave, this);
36182 Roo.EventManager.onWindowResize(this.resize, this);
36184 if(this.bgimage.length){
36185 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36186 this.imageEl.on('load', this.onImageLoad, this);
36193 onImageLoad : function()
36198 resize : function()
36200 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36202 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36204 if(this.bgimage.length){
36205 var image = this.el.select('.roo-brick-image-view', true).first();
36207 image.setWidth(paragraph.getWidth());
36210 image.setHeight(paragraph.getWidth());
36213 this.el.setHeight(image.getHeight());
36214 paragraph.setHeight(image.getHeight());
36220 enter: function(e, el)
36222 e.preventDefault();
36224 if(this.bgimage.length){
36225 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36226 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36230 leave: function(e, el)
36232 e.preventDefault();
36234 if(this.bgimage.length){
36235 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36236 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36251 * @class Roo.bootstrap.NumberField
36252 * @extends Roo.bootstrap.Input
36253 * Bootstrap NumberField class
36259 * Create a new NumberField
36260 * @param {Object} config The config object
36263 Roo.bootstrap.NumberField = function(config){
36264 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36267 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36270 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36272 allowDecimals : true,
36274 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36276 decimalSeparator : ".",
36278 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36280 decimalPrecision : 2,
36282 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36284 allowNegative : true,
36287 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36291 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36293 minValue : Number.NEGATIVE_INFINITY,
36295 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36297 maxValue : Number.MAX_VALUE,
36299 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36301 minText : "The minimum value for this field is {0}",
36303 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36305 maxText : "The maximum value for this field is {0}",
36307 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36308 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36310 nanText : "{0} is not a valid number",
36312 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36314 thousandsDelimiter : false,
36316 * @cfg {String} valueAlign alignment of value
36318 valueAlign : "left",
36320 getAutoCreate : function()
36322 var hiddenInput = {
36326 cls: 'hidden-number-input'
36330 hiddenInput.name = this.name;
36335 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36337 this.name = hiddenInput.name;
36339 if(cfg.cn.length > 0) {
36340 cfg.cn.push(hiddenInput);
36347 initEvents : function()
36349 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36351 var allowed = "0123456789";
36353 if(this.allowDecimals){
36354 allowed += this.decimalSeparator;
36357 if(this.allowNegative){
36361 if(this.thousandsDelimiter) {
36365 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36367 var keyPress = function(e){
36369 var k = e.getKey();
36371 var c = e.getCharCode();
36374 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36375 allowed.indexOf(String.fromCharCode(c)) === -1
36381 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36385 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36390 this.el.on("keypress", keyPress, this);
36393 validateValue : function(value)
36396 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36400 var num = this.parseValue(value);
36403 this.markInvalid(String.format(this.nanText, value));
36407 if(num < this.minValue){
36408 this.markInvalid(String.format(this.minText, this.minValue));
36412 if(num > this.maxValue){
36413 this.markInvalid(String.format(this.maxText, this.maxValue));
36420 getValue : function()
36422 var v = this.hiddenEl().getValue();
36424 return this.fixPrecision(this.parseValue(v));
36427 parseValue : function(value)
36429 if(this.thousandsDelimiter) {
36431 r = new RegExp(",", "g");
36432 value = value.replace(r, "");
36435 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36436 return isNaN(value) ? '' : value;
36439 fixPrecision : function(value)
36441 if(this.thousandsDelimiter) {
36443 r = new RegExp(",", "g");
36444 value = value.replace(r, "");
36447 var nan = isNaN(value);
36449 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36450 return nan ? '' : value;
36452 return parseFloat(value).toFixed(this.decimalPrecision);
36455 setValue : function(v)
36457 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36463 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36465 this.inputEl().dom.value = (v == '') ? '' :
36466 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36468 if(!this.allowZero && v === '0') {
36469 this.hiddenEl().dom.value = '';
36470 this.inputEl().dom.value = '';
36477 decimalPrecisionFcn : function(v)
36479 return Math.floor(v);
36482 beforeBlur : function()
36484 var v = this.parseValue(this.getRawValue());
36486 if(v || v === 0 || v === ''){
36491 hiddenEl : function()
36493 return this.el.select('input.hidden-number-input',true).first();
36505 * @class Roo.bootstrap.DocumentSlider
36506 * @extends Roo.bootstrap.Component
36507 * Bootstrap DocumentSlider class
36510 * Create a new DocumentViewer
36511 * @param {Object} config The config object
36514 Roo.bootstrap.DocumentSlider = function(config){
36515 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36522 * Fire after initEvent
36523 * @param {Roo.bootstrap.DocumentSlider} this
36528 * Fire after update
36529 * @param {Roo.bootstrap.DocumentSlider} this
36535 * @param {Roo.bootstrap.DocumentSlider} this
36541 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36547 getAutoCreate : function()
36551 cls : 'roo-document-slider',
36555 cls : 'roo-document-slider-header',
36559 cls : 'roo-document-slider-header-title'
36565 cls : 'roo-document-slider-body',
36569 cls : 'roo-document-slider-prev',
36573 cls : 'fa fa-chevron-left'
36579 cls : 'roo-document-slider-thumb',
36583 cls : 'roo-document-slider-image'
36589 cls : 'roo-document-slider-next',
36593 cls : 'fa fa-chevron-right'
36605 initEvents : function()
36607 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36608 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36610 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36611 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36613 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36614 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36616 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36617 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36619 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36620 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36622 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36623 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36625 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36626 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36628 this.thumbEl.on('click', this.onClick, this);
36630 this.prevIndicator.on('click', this.prev, this);
36632 this.nextIndicator.on('click', this.next, this);
36636 initial : function()
36638 if(this.files.length){
36639 this.indicator = 1;
36643 this.fireEvent('initial', this);
36646 update : function()
36648 this.imageEl.attr('src', this.files[this.indicator - 1]);
36650 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36652 this.prevIndicator.show();
36654 if(this.indicator == 1){
36655 this.prevIndicator.hide();
36658 this.nextIndicator.show();
36660 if(this.indicator == this.files.length){
36661 this.nextIndicator.hide();
36664 this.thumbEl.scrollTo('top');
36666 this.fireEvent('update', this);
36669 onClick : function(e)
36671 e.preventDefault();
36673 this.fireEvent('click', this);
36678 e.preventDefault();
36680 this.indicator = Math.max(1, this.indicator - 1);
36687 e.preventDefault();
36689 this.indicator = Math.min(this.files.length, this.indicator + 1);
36703 * @class Roo.bootstrap.RadioSet
36704 * @extends Roo.bootstrap.Input
36705 * Bootstrap RadioSet class
36706 * @cfg {String} indicatorpos (left|right) default left
36707 * @cfg {Boolean} inline (true|false) inline the element (default true)
36708 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36710 * Create a new RadioSet
36711 * @param {Object} config The config object
36714 Roo.bootstrap.RadioSet = function(config){
36716 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36720 Roo.bootstrap.RadioSet.register(this);
36725 * Fires when the element is checked or unchecked.
36726 * @param {Roo.bootstrap.RadioSet} this This radio
36727 * @param {Roo.bootstrap.Radio} item The checked item
36732 * Fires when the element is click.
36733 * @param {Roo.bootstrap.RadioSet} this This radio set
36734 * @param {Roo.bootstrap.Radio} item The checked item
36735 * @param {Roo.EventObject} e The event object
36742 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36750 indicatorpos : 'left',
36752 getAutoCreate : function()
36756 cls : 'roo-radio-set-label',
36760 html : this.fieldLabel
36764 if (Roo.bootstrap.version == 3) {
36767 if(this.indicatorpos == 'left'){
36770 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36771 tooltip : 'This field is required'
36776 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36777 tooltip : 'This field is required'
36783 cls : 'roo-radio-set-items'
36786 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36788 if (align === 'left' && this.fieldLabel.length) {
36791 cls : "roo-radio-set-right",
36797 if(this.labelWidth > 12){
36798 label.style = "width: " + this.labelWidth + 'px';
36801 if(this.labelWidth < 13 && this.labelmd == 0){
36802 this.labelmd = this.labelWidth;
36805 if(this.labellg > 0){
36806 label.cls += ' col-lg-' + this.labellg;
36807 items.cls += ' col-lg-' + (12 - this.labellg);
36810 if(this.labelmd > 0){
36811 label.cls += ' col-md-' + this.labelmd;
36812 items.cls += ' col-md-' + (12 - this.labelmd);
36815 if(this.labelsm > 0){
36816 label.cls += ' col-sm-' + this.labelsm;
36817 items.cls += ' col-sm-' + (12 - this.labelsm);
36820 if(this.labelxs > 0){
36821 label.cls += ' col-xs-' + this.labelxs;
36822 items.cls += ' col-xs-' + (12 - this.labelxs);
36828 cls : 'roo-radio-set',
36832 cls : 'roo-radio-set-input',
36835 value : this.value ? this.value : ''
36842 if(this.weight.length){
36843 cfg.cls += ' roo-radio-' + this.weight;
36847 cfg.cls += ' roo-radio-set-inline';
36851 ['xs','sm','md','lg'].map(function(size){
36852 if (settings[size]) {
36853 cfg.cls += ' col-' + size + '-' + settings[size];
36861 initEvents : function()
36863 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36864 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36866 if(!this.fieldLabel.length){
36867 this.labelEl.hide();
36870 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36871 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36873 this.indicator = this.indicatorEl();
36875 if(this.indicator){
36876 this.indicator.addClass('invisible');
36879 this.originalValue = this.getValue();
36883 inputEl: function ()
36885 return this.el.select('.roo-radio-set-input', true).first();
36888 getChildContainer : function()
36890 return this.itemsEl;
36893 register : function(item)
36895 this.radioes.push(item);
36899 validate : function()
36901 if(this.getVisibilityEl().hasClass('hidden')){
36907 Roo.each(this.radioes, function(i){
36916 if(this.allowBlank) {
36920 if(this.disabled || valid){
36925 this.markInvalid();
36930 markValid : function()
36932 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36933 this.indicatorEl().removeClass('visible');
36934 this.indicatorEl().addClass('invisible');
36938 if (Roo.bootstrap.version == 3) {
36939 this.el.removeClass([this.invalidClass, this.validClass]);
36940 this.el.addClass(this.validClass);
36942 this.el.removeClass(['is-invalid','is-valid']);
36943 this.el.addClass(['is-valid']);
36945 this.fireEvent('valid', this);
36948 markInvalid : function(msg)
36950 if(this.allowBlank || this.disabled){
36954 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36955 this.indicatorEl().removeClass('invisible');
36956 this.indicatorEl().addClass('visible');
36958 if (Roo.bootstrap.version == 3) {
36959 this.el.removeClass([this.invalidClass, this.validClass]);
36960 this.el.addClass(this.invalidClass);
36962 this.el.removeClass(['is-invalid','is-valid']);
36963 this.el.addClass(['is-invalid']);
36966 this.fireEvent('invalid', this, msg);
36970 setValue : function(v, suppressEvent)
36972 if(this.value === v){
36979 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36982 Roo.each(this.radioes, function(i){
36984 i.el.removeClass('checked');
36987 Roo.each(this.radioes, function(i){
36989 if(i.value === v || i.value.toString() === v.toString()){
36991 i.el.addClass('checked');
36993 if(suppressEvent !== true){
36994 this.fireEvent('check', this, i);
37005 clearInvalid : function(){
37007 if(!this.el || this.preventMark){
37011 this.el.removeClass([this.invalidClass]);
37013 this.fireEvent('valid', this);
37018 Roo.apply(Roo.bootstrap.RadioSet, {
37022 register : function(set)
37024 this.groups[set.name] = set;
37027 get: function(name)
37029 if (typeof(this.groups[name]) == 'undefined') {
37033 return this.groups[name] ;
37039 * Ext JS Library 1.1.1
37040 * Copyright(c) 2006-2007, Ext JS, LLC.
37042 * Originally Released Under LGPL - original licence link has changed is not relivant.
37045 * <script type="text/javascript">
37050 * @class Roo.bootstrap.SplitBar
37051 * @extends Roo.util.Observable
37052 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37056 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37057 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37058 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37059 split.minSize = 100;
37060 split.maxSize = 600;
37061 split.animate = true;
37062 split.on('moved', splitterMoved);
37065 * Create a new SplitBar
37066 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37067 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37068 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37069 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37070 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37071 position of the SplitBar).
37073 Roo.bootstrap.SplitBar = function(cfg){
37078 // dragElement : elm
37079 // resizingElement: el,
37081 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37082 // placement : Roo.bootstrap.SplitBar.LEFT ,
37083 // existingProxy ???
37086 this.el = Roo.get(cfg.dragElement, true);
37087 this.el.dom.unselectable = "on";
37089 this.resizingEl = Roo.get(cfg.resizingElement, true);
37093 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37094 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37097 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37100 * The minimum size of the resizing element. (Defaults to 0)
37106 * The maximum size of the resizing element. (Defaults to 2000)
37109 this.maxSize = 2000;
37112 * Whether to animate the transition to the new size
37115 this.animate = false;
37118 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37121 this.useShim = false;
37126 if(!cfg.existingProxy){
37128 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37130 this.proxy = Roo.get(cfg.existingProxy).dom;
37133 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37136 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37139 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37142 this.dragSpecs = {};
37145 * @private The adapter to use to positon and resize elements
37147 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37148 this.adapter.init(this);
37150 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37152 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37153 this.el.addClass("roo-splitbar-h");
37156 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37157 this.el.addClass("roo-splitbar-v");
37163 * Fires when the splitter is moved (alias for {@link #event-moved})
37164 * @param {Roo.bootstrap.SplitBar} this
37165 * @param {Number} newSize the new width or height
37170 * Fires when the splitter is moved
37171 * @param {Roo.bootstrap.SplitBar} this
37172 * @param {Number} newSize the new width or height
37176 * @event beforeresize
37177 * Fires before the splitter is dragged
37178 * @param {Roo.bootstrap.SplitBar} this
37180 "beforeresize" : true,
37182 "beforeapply" : true
37185 Roo.util.Observable.call(this);
37188 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37189 onStartProxyDrag : function(x, y){
37190 this.fireEvent("beforeresize", this);
37192 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37194 o.enableDisplayMode("block");
37195 // all splitbars share the same overlay
37196 Roo.bootstrap.SplitBar.prototype.overlay = o;
37198 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37199 this.overlay.show();
37200 Roo.get(this.proxy).setDisplayed("block");
37201 var size = this.adapter.getElementSize(this);
37202 this.activeMinSize = this.getMinimumSize();;
37203 this.activeMaxSize = this.getMaximumSize();;
37204 var c1 = size - this.activeMinSize;
37205 var c2 = Math.max(this.activeMaxSize - size, 0);
37206 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37207 this.dd.resetConstraints();
37208 this.dd.setXConstraint(
37209 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37210 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37212 this.dd.setYConstraint(0, 0);
37214 this.dd.resetConstraints();
37215 this.dd.setXConstraint(0, 0);
37216 this.dd.setYConstraint(
37217 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37218 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37221 this.dragSpecs.startSize = size;
37222 this.dragSpecs.startPoint = [x, y];
37223 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37227 * @private Called after the drag operation by the DDProxy
37229 onEndProxyDrag : function(e){
37230 Roo.get(this.proxy).setDisplayed(false);
37231 var endPoint = Roo.lib.Event.getXY(e);
37233 this.overlay.hide();
37236 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37237 newSize = this.dragSpecs.startSize +
37238 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37239 endPoint[0] - this.dragSpecs.startPoint[0] :
37240 this.dragSpecs.startPoint[0] - endPoint[0]
37243 newSize = this.dragSpecs.startSize +
37244 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37245 endPoint[1] - this.dragSpecs.startPoint[1] :
37246 this.dragSpecs.startPoint[1] - endPoint[1]
37249 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37250 if(newSize != this.dragSpecs.startSize){
37251 if(this.fireEvent('beforeapply', this, newSize) !== false){
37252 this.adapter.setElementSize(this, newSize);
37253 this.fireEvent("moved", this, newSize);
37254 this.fireEvent("resize", this, newSize);
37260 * Get the adapter this SplitBar uses
37261 * @return The adapter object
37263 getAdapter : function(){
37264 return this.adapter;
37268 * Set the adapter this SplitBar uses
37269 * @param {Object} adapter A SplitBar adapter object
37271 setAdapter : function(adapter){
37272 this.adapter = adapter;
37273 this.adapter.init(this);
37277 * Gets the minimum size for the resizing element
37278 * @return {Number} The minimum size
37280 getMinimumSize : function(){
37281 return this.minSize;
37285 * Sets the minimum size for the resizing element
37286 * @param {Number} minSize The minimum size
37288 setMinimumSize : function(minSize){
37289 this.minSize = minSize;
37293 * Gets the maximum size for the resizing element
37294 * @return {Number} The maximum size
37296 getMaximumSize : function(){
37297 return this.maxSize;
37301 * Sets the maximum size for the resizing element
37302 * @param {Number} maxSize The maximum size
37304 setMaximumSize : function(maxSize){
37305 this.maxSize = maxSize;
37309 * Sets the initialize size for the resizing element
37310 * @param {Number} size The initial size
37312 setCurrentSize : function(size){
37313 var oldAnimate = this.animate;
37314 this.animate = false;
37315 this.adapter.setElementSize(this, size);
37316 this.animate = oldAnimate;
37320 * Destroy this splitbar.
37321 * @param {Boolean} removeEl True to remove the element
37323 destroy : function(removeEl){
37325 this.shim.remove();
37328 this.proxy.parentNode.removeChild(this.proxy);
37336 * @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.
37338 Roo.bootstrap.SplitBar.createProxy = function(dir){
37339 var proxy = new Roo.Element(document.createElement("div"));
37340 proxy.unselectable();
37341 var cls = 'roo-splitbar-proxy';
37342 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37343 document.body.appendChild(proxy.dom);
37348 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37349 * Default Adapter. It assumes the splitter and resizing element are not positioned
37350 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37352 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37355 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37356 // do nothing for now
37357 init : function(s){
37361 * Called before drag operations to get the current size of the resizing element.
37362 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37364 getElementSize : function(s){
37365 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37366 return s.resizingEl.getWidth();
37368 return s.resizingEl.getHeight();
37373 * Called after drag operations to set the size of the resizing element.
37374 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37375 * @param {Number} newSize The new size to set
37376 * @param {Function} onComplete A function to be invoked when resizing is complete
37378 setElementSize : function(s, newSize, onComplete){
37379 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37381 s.resizingEl.setWidth(newSize);
37383 onComplete(s, newSize);
37386 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37391 s.resizingEl.setHeight(newSize);
37393 onComplete(s, newSize);
37396 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37403 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37404 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37405 * Adapter that moves the splitter element to align with the resized sizing element.
37406 * Used with an absolute positioned SplitBar.
37407 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37408 * document.body, make sure you assign an id to the body element.
37410 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37411 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37412 this.container = Roo.get(container);
37415 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37416 init : function(s){
37417 this.basic.init(s);
37420 getElementSize : function(s){
37421 return this.basic.getElementSize(s);
37424 setElementSize : function(s, newSize, onComplete){
37425 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37428 moveSplitter : function(s){
37429 var yes = Roo.bootstrap.SplitBar;
37430 switch(s.placement){
37432 s.el.setX(s.resizingEl.getRight());
37435 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37438 s.el.setY(s.resizingEl.getBottom());
37441 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37448 * Orientation constant - Create a vertical SplitBar
37452 Roo.bootstrap.SplitBar.VERTICAL = 1;
37455 * Orientation constant - Create a horizontal SplitBar
37459 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37462 * Placement constant - The resizing element is to the left of the splitter element
37466 Roo.bootstrap.SplitBar.LEFT = 1;
37469 * Placement constant - The resizing element is to the right of the splitter element
37473 Roo.bootstrap.SplitBar.RIGHT = 2;
37476 * Placement constant - The resizing element is positioned above the splitter element
37480 Roo.bootstrap.SplitBar.TOP = 3;
37483 * Placement constant - The resizing element is positioned under splitter element
37487 Roo.bootstrap.SplitBar.BOTTOM = 4;
37488 Roo.namespace("Roo.bootstrap.layout");/*
37490 * Ext JS Library 1.1.1
37491 * Copyright(c) 2006-2007, Ext JS, LLC.
37493 * Originally Released Under LGPL - original licence link has changed is not relivant.
37496 * <script type="text/javascript">
37500 * @class Roo.bootstrap.layout.Manager
37501 * @extends Roo.bootstrap.Component
37502 * Base class for layout managers.
37504 Roo.bootstrap.layout.Manager = function(config)
37506 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37512 /** false to disable window resize monitoring @type Boolean */
37513 this.monitorWindowResize = true;
37518 * Fires when a layout is performed.
37519 * @param {Roo.LayoutManager} this
37523 * @event regionresized
37524 * Fires when the user resizes a region.
37525 * @param {Roo.LayoutRegion} region The resized region
37526 * @param {Number} newSize The new size (width for east/west, height for north/south)
37528 "regionresized" : true,
37530 * @event regioncollapsed
37531 * Fires when a region is collapsed.
37532 * @param {Roo.LayoutRegion} region The collapsed region
37534 "regioncollapsed" : true,
37536 * @event regionexpanded
37537 * Fires when a region is expanded.
37538 * @param {Roo.LayoutRegion} region The expanded region
37540 "regionexpanded" : true
37542 this.updating = false;
37545 this.el = Roo.get(config.el);
37551 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37556 monitorWindowResize : true,
37562 onRender : function(ct, position)
37565 this.el = Roo.get(ct);
37568 //this.fireEvent('render',this);
37572 initEvents: function()
37576 // ie scrollbar fix
37577 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37578 document.body.scroll = "no";
37579 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37580 this.el.position('relative');
37582 this.id = this.el.id;
37583 this.el.addClass("roo-layout-container");
37584 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37585 if(this.el.dom != document.body ) {
37586 this.el.on('resize', this.layout,this);
37587 this.el.on('show', this.layout,this);
37593 * Returns true if this layout is currently being updated
37594 * @return {Boolean}
37596 isUpdating : function(){
37597 return this.updating;
37601 * Suspend the LayoutManager from doing auto-layouts while
37602 * making multiple add or remove calls
37604 beginUpdate : function(){
37605 this.updating = true;
37609 * Restore auto-layouts and optionally disable the manager from performing a layout
37610 * @param {Boolean} noLayout true to disable a layout update
37612 endUpdate : function(noLayout){
37613 this.updating = false;
37619 layout: function(){
37623 onRegionResized : function(region, newSize){
37624 this.fireEvent("regionresized", region, newSize);
37628 onRegionCollapsed : function(region){
37629 this.fireEvent("regioncollapsed", region);
37632 onRegionExpanded : function(region){
37633 this.fireEvent("regionexpanded", region);
37637 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37638 * performs box-model adjustments.
37639 * @return {Object} The size as an object {width: (the width), height: (the height)}
37641 getViewSize : function()
37644 if(this.el.dom != document.body){
37645 size = this.el.getSize();
37647 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37649 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37650 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37655 * Returns the Element this layout is bound to.
37656 * @return {Roo.Element}
37658 getEl : function(){
37663 * Returns the specified region.
37664 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37665 * @return {Roo.LayoutRegion}
37667 getRegion : function(target){
37668 return this.regions[target.toLowerCase()];
37671 onWindowResize : function(){
37672 if(this.monitorWindowResize){
37679 * Ext JS Library 1.1.1
37680 * Copyright(c) 2006-2007, Ext JS, LLC.
37682 * Originally Released Under LGPL - original licence link has changed is not relivant.
37685 * <script type="text/javascript">
37688 * @class Roo.bootstrap.layout.Border
37689 * @extends Roo.bootstrap.layout.Manager
37690 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37691 * please see: examples/bootstrap/nested.html<br><br>
37693 <b>The container the layout is rendered into can be either the body element or any other element.
37694 If it is not the body element, the container needs to either be an absolute positioned element,
37695 or you will need to add "position:relative" to the css of the container. You will also need to specify
37696 the container size if it is not the body element.</b>
37699 * Create a new Border
37700 * @param {Object} config Configuration options
37702 Roo.bootstrap.layout.Border = function(config){
37703 config = config || {};
37704 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37708 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37709 if(config[region]){
37710 config[region].region = region;
37711 this.addRegion(config[region]);
37717 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37719 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37721 parent : false, // this might point to a 'nest' or a ???
37724 * Creates and adds a new region if it doesn't already exist.
37725 * @param {String} target The target region key (north, south, east, west or center).
37726 * @param {Object} config The regions config object
37727 * @return {BorderLayoutRegion} The new region
37729 addRegion : function(config)
37731 if(!this.regions[config.region]){
37732 var r = this.factory(config);
37733 this.bindRegion(r);
37735 return this.regions[config.region];
37739 bindRegion : function(r){
37740 this.regions[r.config.region] = r;
37742 r.on("visibilitychange", this.layout, this);
37743 r.on("paneladded", this.layout, this);
37744 r.on("panelremoved", this.layout, this);
37745 r.on("invalidated", this.layout, this);
37746 r.on("resized", this.onRegionResized, this);
37747 r.on("collapsed", this.onRegionCollapsed, this);
37748 r.on("expanded", this.onRegionExpanded, this);
37752 * Performs a layout update.
37754 layout : function()
37756 if(this.updating) {
37760 // render all the rebions if they have not been done alreayd?
37761 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37762 if(this.regions[region] && !this.regions[region].bodyEl){
37763 this.regions[region].onRender(this.el)
37767 var size = this.getViewSize();
37768 var w = size.width;
37769 var h = size.height;
37774 //var x = 0, y = 0;
37776 var rs = this.regions;
37777 var north = rs["north"];
37778 var south = rs["south"];
37779 var west = rs["west"];
37780 var east = rs["east"];
37781 var center = rs["center"];
37782 //if(this.hideOnLayout){ // not supported anymore
37783 //c.el.setStyle("display", "none");
37785 if(north && north.isVisible()){
37786 var b = north.getBox();
37787 var m = north.getMargins();
37788 b.width = w - (m.left+m.right);
37791 centerY = b.height + b.y + m.bottom;
37792 centerH -= centerY;
37793 north.updateBox(this.safeBox(b));
37795 if(south && south.isVisible()){
37796 var b = south.getBox();
37797 var m = south.getMargins();
37798 b.width = w - (m.left+m.right);
37800 var totalHeight = (b.height + m.top + m.bottom);
37801 b.y = h - totalHeight + m.top;
37802 centerH -= totalHeight;
37803 south.updateBox(this.safeBox(b));
37805 if(west && west.isVisible()){
37806 var b = west.getBox();
37807 var m = west.getMargins();
37808 b.height = centerH - (m.top+m.bottom);
37810 b.y = centerY + m.top;
37811 var totalWidth = (b.width + m.left + m.right);
37812 centerX += totalWidth;
37813 centerW -= totalWidth;
37814 west.updateBox(this.safeBox(b));
37816 if(east && east.isVisible()){
37817 var b = east.getBox();
37818 var m = east.getMargins();
37819 b.height = centerH - (m.top+m.bottom);
37820 var totalWidth = (b.width + m.left + m.right);
37821 b.x = w - totalWidth + m.left;
37822 b.y = centerY + m.top;
37823 centerW -= totalWidth;
37824 east.updateBox(this.safeBox(b));
37827 var m = center.getMargins();
37829 x: centerX + m.left,
37830 y: centerY + m.top,
37831 width: centerW - (m.left+m.right),
37832 height: centerH - (m.top+m.bottom)
37834 //if(this.hideOnLayout){
37835 //center.el.setStyle("display", "block");
37837 center.updateBox(this.safeBox(centerBox));
37840 this.fireEvent("layout", this);
37844 safeBox : function(box){
37845 box.width = Math.max(0, box.width);
37846 box.height = Math.max(0, box.height);
37851 * Adds a ContentPanel (or subclass) to this layout.
37852 * @param {String} target The target region key (north, south, east, west or center).
37853 * @param {Roo.ContentPanel} panel The panel to add
37854 * @return {Roo.ContentPanel} The added panel
37856 add : function(target, panel){
37858 target = target.toLowerCase();
37859 return this.regions[target].add(panel);
37863 * Remove a ContentPanel (or subclass) to this layout.
37864 * @param {String} target The target region key (north, south, east, west or center).
37865 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37866 * @return {Roo.ContentPanel} The removed panel
37868 remove : function(target, panel){
37869 target = target.toLowerCase();
37870 return this.regions[target].remove(panel);
37874 * Searches all regions for a panel with the specified id
37875 * @param {String} panelId
37876 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37878 findPanel : function(panelId){
37879 var rs = this.regions;
37880 for(var target in rs){
37881 if(typeof rs[target] != "function"){
37882 var p = rs[target].getPanel(panelId);
37892 * Searches all regions for a panel with the specified id and activates (shows) it.
37893 * @param {String/ContentPanel} panelId The panels id or the panel itself
37894 * @return {Roo.ContentPanel} The shown panel or null
37896 showPanel : function(panelId) {
37897 var rs = this.regions;
37898 for(var target in rs){
37899 var r = rs[target];
37900 if(typeof r != "function"){
37901 if(r.hasPanel(panelId)){
37902 return r.showPanel(panelId);
37910 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37911 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37914 restoreState : function(provider){
37916 provider = Roo.state.Manager;
37918 var sm = new Roo.LayoutStateManager();
37919 sm.init(this, provider);
37925 * Adds a xtype elements to the layout.
37929 xtype : 'ContentPanel',
37936 xtype : 'NestedLayoutPanel',
37942 items : [ ... list of content panels or nested layout panels.. ]
37946 * @param {Object} cfg Xtype definition of item to add.
37948 addxtype : function(cfg)
37950 // basically accepts a pannel...
37951 // can accept a layout region..!?!?
37952 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37955 // theory? children can only be panels??
37957 //if (!cfg.xtype.match(/Panel$/)) {
37962 if (typeof(cfg.region) == 'undefined') {
37963 Roo.log("Failed to add Panel, region was not set");
37967 var region = cfg.region;
37973 xitems = cfg.items;
37978 if ( region == 'center') {
37979 Roo.log("Center: " + cfg.title);
37985 case 'Content': // ContentPanel (el, cfg)
37986 case 'Scroll': // ContentPanel (el, cfg)
37988 cfg.autoCreate = cfg.autoCreate || true;
37989 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37991 // var el = this.el.createChild();
37992 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37995 this.add(region, ret);
37999 case 'TreePanel': // our new panel!
38000 cfg.el = this.el.createChild();
38001 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38002 this.add(region, ret);
38007 // create a new Layout (which is a Border Layout...
38009 var clayout = cfg.layout;
38010 clayout.el = this.el.createChild();
38011 clayout.items = clayout.items || [];
38015 // replace this exitems with the clayout ones..
38016 xitems = clayout.items;
38018 // force background off if it's in center...
38019 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38020 cfg.background = false;
38022 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38025 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38026 //console.log('adding nested layout panel ' + cfg.toSource());
38027 this.add(region, ret);
38028 nb = {}; /// find first...
38033 // needs grid and region
38035 //var el = this.getRegion(region).el.createChild();
38037 *var el = this.el.createChild();
38038 // create the grid first...
38039 cfg.grid.container = el;
38040 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38043 if (region == 'center' && this.active ) {
38044 cfg.background = false;
38047 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38049 this.add(region, ret);
38051 if (cfg.background) {
38052 // render grid on panel activation (if panel background)
38053 ret.on('activate', function(gp) {
38054 if (!gp.grid.rendered) {
38055 // gp.grid.render(el);
38059 // cfg.grid.render(el);
38065 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38066 // it was the old xcomponent building that caused this before.
38067 // espeically if border is the top element in the tree.
38077 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38079 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38080 this.add(region, ret);
38084 throw "Can not add '" + cfg.xtype + "' to Border";
38090 this.beginUpdate();
38094 Roo.each(xitems, function(i) {
38095 region = nb && i.region ? i.region : false;
38097 var add = ret.addxtype(i);
38100 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38101 if (!i.background) {
38102 abn[region] = nb[region] ;
38109 // make the last non-background panel active..
38110 //if (nb) { Roo.log(abn); }
38113 for(var r in abn) {
38114 region = this.getRegion(r);
38116 // tried using nb[r], but it does not work..
38118 region.showPanel(abn[r]);
38129 factory : function(cfg)
38132 var validRegions = Roo.bootstrap.layout.Border.regions;
38134 var target = cfg.region;
38137 var r = Roo.bootstrap.layout;
38141 return new r.North(cfg);
38143 return new r.South(cfg);
38145 return new r.East(cfg);
38147 return new r.West(cfg);
38149 return new r.Center(cfg);
38151 throw 'Layout region "'+target+'" not supported.';
38158 * Ext JS Library 1.1.1
38159 * Copyright(c) 2006-2007, Ext JS, LLC.
38161 * Originally Released Under LGPL - original licence link has changed is not relivant.
38164 * <script type="text/javascript">
38168 * @class Roo.bootstrap.layout.Basic
38169 * @extends Roo.util.Observable
38170 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38171 * and does not have a titlebar, tabs or any other features. All it does is size and position
38172 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38173 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38174 * @cfg {string} region the region that it inhabits..
38175 * @cfg {bool} skipConfig skip config?
38179 Roo.bootstrap.layout.Basic = function(config){
38181 this.mgr = config.mgr;
38183 this.position = config.region;
38185 var skipConfig = config.skipConfig;
38189 * @scope Roo.BasicLayoutRegion
38193 * @event beforeremove
38194 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38195 * @param {Roo.LayoutRegion} this
38196 * @param {Roo.ContentPanel} panel The panel
38197 * @param {Object} e The cancel event object
38199 "beforeremove" : true,
38201 * @event invalidated
38202 * Fires when the layout for this region is changed.
38203 * @param {Roo.LayoutRegion} this
38205 "invalidated" : true,
38207 * @event visibilitychange
38208 * Fires when this region is shown or hidden
38209 * @param {Roo.LayoutRegion} this
38210 * @param {Boolean} visibility true or false
38212 "visibilitychange" : true,
38214 * @event paneladded
38215 * Fires when a panel is added.
38216 * @param {Roo.LayoutRegion} this
38217 * @param {Roo.ContentPanel} panel The panel
38219 "paneladded" : true,
38221 * @event panelremoved
38222 * Fires when a panel is removed.
38223 * @param {Roo.LayoutRegion} this
38224 * @param {Roo.ContentPanel} panel The panel
38226 "panelremoved" : true,
38228 * @event beforecollapse
38229 * Fires when this region before collapse.
38230 * @param {Roo.LayoutRegion} this
38232 "beforecollapse" : true,
38235 * Fires when this region is collapsed.
38236 * @param {Roo.LayoutRegion} this
38238 "collapsed" : true,
38241 * Fires when this region is expanded.
38242 * @param {Roo.LayoutRegion} this
38247 * Fires when this region is slid into view.
38248 * @param {Roo.LayoutRegion} this
38250 "slideshow" : true,
38253 * Fires when this region slides out of view.
38254 * @param {Roo.LayoutRegion} this
38256 "slidehide" : true,
38258 * @event panelactivated
38259 * Fires when a panel is activated.
38260 * @param {Roo.LayoutRegion} this
38261 * @param {Roo.ContentPanel} panel The activated panel
38263 "panelactivated" : true,
38266 * Fires when the user resizes this region.
38267 * @param {Roo.LayoutRegion} this
38268 * @param {Number} newSize The new size (width for east/west, height for north/south)
38272 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38273 this.panels = new Roo.util.MixedCollection();
38274 this.panels.getKey = this.getPanelId.createDelegate(this);
38276 this.activePanel = null;
38277 // ensure listeners are added...
38279 if (config.listeners || config.events) {
38280 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38281 listeners : config.listeners || {},
38282 events : config.events || {}
38286 if(skipConfig !== true){
38287 this.applyConfig(config);
38291 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38293 getPanelId : function(p){
38297 applyConfig : function(config){
38298 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38299 this.config = config;
38304 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38305 * the width, for horizontal (north, south) the height.
38306 * @param {Number} newSize The new width or height
38308 resizeTo : function(newSize){
38309 var el = this.el ? this.el :
38310 (this.activePanel ? this.activePanel.getEl() : null);
38312 switch(this.position){
38315 el.setWidth(newSize);
38316 this.fireEvent("resized", this, newSize);
38320 el.setHeight(newSize);
38321 this.fireEvent("resized", this, newSize);
38327 getBox : function(){
38328 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38331 getMargins : function(){
38332 return this.margins;
38335 updateBox : function(box){
38337 var el = this.activePanel.getEl();
38338 el.dom.style.left = box.x + "px";
38339 el.dom.style.top = box.y + "px";
38340 this.activePanel.setSize(box.width, box.height);
38344 * Returns the container element for this region.
38345 * @return {Roo.Element}
38347 getEl : function(){
38348 return this.activePanel;
38352 * Returns true if this region is currently visible.
38353 * @return {Boolean}
38355 isVisible : function(){
38356 return this.activePanel ? true : false;
38359 setActivePanel : function(panel){
38360 panel = this.getPanel(panel);
38361 if(this.activePanel && this.activePanel != panel){
38362 this.activePanel.setActiveState(false);
38363 this.activePanel.getEl().setLeftTop(-10000,-10000);
38365 this.activePanel = panel;
38366 panel.setActiveState(true);
38368 panel.setSize(this.box.width, this.box.height);
38370 this.fireEvent("panelactivated", this, panel);
38371 this.fireEvent("invalidated");
38375 * Show the specified panel.
38376 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38377 * @return {Roo.ContentPanel} The shown panel or null
38379 showPanel : function(panel){
38380 panel = this.getPanel(panel);
38382 this.setActivePanel(panel);
38388 * Get the active panel for this region.
38389 * @return {Roo.ContentPanel} The active panel or null
38391 getActivePanel : function(){
38392 return this.activePanel;
38396 * Add the passed ContentPanel(s)
38397 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38398 * @return {Roo.ContentPanel} The panel added (if only one was added)
38400 add : function(panel){
38401 if(arguments.length > 1){
38402 for(var i = 0, len = arguments.length; i < len; i++) {
38403 this.add(arguments[i]);
38407 if(this.hasPanel(panel)){
38408 this.showPanel(panel);
38411 var el = panel.getEl();
38412 if(el.dom.parentNode != this.mgr.el.dom){
38413 this.mgr.el.dom.appendChild(el.dom);
38415 if(panel.setRegion){
38416 panel.setRegion(this);
38418 this.panels.add(panel);
38419 el.setStyle("position", "absolute");
38420 if(!panel.background){
38421 this.setActivePanel(panel);
38422 if(this.config.initialSize && this.panels.getCount()==1){
38423 this.resizeTo(this.config.initialSize);
38426 this.fireEvent("paneladded", this, panel);
38431 * Returns true if the panel is in this region.
38432 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38433 * @return {Boolean}
38435 hasPanel : function(panel){
38436 if(typeof panel == "object"){ // must be panel obj
38437 panel = panel.getId();
38439 return this.getPanel(panel) ? true : false;
38443 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38444 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38445 * @param {Boolean} preservePanel Overrides the config preservePanel option
38446 * @return {Roo.ContentPanel} The panel that was removed
38448 remove : function(panel, preservePanel){
38449 panel = this.getPanel(panel);
38454 this.fireEvent("beforeremove", this, panel, e);
38455 if(e.cancel === true){
38458 var panelId = panel.getId();
38459 this.panels.removeKey(panelId);
38464 * Returns the panel specified or null if it's not in this region.
38465 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38466 * @return {Roo.ContentPanel}
38468 getPanel : function(id){
38469 if(typeof id == "object"){ // must be panel obj
38472 return this.panels.get(id);
38476 * Returns this regions position (north/south/east/west/center).
38479 getPosition: function(){
38480 return this.position;
38484 * Ext JS Library 1.1.1
38485 * Copyright(c) 2006-2007, Ext JS, LLC.
38487 * Originally Released Under LGPL - original licence link has changed is not relivant.
38490 * <script type="text/javascript">
38494 * @class Roo.bootstrap.layout.Region
38495 * @extends Roo.bootstrap.layout.Basic
38496 * This class represents a region in a layout manager.
38498 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38499 * @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})
38500 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38501 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38502 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38503 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38504 * @cfg {String} title The title for the region (overrides panel titles)
38505 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38506 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38507 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38508 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38509 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38510 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38511 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38512 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38513 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38514 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38516 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38517 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38518 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38519 * @cfg {Number} width For East/West panels
38520 * @cfg {Number} height For North/South panels
38521 * @cfg {Boolean} split To show the splitter
38522 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38524 * @cfg {string} cls Extra CSS classes to add to region
38526 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38527 * @cfg {string} region the region that it inhabits..
38530 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38531 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38533 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38534 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38535 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38537 Roo.bootstrap.layout.Region = function(config)
38539 this.applyConfig(config);
38541 var mgr = config.mgr;
38542 var pos = config.region;
38543 config.skipConfig = true;
38544 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38547 this.onRender(mgr.el);
38550 this.visible = true;
38551 this.collapsed = false;
38552 this.unrendered_panels = [];
38555 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38557 position: '', // set by wrapper (eg. north/south etc..)
38558 unrendered_panels : null, // unrendered panels.
38560 tabPosition : false,
38562 mgr: false, // points to 'Border'
38565 createBody : function(){
38566 /** This region's body element
38567 * @type Roo.Element */
38568 this.bodyEl = this.el.createChild({
38570 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38574 onRender: function(ctr, pos)
38576 var dh = Roo.DomHelper;
38577 /** This region's container element
38578 * @type Roo.Element */
38579 this.el = dh.append(ctr.dom, {
38581 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38583 /** This region's title element
38584 * @type Roo.Element */
38586 this.titleEl = dh.append(this.el.dom, {
38588 unselectable: "on",
38589 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38591 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38592 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38596 this.titleEl.enableDisplayMode();
38597 /** This region's title text element
38598 * @type HTMLElement */
38599 this.titleTextEl = this.titleEl.dom.firstChild;
38600 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38602 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38603 this.closeBtn.enableDisplayMode();
38604 this.closeBtn.on("click", this.closeClicked, this);
38605 this.closeBtn.hide();
38607 this.createBody(this.config);
38608 if(this.config.hideWhenEmpty){
38610 this.on("paneladded", this.validateVisibility, this);
38611 this.on("panelremoved", this.validateVisibility, this);
38613 if(this.autoScroll){
38614 this.bodyEl.setStyle("overflow", "auto");
38616 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38618 //if(c.titlebar !== false){
38619 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38620 this.titleEl.hide();
38622 this.titleEl.show();
38623 if(this.config.title){
38624 this.titleTextEl.innerHTML = this.config.title;
38628 if(this.config.collapsed){
38629 this.collapse(true);
38631 if(this.config.hidden){
38635 if (this.unrendered_panels && this.unrendered_panels.length) {
38636 for (var i =0;i< this.unrendered_panels.length; i++) {
38637 this.add(this.unrendered_panels[i]);
38639 this.unrendered_panels = null;
38645 applyConfig : function(c)
38648 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38649 var dh = Roo.DomHelper;
38650 if(c.titlebar !== false){
38651 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38652 this.collapseBtn.on("click", this.collapse, this);
38653 this.collapseBtn.enableDisplayMode();
38655 if(c.showPin === true || this.showPin){
38656 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38657 this.stickBtn.enableDisplayMode();
38658 this.stickBtn.on("click", this.expand, this);
38659 this.stickBtn.hide();
38664 /** This region's collapsed element
38665 * @type Roo.Element */
38668 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38669 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38672 if(c.floatable !== false){
38673 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38674 this.collapsedEl.on("click", this.collapseClick, this);
38677 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38678 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38679 id: "message", unselectable: "on", style:{"float":"left"}});
38680 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38682 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38683 this.expandBtn.on("click", this.expand, this);
38687 if(this.collapseBtn){
38688 this.collapseBtn.setVisible(c.collapsible == true);
38691 this.cmargins = c.cmargins || this.cmargins ||
38692 (this.position == "west" || this.position == "east" ?
38693 {top: 0, left: 2, right:2, bottom: 0} :
38694 {top: 2, left: 0, right:0, bottom: 2});
38696 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38699 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38701 this.autoScroll = c.autoScroll || false;
38706 this.duration = c.duration || .30;
38707 this.slideDuration = c.slideDuration || .45;
38712 * Returns true if this region is currently visible.
38713 * @return {Boolean}
38715 isVisible : function(){
38716 return this.visible;
38720 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38721 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38723 //setCollapsedTitle : function(title){
38724 // title = title || " ";
38725 // if(this.collapsedTitleTextEl){
38726 // this.collapsedTitleTextEl.innerHTML = title;
38730 getBox : function(){
38732 // if(!this.collapsed){
38733 b = this.el.getBox(false, true);
38735 // b = this.collapsedEl.getBox(false, true);
38740 getMargins : function(){
38741 return this.margins;
38742 //return this.collapsed ? this.cmargins : this.margins;
38745 highlight : function(){
38746 this.el.addClass("x-layout-panel-dragover");
38749 unhighlight : function(){
38750 this.el.removeClass("x-layout-panel-dragover");
38753 updateBox : function(box)
38755 if (!this.bodyEl) {
38756 return; // not rendered yet..
38760 if(!this.collapsed){
38761 this.el.dom.style.left = box.x + "px";
38762 this.el.dom.style.top = box.y + "px";
38763 this.updateBody(box.width, box.height);
38765 this.collapsedEl.dom.style.left = box.x + "px";
38766 this.collapsedEl.dom.style.top = box.y + "px";
38767 this.collapsedEl.setSize(box.width, box.height);
38770 this.tabs.autoSizeTabs();
38774 updateBody : function(w, h)
38777 this.el.setWidth(w);
38778 w -= this.el.getBorderWidth("rl");
38779 if(this.config.adjustments){
38780 w += this.config.adjustments[0];
38783 if(h !== null && h > 0){
38784 this.el.setHeight(h);
38785 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38786 h -= this.el.getBorderWidth("tb");
38787 if(this.config.adjustments){
38788 h += this.config.adjustments[1];
38790 this.bodyEl.setHeight(h);
38792 h = this.tabs.syncHeight(h);
38795 if(this.panelSize){
38796 w = w !== null ? w : this.panelSize.width;
38797 h = h !== null ? h : this.panelSize.height;
38799 if(this.activePanel){
38800 var el = this.activePanel.getEl();
38801 w = w !== null ? w : el.getWidth();
38802 h = h !== null ? h : el.getHeight();
38803 this.panelSize = {width: w, height: h};
38804 this.activePanel.setSize(w, h);
38806 if(Roo.isIE && this.tabs){
38807 this.tabs.el.repaint();
38812 * Returns the container element for this region.
38813 * @return {Roo.Element}
38815 getEl : function(){
38820 * Hides this region.
38823 //if(!this.collapsed){
38824 this.el.dom.style.left = "-2000px";
38827 // this.collapsedEl.dom.style.left = "-2000px";
38828 // this.collapsedEl.hide();
38830 this.visible = false;
38831 this.fireEvent("visibilitychange", this, false);
38835 * Shows this region if it was previously hidden.
38838 //if(!this.collapsed){
38841 // this.collapsedEl.show();
38843 this.visible = true;
38844 this.fireEvent("visibilitychange", this, true);
38847 closeClicked : function(){
38848 if(this.activePanel){
38849 this.remove(this.activePanel);
38853 collapseClick : function(e){
38855 e.stopPropagation();
38858 e.stopPropagation();
38864 * Collapses this region.
38865 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38868 collapse : function(skipAnim, skipCheck = false){
38869 if(this.collapsed) {
38873 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38875 this.collapsed = true;
38877 this.split.el.hide();
38879 if(this.config.animate && skipAnim !== true){
38880 this.fireEvent("invalidated", this);
38881 this.animateCollapse();
38883 this.el.setLocation(-20000,-20000);
38885 this.collapsedEl.show();
38886 this.fireEvent("collapsed", this);
38887 this.fireEvent("invalidated", this);
38893 animateCollapse : function(){
38898 * Expands this region if it was previously collapsed.
38899 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38900 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38903 expand : function(e, skipAnim){
38905 e.stopPropagation();
38907 if(!this.collapsed || this.el.hasActiveFx()) {
38911 this.afterSlideIn();
38914 this.collapsed = false;
38915 if(this.config.animate && skipAnim !== true){
38916 this.animateExpand();
38920 this.split.el.show();
38922 this.collapsedEl.setLocation(-2000,-2000);
38923 this.collapsedEl.hide();
38924 this.fireEvent("invalidated", this);
38925 this.fireEvent("expanded", this);
38929 animateExpand : function(){
38933 initTabs : function()
38935 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38937 var ts = new Roo.bootstrap.panel.Tabs({
38938 el: this.bodyEl.dom,
38940 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38941 disableTooltips: this.config.disableTabTips,
38942 toolbar : this.config.toolbar
38945 if(this.config.hideTabs){
38946 ts.stripWrap.setDisplayed(false);
38949 ts.resizeTabs = this.config.resizeTabs === true;
38950 ts.minTabWidth = this.config.minTabWidth || 40;
38951 ts.maxTabWidth = this.config.maxTabWidth || 250;
38952 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38953 ts.monitorResize = false;
38954 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38955 ts.bodyEl.addClass('roo-layout-tabs-body');
38956 this.panels.each(this.initPanelAsTab, this);
38959 initPanelAsTab : function(panel){
38960 var ti = this.tabs.addTab(
38964 this.config.closeOnTab && panel.isClosable(),
38967 if(panel.tabTip !== undefined){
38968 ti.setTooltip(panel.tabTip);
38970 ti.on("activate", function(){
38971 this.setActivePanel(panel);
38974 if(this.config.closeOnTab){
38975 ti.on("beforeclose", function(t, e){
38977 this.remove(panel);
38981 panel.tabItem = ti;
38986 updatePanelTitle : function(panel, title)
38988 if(this.activePanel == panel){
38989 this.updateTitle(title);
38992 var ti = this.tabs.getTab(panel.getEl().id);
38994 if(panel.tabTip !== undefined){
38995 ti.setTooltip(panel.tabTip);
39000 updateTitle : function(title){
39001 if(this.titleTextEl && !this.config.title){
39002 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39006 setActivePanel : function(panel)
39008 panel = this.getPanel(panel);
39009 if(this.activePanel && this.activePanel != panel){
39010 if(this.activePanel.setActiveState(false) === false){
39014 this.activePanel = panel;
39015 panel.setActiveState(true);
39016 if(this.panelSize){
39017 panel.setSize(this.panelSize.width, this.panelSize.height);
39020 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39022 this.updateTitle(panel.getTitle());
39024 this.fireEvent("invalidated", this);
39026 this.fireEvent("panelactivated", this, panel);
39030 * Shows the specified panel.
39031 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39032 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39034 showPanel : function(panel)
39036 panel = this.getPanel(panel);
39039 var tab = this.tabs.getTab(panel.getEl().id);
39040 if(tab.isHidden()){
39041 this.tabs.unhideTab(tab.id);
39045 this.setActivePanel(panel);
39052 * Get the active panel for this region.
39053 * @return {Roo.ContentPanel} The active panel or null
39055 getActivePanel : function(){
39056 return this.activePanel;
39059 validateVisibility : function(){
39060 if(this.panels.getCount() < 1){
39061 this.updateTitle(" ");
39062 this.closeBtn.hide();
39065 if(!this.isVisible()){
39072 * Adds the passed ContentPanel(s) to this region.
39073 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39074 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39076 add : function(panel)
39078 if(arguments.length > 1){
39079 for(var i = 0, len = arguments.length; i < len; i++) {
39080 this.add(arguments[i]);
39085 // if we have not been rendered yet, then we can not really do much of this..
39086 if (!this.bodyEl) {
39087 this.unrendered_panels.push(panel);
39094 if(this.hasPanel(panel)){
39095 this.showPanel(panel);
39098 panel.setRegion(this);
39099 this.panels.add(panel);
39100 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39101 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39102 // and hide them... ???
39103 this.bodyEl.dom.appendChild(panel.getEl().dom);
39104 if(panel.background !== true){
39105 this.setActivePanel(panel);
39107 this.fireEvent("paneladded", this, panel);
39114 this.initPanelAsTab(panel);
39118 if(panel.background !== true){
39119 this.tabs.activate(panel.getEl().id);
39121 this.fireEvent("paneladded", this, panel);
39126 * Hides the tab for the specified panel.
39127 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39129 hidePanel : function(panel){
39130 if(this.tabs && (panel = this.getPanel(panel))){
39131 this.tabs.hideTab(panel.getEl().id);
39136 * Unhides the tab for a previously hidden panel.
39137 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39139 unhidePanel : function(panel){
39140 if(this.tabs && (panel = this.getPanel(panel))){
39141 this.tabs.unhideTab(panel.getEl().id);
39145 clearPanels : function(){
39146 while(this.panels.getCount() > 0){
39147 this.remove(this.panels.first());
39152 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39153 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39154 * @param {Boolean} preservePanel Overrides the config preservePanel option
39155 * @return {Roo.ContentPanel} The panel that was removed
39157 remove : function(panel, preservePanel)
39159 panel = this.getPanel(panel);
39164 this.fireEvent("beforeremove", this, panel, e);
39165 if(e.cancel === true){
39168 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39169 var panelId = panel.getId();
39170 this.panels.removeKey(panelId);
39172 document.body.appendChild(panel.getEl().dom);
39175 this.tabs.removeTab(panel.getEl().id);
39176 }else if (!preservePanel){
39177 this.bodyEl.dom.removeChild(panel.getEl().dom);
39179 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39180 var p = this.panels.first();
39181 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39182 tempEl.appendChild(p.getEl().dom);
39183 this.bodyEl.update("");
39184 this.bodyEl.dom.appendChild(p.getEl().dom);
39186 this.updateTitle(p.getTitle());
39188 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39189 this.setActivePanel(p);
39191 panel.setRegion(null);
39192 if(this.activePanel == panel){
39193 this.activePanel = null;
39195 if(this.config.autoDestroy !== false && preservePanel !== true){
39196 try{panel.destroy();}catch(e){}
39198 this.fireEvent("panelremoved", this, panel);
39203 * Returns the TabPanel component used by this region
39204 * @return {Roo.TabPanel}
39206 getTabs : function(){
39210 createTool : function(parentEl, className){
39211 var btn = Roo.DomHelper.append(parentEl, {
39213 cls: "x-layout-tools-button",
39216 cls: "roo-layout-tools-button-inner " + className,
39220 btn.addClassOnOver("roo-layout-tools-button-over");
39225 * Ext JS Library 1.1.1
39226 * Copyright(c) 2006-2007, Ext JS, LLC.
39228 * Originally Released Under LGPL - original licence link has changed is not relivant.
39231 * <script type="text/javascript">
39237 * @class Roo.SplitLayoutRegion
39238 * @extends Roo.LayoutRegion
39239 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39241 Roo.bootstrap.layout.Split = function(config){
39242 this.cursor = config.cursor;
39243 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39246 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39248 splitTip : "Drag to resize.",
39249 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39250 useSplitTips : false,
39252 applyConfig : function(config){
39253 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39256 onRender : function(ctr,pos) {
39258 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39259 if(!this.config.split){
39264 var splitEl = Roo.DomHelper.append(ctr.dom, {
39266 id: this.el.id + "-split",
39267 cls: "roo-layout-split roo-layout-split-"+this.position,
39270 /** The SplitBar for this region
39271 * @type Roo.SplitBar */
39272 // does not exist yet...
39273 Roo.log([this.position, this.orientation]);
39275 this.split = new Roo.bootstrap.SplitBar({
39276 dragElement : splitEl,
39277 resizingElement: this.el,
39278 orientation : this.orientation
39281 this.split.on("moved", this.onSplitMove, this);
39282 this.split.useShim = this.config.useShim === true;
39283 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39284 if(this.useSplitTips){
39285 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39287 //if(config.collapsible){
39288 // this.split.el.on("dblclick", this.collapse, this);
39291 if(typeof this.config.minSize != "undefined"){
39292 this.split.minSize = this.config.minSize;
39294 if(typeof this.config.maxSize != "undefined"){
39295 this.split.maxSize = this.config.maxSize;
39297 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39298 this.hideSplitter();
39303 getHMaxSize : function(){
39304 var cmax = this.config.maxSize || 10000;
39305 var center = this.mgr.getRegion("center");
39306 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39309 getVMaxSize : function(){
39310 var cmax = this.config.maxSize || 10000;
39311 var center = this.mgr.getRegion("center");
39312 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39315 onSplitMove : function(split, newSize){
39316 this.fireEvent("resized", this, newSize);
39320 * Returns the {@link Roo.SplitBar} for this region.
39321 * @return {Roo.SplitBar}
39323 getSplitBar : function(){
39328 this.hideSplitter();
39329 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39332 hideSplitter : function(){
39334 this.split.el.setLocation(-2000,-2000);
39335 this.split.el.hide();
39341 this.split.el.show();
39343 Roo.bootstrap.layout.Split.superclass.show.call(this);
39346 beforeSlide: function(){
39347 if(Roo.isGecko){// firefox overflow auto bug workaround
39348 this.bodyEl.clip();
39350 this.tabs.bodyEl.clip();
39352 if(this.activePanel){
39353 this.activePanel.getEl().clip();
39355 if(this.activePanel.beforeSlide){
39356 this.activePanel.beforeSlide();
39362 afterSlide : function(){
39363 if(Roo.isGecko){// firefox overflow auto bug workaround
39364 this.bodyEl.unclip();
39366 this.tabs.bodyEl.unclip();
39368 if(this.activePanel){
39369 this.activePanel.getEl().unclip();
39370 if(this.activePanel.afterSlide){
39371 this.activePanel.afterSlide();
39377 initAutoHide : function(){
39378 if(this.autoHide !== false){
39379 if(!this.autoHideHd){
39380 var st = new Roo.util.DelayedTask(this.slideIn, this);
39381 this.autoHideHd = {
39382 "mouseout": function(e){
39383 if(!e.within(this.el, true)){
39387 "mouseover" : function(e){
39393 this.el.on(this.autoHideHd);
39397 clearAutoHide : function(){
39398 if(this.autoHide !== false){
39399 this.el.un("mouseout", this.autoHideHd.mouseout);
39400 this.el.un("mouseover", this.autoHideHd.mouseover);
39404 clearMonitor : function(){
39405 Roo.get(document).un("click", this.slideInIf, this);
39408 // these names are backwards but not changed for compat
39409 slideOut : function(){
39410 if(this.isSlid || this.el.hasActiveFx()){
39413 this.isSlid = true;
39414 if(this.collapseBtn){
39415 this.collapseBtn.hide();
39417 this.closeBtnState = this.closeBtn.getStyle('display');
39418 this.closeBtn.hide();
39420 this.stickBtn.show();
39423 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39424 this.beforeSlide();
39425 this.el.setStyle("z-index", 10001);
39426 this.el.slideIn(this.getSlideAnchor(), {
39427 callback: function(){
39429 this.initAutoHide();
39430 Roo.get(document).on("click", this.slideInIf, this);
39431 this.fireEvent("slideshow", this);
39438 afterSlideIn : function(){
39439 this.clearAutoHide();
39440 this.isSlid = false;
39441 this.clearMonitor();
39442 this.el.setStyle("z-index", "");
39443 if(this.collapseBtn){
39444 this.collapseBtn.show();
39446 this.closeBtn.setStyle('display', this.closeBtnState);
39448 this.stickBtn.hide();
39450 this.fireEvent("slidehide", this);
39453 slideIn : function(cb){
39454 if(!this.isSlid || this.el.hasActiveFx()){
39458 this.isSlid = false;
39459 this.beforeSlide();
39460 this.el.slideOut(this.getSlideAnchor(), {
39461 callback: function(){
39462 this.el.setLeftTop(-10000, -10000);
39464 this.afterSlideIn();
39472 slideInIf : function(e){
39473 if(!e.within(this.el)){
39478 animateCollapse : function(){
39479 this.beforeSlide();
39480 this.el.setStyle("z-index", 20000);
39481 var anchor = this.getSlideAnchor();
39482 this.el.slideOut(anchor, {
39483 callback : function(){
39484 this.el.setStyle("z-index", "");
39485 this.collapsedEl.slideIn(anchor, {duration:.3});
39487 this.el.setLocation(-10000,-10000);
39489 this.fireEvent("collapsed", this);
39496 animateExpand : function(){
39497 this.beforeSlide();
39498 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39499 this.el.setStyle("z-index", 20000);
39500 this.collapsedEl.hide({
39503 this.el.slideIn(this.getSlideAnchor(), {
39504 callback : function(){
39505 this.el.setStyle("z-index", "");
39508 this.split.el.show();
39510 this.fireEvent("invalidated", this);
39511 this.fireEvent("expanded", this);
39539 getAnchor : function(){
39540 return this.anchors[this.position];
39543 getCollapseAnchor : function(){
39544 return this.canchors[this.position];
39547 getSlideAnchor : function(){
39548 return this.sanchors[this.position];
39551 getAlignAdj : function(){
39552 var cm = this.cmargins;
39553 switch(this.position){
39569 getExpandAdj : function(){
39570 var c = this.collapsedEl, cm = this.cmargins;
39571 switch(this.position){
39573 return [-(cm.right+c.getWidth()+cm.left), 0];
39576 return [cm.right+c.getWidth()+cm.left, 0];
39579 return [0, -(cm.top+cm.bottom+c.getHeight())];
39582 return [0, cm.top+cm.bottom+c.getHeight()];
39588 * Ext JS Library 1.1.1
39589 * Copyright(c) 2006-2007, Ext JS, LLC.
39591 * Originally Released Under LGPL - original licence link has changed is not relivant.
39594 * <script type="text/javascript">
39597 * These classes are private internal classes
39599 Roo.bootstrap.layout.Center = function(config){
39600 config.region = "center";
39601 Roo.bootstrap.layout.Region.call(this, config);
39602 this.visible = true;
39603 this.minWidth = config.minWidth || 20;
39604 this.minHeight = config.minHeight || 20;
39607 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39609 // center panel can't be hidden
39613 // center panel can't be hidden
39616 getMinWidth: function(){
39617 return this.minWidth;
39620 getMinHeight: function(){
39621 return this.minHeight;
39635 Roo.bootstrap.layout.North = function(config)
39637 config.region = 'north';
39638 config.cursor = 'n-resize';
39640 Roo.bootstrap.layout.Split.call(this, config);
39644 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39645 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39646 this.split.el.addClass("roo-layout-split-v");
39648 //var size = config.initialSize || config.height;
39649 //if(this.el && typeof size != "undefined"){
39650 // this.el.setHeight(size);
39653 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39655 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39658 onRender : function(ctr, pos)
39660 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39661 var size = this.config.initialSize || this.config.height;
39662 if(this.el && typeof size != "undefined"){
39663 this.el.setHeight(size);
39668 getBox : function(){
39669 if(this.collapsed){
39670 return this.collapsedEl.getBox();
39672 var box = this.el.getBox();
39674 box.height += this.split.el.getHeight();
39679 updateBox : function(box){
39680 if(this.split && !this.collapsed){
39681 box.height -= this.split.el.getHeight();
39682 this.split.el.setLeft(box.x);
39683 this.split.el.setTop(box.y+box.height);
39684 this.split.el.setWidth(box.width);
39686 if(this.collapsed){
39687 this.updateBody(box.width, null);
39689 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39697 Roo.bootstrap.layout.South = function(config){
39698 config.region = 'south';
39699 config.cursor = 's-resize';
39700 Roo.bootstrap.layout.Split.call(this, config);
39702 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39703 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39704 this.split.el.addClass("roo-layout-split-v");
39709 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39710 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39712 onRender : function(ctr, pos)
39714 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39715 var size = this.config.initialSize || this.config.height;
39716 if(this.el && typeof size != "undefined"){
39717 this.el.setHeight(size);
39722 getBox : function(){
39723 if(this.collapsed){
39724 return this.collapsedEl.getBox();
39726 var box = this.el.getBox();
39728 var sh = this.split.el.getHeight();
39735 updateBox : function(box){
39736 if(this.split && !this.collapsed){
39737 var sh = this.split.el.getHeight();
39740 this.split.el.setLeft(box.x);
39741 this.split.el.setTop(box.y-sh);
39742 this.split.el.setWidth(box.width);
39744 if(this.collapsed){
39745 this.updateBody(box.width, null);
39747 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39751 Roo.bootstrap.layout.East = function(config){
39752 config.region = "east";
39753 config.cursor = "e-resize";
39754 Roo.bootstrap.layout.Split.call(this, config);
39756 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39757 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39758 this.split.el.addClass("roo-layout-split-h");
39762 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39763 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39765 onRender : function(ctr, pos)
39767 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39768 var size = this.config.initialSize || this.config.width;
39769 if(this.el && typeof size != "undefined"){
39770 this.el.setWidth(size);
39775 getBox : function(){
39776 if(this.collapsed){
39777 return this.collapsedEl.getBox();
39779 var box = this.el.getBox();
39781 var sw = this.split.el.getWidth();
39788 updateBox : function(box){
39789 if(this.split && !this.collapsed){
39790 var sw = this.split.el.getWidth();
39792 this.split.el.setLeft(box.x);
39793 this.split.el.setTop(box.y);
39794 this.split.el.setHeight(box.height);
39797 if(this.collapsed){
39798 this.updateBody(null, box.height);
39800 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39804 Roo.bootstrap.layout.West = function(config){
39805 config.region = "west";
39806 config.cursor = "w-resize";
39808 Roo.bootstrap.layout.Split.call(this, config);
39810 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39811 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39812 this.split.el.addClass("roo-layout-split-h");
39816 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39817 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39819 onRender: function(ctr, pos)
39821 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39822 var size = this.config.initialSize || this.config.width;
39823 if(typeof size != "undefined"){
39824 this.el.setWidth(size);
39828 getBox : function(){
39829 if(this.collapsed){
39830 return this.collapsedEl.getBox();
39832 var box = this.el.getBox();
39833 if (box.width == 0) {
39834 box.width = this.config.width; // kludge?
39837 box.width += this.split.el.getWidth();
39842 updateBox : function(box){
39843 if(this.split && !this.collapsed){
39844 var sw = this.split.el.getWidth();
39846 this.split.el.setLeft(box.x+box.width);
39847 this.split.el.setTop(box.y);
39848 this.split.el.setHeight(box.height);
39850 if(this.collapsed){
39851 this.updateBody(null, box.height);
39853 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39855 });Roo.namespace("Roo.bootstrap.panel");/*
39857 * Ext JS Library 1.1.1
39858 * Copyright(c) 2006-2007, Ext JS, LLC.
39860 * Originally Released Under LGPL - original licence link has changed is not relivant.
39863 * <script type="text/javascript">
39866 * @class Roo.ContentPanel
39867 * @extends Roo.util.Observable
39868 * A basic ContentPanel element.
39869 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39870 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39871 * @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
39872 * @cfg {Boolean} closable True if the panel can be closed/removed
39873 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39874 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39875 * @cfg {Toolbar} toolbar A toolbar for this panel
39876 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39877 * @cfg {String} title The title for this panel
39878 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39879 * @cfg {String} url Calls {@link #setUrl} with this value
39880 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39881 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39882 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39883 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39884 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39885 * @cfg {Boolean} badges render the badges
39886 * @cfg {String} cls extra classes to use
39887 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39890 * Create a new ContentPanel.
39891 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39892 * @param {String/Object} config A string to set only the title or a config object
39893 * @param {String} content (optional) Set the HTML content for this panel
39894 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39896 Roo.bootstrap.panel.Content = function( config){
39898 this.tpl = config.tpl || false;
39900 var el = config.el;
39901 var content = config.content;
39903 if(config.autoCreate){ // xtype is available if this is called from factory
39906 this.el = Roo.get(el);
39907 if(!this.el && config && config.autoCreate){
39908 if(typeof config.autoCreate == "object"){
39909 if(!config.autoCreate.id){
39910 config.autoCreate.id = config.id||el;
39912 this.el = Roo.DomHelper.append(document.body,
39913 config.autoCreate, true);
39917 cls: (config.cls || '') +
39918 (config.background ? ' bg-' + config.background : '') +
39919 " roo-layout-inactive-content",
39922 if (config.iframe) {
39926 style : 'border: 0px',
39927 src : 'about:blank'
39933 elcfg.html = config.html;
39937 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39938 if (config.iframe) {
39939 this.iframeEl = this.el.select('iframe',true).first();
39944 this.closable = false;
39945 this.loaded = false;
39946 this.active = false;
39949 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39951 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39953 this.wrapEl = this.el; //this.el.wrap();
39955 if (config.toolbar.items) {
39956 ti = config.toolbar.items ;
39957 delete config.toolbar.items ;
39961 this.toolbar.render(this.wrapEl, 'before');
39962 for(var i =0;i < ti.length;i++) {
39963 // Roo.log(['add child', items[i]]);
39964 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39966 this.toolbar.items = nitems;
39967 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39968 delete config.toolbar;
39972 // xtype created footer. - not sure if will work as we normally have to render first..
39973 if (this.footer && !this.footer.el && this.footer.xtype) {
39974 if (!this.wrapEl) {
39975 this.wrapEl = this.el.wrap();
39978 this.footer.container = this.wrapEl.createChild();
39980 this.footer = Roo.factory(this.footer, Roo);
39985 if(typeof config == "string"){
39986 this.title = config;
39988 Roo.apply(this, config);
39992 this.resizeEl = Roo.get(this.resizeEl, true);
39994 this.resizeEl = this.el;
39996 // handle view.xtype
40004 * Fires when this panel is activated.
40005 * @param {Roo.ContentPanel} this
40009 * @event deactivate
40010 * Fires when this panel is activated.
40011 * @param {Roo.ContentPanel} this
40013 "deactivate" : true,
40017 * Fires when this panel is resized if fitToFrame is true.
40018 * @param {Roo.ContentPanel} this
40019 * @param {Number} width The width after any component adjustments
40020 * @param {Number} height The height after any component adjustments
40026 * Fires when this tab is created
40027 * @param {Roo.ContentPanel} this
40038 if(this.autoScroll && !this.iframe){
40039 this.resizeEl.setStyle("overflow", "auto");
40041 // fix randome scrolling
40042 //this.el.on('scroll', function() {
40043 // Roo.log('fix random scolling');
40044 // this.scrollTo('top',0);
40047 content = content || this.content;
40049 this.setContent(content);
40051 if(config && config.url){
40052 this.setUrl(this.url, this.params, this.loadOnce);
40057 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40059 if (this.view && typeof(this.view.xtype) != 'undefined') {
40060 this.view.el = this.el.appendChild(document.createElement("div"));
40061 this.view = Roo.factory(this.view);
40062 this.view.render && this.view.render(false, '');
40066 this.fireEvent('render', this);
40069 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40079 setRegion : function(region){
40080 this.region = region;
40081 this.setActiveClass(region && !this.background);
40085 setActiveClass: function(state)
40088 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40089 this.el.setStyle('position','relative');
40091 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40092 this.el.setStyle('position', 'absolute');
40097 * Returns the toolbar for this Panel if one was configured.
40098 * @return {Roo.Toolbar}
40100 getToolbar : function(){
40101 return this.toolbar;
40104 setActiveState : function(active)
40106 this.active = active;
40107 this.setActiveClass(active);
40109 if(this.fireEvent("deactivate", this) === false){
40114 this.fireEvent("activate", this);
40118 * Updates this panel's element (not for iframe)
40119 * @param {String} content The new content
40120 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40122 setContent : function(content, loadScripts){
40127 this.el.update(content, loadScripts);
40130 ignoreResize : function(w, h){
40131 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40134 this.lastSize = {width: w, height: h};
40139 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40140 * @return {Roo.UpdateManager} The UpdateManager
40142 getUpdateManager : function(){
40146 return this.el.getUpdateManager();
40149 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40150 * Does not work with IFRAME contents
40151 * @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:
40154 url: "your-url.php",
40155 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40156 callback: yourFunction,
40157 scope: yourObject, //(optional scope)
40160 text: "Loading...",
40166 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40167 * 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.
40168 * @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}
40169 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40170 * @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.
40171 * @return {Roo.ContentPanel} this
40179 var um = this.el.getUpdateManager();
40180 um.update.apply(um, arguments);
40186 * 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.
40187 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40188 * @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)
40189 * @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)
40190 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40192 setUrl : function(url, params, loadOnce){
40194 this.iframeEl.dom.src = url;
40198 if(this.refreshDelegate){
40199 this.removeListener("activate", this.refreshDelegate);
40201 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40202 this.on("activate", this.refreshDelegate);
40203 return this.el.getUpdateManager();
40206 _handleRefresh : function(url, params, loadOnce){
40207 if(!loadOnce || !this.loaded){
40208 var updater = this.el.getUpdateManager();
40209 updater.update(url, params, this._setLoaded.createDelegate(this));
40213 _setLoaded : function(){
40214 this.loaded = true;
40218 * Returns this panel's id
40221 getId : function(){
40226 * Returns this panel's element - used by regiosn to add.
40227 * @return {Roo.Element}
40229 getEl : function(){
40230 return this.wrapEl || this.el;
40235 adjustForComponents : function(width, height)
40237 //Roo.log('adjustForComponents ');
40238 if(this.resizeEl != this.el){
40239 width -= this.el.getFrameWidth('lr');
40240 height -= this.el.getFrameWidth('tb');
40243 var te = this.toolbar.getEl();
40244 te.setWidth(width);
40245 height -= te.getHeight();
40248 var te = this.footer.getEl();
40249 te.setWidth(width);
40250 height -= te.getHeight();
40254 if(this.adjustments){
40255 width += this.adjustments[0];
40256 height += this.adjustments[1];
40258 return {"width": width, "height": height};
40261 setSize : function(width, height){
40262 if(this.fitToFrame && !this.ignoreResize(width, height)){
40263 if(this.fitContainer && this.resizeEl != this.el){
40264 this.el.setSize(width, height);
40266 var size = this.adjustForComponents(width, height);
40268 this.iframeEl.setSize(width,height);
40271 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40272 this.fireEvent('resize', this, size.width, size.height);
40279 * Returns this panel's title
40282 getTitle : function(){
40284 if (typeof(this.title) != 'object') {
40289 for (var k in this.title) {
40290 if (!this.title.hasOwnProperty(k)) {
40294 if (k.indexOf('-') >= 0) {
40295 var s = k.split('-');
40296 for (var i = 0; i<s.length; i++) {
40297 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40300 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40307 * Set this panel's title
40308 * @param {String} title
40310 setTitle : function(title){
40311 this.title = title;
40313 this.region.updatePanelTitle(this, title);
40318 * Returns true is this panel was configured to be closable
40319 * @return {Boolean}
40321 isClosable : function(){
40322 return this.closable;
40325 beforeSlide : function(){
40327 this.resizeEl.clip();
40330 afterSlide : function(){
40332 this.resizeEl.unclip();
40336 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40337 * Will fail silently if the {@link #setUrl} method has not been called.
40338 * This does not activate the panel, just updates its content.
40340 refresh : function(){
40341 if(this.refreshDelegate){
40342 this.loaded = false;
40343 this.refreshDelegate();
40348 * Destroys this panel
40350 destroy : function(){
40351 this.el.removeAllListeners();
40352 var tempEl = document.createElement("span");
40353 tempEl.appendChild(this.el.dom);
40354 tempEl.innerHTML = "";
40360 * form - if the content panel contains a form - this is a reference to it.
40361 * @type {Roo.form.Form}
40365 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40366 * This contains a reference to it.
40372 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40382 * @param {Object} cfg Xtype definition of item to add.
40386 getChildContainer: function () {
40387 return this.getEl();
40392 var ret = new Roo.factory(cfg);
40397 if (cfg.xtype.match(/^Form$/)) {
40400 //if (this.footer) {
40401 // el = this.footer.container.insertSibling(false, 'before');
40403 el = this.el.createChild();
40406 this.form = new Roo.form.Form(cfg);
40409 if ( this.form.allItems.length) {
40410 this.form.render(el.dom);
40414 // should only have one of theses..
40415 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40416 // views.. should not be just added - used named prop 'view''
40418 cfg.el = this.el.appendChild(document.createElement("div"));
40421 var ret = new Roo.factory(cfg);
40423 ret.render && ret.render(false, ''); // render blank..
40433 * @class Roo.bootstrap.panel.Grid
40434 * @extends Roo.bootstrap.panel.Content
40436 * Create a new GridPanel.
40437 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40438 * @param {Object} config A the config object
40444 Roo.bootstrap.panel.Grid = function(config)
40448 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40449 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40451 config.el = this.wrapper;
40452 //this.el = this.wrapper;
40454 if (config.container) {
40455 // ctor'ed from a Border/panel.grid
40458 this.wrapper.setStyle("overflow", "hidden");
40459 this.wrapper.addClass('roo-grid-container');
40464 if(config.toolbar){
40465 var tool_el = this.wrapper.createChild();
40466 this.toolbar = Roo.factory(config.toolbar);
40468 if (config.toolbar.items) {
40469 ti = config.toolbar.items ;
40470 delete config.toolbar.items ;
40474 this.toolbar.render(tool_el);
40475 for(var i =0;i < ti.length;i++) {
40476 // Roo.log(['add child', items[i]]);
40477 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40479 this.toolbar.items = nitems;
40481 delete config.toolbar;
40484 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40485 config.grid.scrollBody = true;;
40486 config.grid.monitorWindowResize = false; // turn off autosizing
40487 config.grid.autoHeight = false;
40488 config.grid.autoWidth = false;
40490 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40492 if (config.background) {
40493 // render grid on panel activation (if panel background)
40494 this.on('activate', function(gp) {
40495 if (!gp.grid.rendered) {
40496 gp.grid.render(this.wrapper);
40497 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40502 this.grid.render(this.wrapper);
40503 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40506 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40507 // ??? needed ??? config.el = this.wrapper;
40512 // xtype created footer. - not sure if will work as we normally have to render first..
40513 if (this.footer && !this.footer.el && this.footer.xtype) {
40515 var ctr = this.grid.getView().getFooterPanel(true);
40516 this.footer.dataSource = this.grid.dataSource;
40517 this.footer = Roo.factory(this.footer, Roo);
40518 this.footer.render(ctr);
40528 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40529 getId : function(){
40530 return this.grid.id;
40534 * Returns the grid for this panel
40535 * @return {Roo.bootstrap.Table}
40537 getGrid : function(){
40541 setSize : function(width, height){
40542 if(!this.ignoreResize(width, height)){
40543 var grid = this.grid;
40544 var size = this.adjustForComponents(width, height);
40545 // tfoot is not a footer?
40548 var gridel = grid.getGridEl();
40549 gridel.setSize(size.width, size.height);
40551 var tbd = grid.getGridEl().select('tbody', true).first();
40552 var thd = grid.getGridEl().select('thead',true).first();
40553 var tbf= grid.getGridEl().select('tfoot', true).first();
40556 size.height -= tbf.getHeight();
40559 size.height -= thd.getHeight();
40562 tbd.setSize(size.width, size.height );
40563 // this is for the account management tab -seems to work there.
40564 var thd = grid.getGridEl().select('thead',true).first();
40566 // tbd.setSize(size.width, size.height - thd.getHeight());
40575 beforeSlide : function(){
40576 this.grid.getView().scroller.clip();
40579 afterSlide : function(){
40580 this.grid.getView().scroller.unclip();
40583 destroy : function(){
40584 this.grid.destroy();
40586 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40591 * @class Roo.bootstrap.panel.Nest
40592 * @extends Roo.bootstrap.panel.Content
40594 * Create a new Panel, that can contain a layout.Border.
40597 * @param {Roo.BorderLayout} layout The layout for this panel
40598 * @param {String/Object} config A string to set only the title or a config object
40600 Roo.bootstrap.panel.Nest = function(config)
40602 // construct with only one argument..
40603 /* FIXME - implement nicer consturctors
40604 if (layout.layout) {
40606 layout = config.layout;
40607 delete config.layout;
40609 if (layout.xtype && !layout.getEl) {
40610 // then layout needs constructing..
40611 layout = Roo.factory(layout, Roo);
40615 config.el = config.layout.getEl();
40617 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40619 config.layout.monitorWindowResize = false; // turn off autosizing
40620 this.layout = config.layout;
40621 this.layout.getEl().addClass("roo-layout-nested-layout");
40622 this.layout.parent = this;
40629 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40631 setSize : function(width, height){
40632 if(!this.ignoreResize(width, height)){
40633 var size = this.adjustForComponents(width, height);
40634 var el = this.layout.getEl();
40635 if (size.height < 1) {
40636 el.setWidth(size.width);
40638 el.setSize(size.width, size.height);
40640 var touch = el.dom.offsetWidth;
40641 this.layout.layout();
40642 // ie requires a double layout on the first pass
40643 if(Roo.isIE && !this.initialized){
40644 this.initialized = true;
40645 this.layout.layout();
40650 // activate all subpanels if not currently active..
40652 setActiveState : function(active){
40653 this.active = active;
40654 this.setActiveClass(active);
40657 this.fireEvent("deactivate", this);
40661 this.fireEvent("activate", this);
40662 // not sure if this should happen before or after..
40663 if (!this.layout) {
40664 return; // should not happen..
40667 for (var r in this.layout.regions) {
40668 reg = this.layout.getRegion(r);
40669 if (reg.getActivePanel()) {
40670 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40671 reg.setActivePanel(reg.getActivePanel());
40674 if (!reg.panels.length) {
40677 reg.showPanel(reg.getPanel(0));
40686 * Returns the nested BorderLayout for this panel
40687 * @return {Roo.BorderLayout}
40689 getLayout : function(){
40690 return this.layout;
40694 * Adds a xtype elements to the layout of the nested panel
40698 xtype : 'ContentPanel',
40705 xtype : 'NestedLayoutPanel',
40711 items : [ ... list of content panels or nested layout panels.. ]
40715 * @param {Object} cfg Xtype definition of item to add.
40717 addxtype : function(cfg) {
40718 return this.layout.addxtype(cfg);
40723 * Ext JS Library 1.1.1
40724 * Copyright(c) 2006-2007, Ext JS, LLC.
40726 * Originally Released Under LGPL - original licence link has changed is not relivant.
40729 * <script type="text/javascript">
40732 * @class Roo.TabPanel
40733 * @extends Roo.util.Observable
40734 * A lightweight tab container.
40738 // basic tabs 1, built from existing content
40739 var tabs = new Roo.TabPanel("tabs1");
40740 tabs.addTab("script", "View Script");
40741 tabs.addTab("markup", "View Markup");
40742 tabs.activate("script");
40744 // more advanced tabs, built from javascript
40745 var jtabs = new Roo.TabPanel("jtabs");
40746 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40748 // set up the UpdateManager
40749 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40750 var updater = tab2.getUpdateManager();
40751 updater.setDefaultUrl("ajax1.htm");
40752 tab2.on('activate', updater.refresh, updater, true);
40754 // Use setUrl for Ajax loading
40755 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40756 tab3.setUrl("ajax2.htm", null, true);
40759 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40762 jtabs.activate("jtabs-1");
40765 * Create a new TabPanel.
40766 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40767 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40769 Roo.bootstrap.panel.Tabs = function(config){
40771 * The container element for this TabPanel.
40772 * @type Roo.Element
40774 this.el = Roo.get(config.el);
40777 if(typeof config == "boolean"){
40778 this.tabPosition = config ? "bottom" : "top";
40780 Roo.apply(this, config);
40784 if(this.tabPosition == "bottom"){
40785 // if tabs are at the bottom = create the body first.
40786 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40787 this.el.addClass("roo-tabs-bottom");
40789 // next create the tabs holders
40791 if (this.tabPosition == "west"){
40793 var reg = this.region; // fake it..
40795 if (!reg.mgr.parent) {
40798 reg = reg.mgr.parent.region;
40800 Roo.log("got nest?");
40802 if (reg.mgr.getRegion('west')) {
40803 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40804 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40805 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40806 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40807 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40815 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40816 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40817 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40818 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40823 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40826 // finally - if tabs are at the top, then create the body last..
40827 if(this.tabPosition != "bottom"){
40828 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40829 * @type Roo.Element
40831 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40832 this.el.addClass("roo-tabs-top");
40836 this.bodyEl.setStyle("position", "relative");
40838 this.active = null;
40839 this.activateDelegate = this.activate.createDelegate(this);
40844 * Fires when the active tab changes
40845 * @param {Roo.TabPanel} this
40846 * @param {Roo.TabPanelItem} activePanel The new active tab
40850 * @event beforetabchange
40851 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40852 * @param {Roo.TabPanel} this
40853 * @param {Object} e Set cancel to true on this object to cancel the tab change
40854 * @param {Roo.TabPanelItem} tab The tab being changed to
40856 "beforetabchange" : true
40859 Roo.EventManager.onWindowResize(this.onResize, this);
40860 this.cpad = this.el.getPadding("lr");
40861 this.hiddenCount = 0;
40864 // toolbar on the tabbar support...
40865 if (this.toolbar) {
40866 alert("no toolbar support yet");
40867 this.toolbar = false;
40869 var tcfg = this.toolbar;
40870 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40871 this.toolbar = new Roo.Toolbar(tcfg);
40872 if (Roo.isSafari) {
40873 var tbl = tcfg.container.child('table', true);
40874 tbl.setAttribute('width', '100%');
40882 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40885 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40887 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40889 tabPosition : "top",
40891 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40893 currentTabWidth : 0,
40895 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40899 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40903 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40905 preferredTabWidth : 175,
40907 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40909 resizeTabs : false,
40911 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40913 monitorResize : true,
40915 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40917 toolbar : false, // set by caller..
40919 region : false, /// set by caller
40921 disableTooltips : true, // not used yet...
40924 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40925 * @param {String} id The id of the div to use <b>or create</b>
40926 * @param {String} text The text for the tab
40927 * @param {String} content (optional) Content to put in the TabPanelItem body
40928 * @param {Boolean} closable (optional) True to create a close icon on the tab
40929 * @return {Roo.TabPanelItem} The created TabPanelItem
40931 addTab : function(id, text, content, closable, tpl)
40933 var item = new Roo.bootstrap.panel.TabItem({
40937 closable : closable,
40940 this.addTabItem(item);
40942 item.setContent(content);
40948 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40949 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40950 * @return {Roo.TabPanelItem}
40952 getTab : function(id){
40953 return this.items[id];
40957 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40958 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40960 hideTab : function(id){
40961 var t = this.items[id];
40964 this.hiddenCount++;
40965 this.autoSizeTabs();
40970 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40971 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40973 unhideTab : function(id){
40974 var t = this.items[id];
40976 t.setHidden(false);
40977 this.hiddenCount--;
40978 this.autoSizeTabs();
40983 * Adds an existing {@link Roo.TabPanelItem}.
40984 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40986 addTabItem : function(item)
40988 this.items[item.id] = item;
40989 this.items.push(item);
40990 this.autoSizeTabs();
40991 // if(this.resizeTabs){
40992 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40993 // this.autoSizeTabs();
40995 // item.autoSize();
41000 * Removes a {@link Roo.TabPanelItem}.
41001 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41003 removeTab : function(id){
41004 var items = this.items;
41005 var tab = items[id];
41006 if(!tab) { return; }
41007 var index = items.indexOf(tab);
41008 if(this.active == tab && items.length > 1){
41009 var newTab = this.getNextAvailable(index);
41014 this.stripEl.dom.removeChild(tab.pnode.dom);
41015 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41016 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41018 items.splice(index, 1);
41019 delete this.items[tab.id];
41020 tab.fireEvent("close", tab);
41021 tab.purgeListeners();
41022 this.autoSizeTabs();
41025 getNextAvailable : function(start){
41026 var items = this.items;
41028 // look for a next tab that will slide over to
41029 // replace the one being removed
41030 while(index < items.length){
41031 var item = items[++index];
41032 if(item && !item.isHidden()){
41036 // if one isn't found select the previous tab (on the left)
41039 var item = items[--index];
41040 if(item && !item.isHidden()){
41048 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41049 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41051 disableTab : function(id){
41052 var tab = this.items[id];
41053 if(tab && this.active != tab){
41059 * Enables a {@link Roo.TabPanelItem} that is disabled.
41060 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41062 enableTab : function(id){
41063 var tab = this.items[id];
41068 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41069 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41070 * @return {Roo.TabPanelItem} The TabPanelItem.
41072 activate : function(id)
41074 //Roo.log('activite:' + id);
41076 var tab = this.items[id];
41080 if(tab == this.active || tab.disabled){
41084 this.fireEvent("beforetabchange", this, e, tab);
41085 if(e.cancel !== true && !tab.disabled){
41087 this.active.hide();
41089 this.active = this.items[id];
41090 this.active.show();
41091 this.fireEvent("tabchange", this, this.active);
41097 * Gets the active {@link Roo.TabPanelItem}.
41098 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41100 getActiveTab : function(){
41101 return this.active;
41105 * Updates the tab body element to fit the height of the container element
41106 * for overflow scrolling
41107 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41109 syncHeight : function(targetHeight){
41110 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41111 var bm = this.bodyEl.getMargins();
41112 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41113 this.bodyEl.setHeight(newHeight);
41117 onResize : function(){
41118 if(this.monitorResize){
41119 this.autoSizeTabs();
41124 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41126 beginUpdate : function(){
41127 this.updating = true;
41131 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41133 endUpdate : function(){
41134 this.updating = false;
41135 this.autoSizeTabs();
41139 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41141 autoSizeTabs : function()
41143 var count = this.items.length;
41144 var vcount = count - this.hiddenCount;
41147 this.stripEl.hide();
41149 this.stripEl.show();
41152 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41157 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41158 var availWidth = Math.floor(w / vcount);
41159 var b = this.stripBody;
41160 if(b.getWidth() > w){
41161 var tabs = this.items;
41162 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41163 if(availWidth < this.minTabWidth){
41164 /*if(!this.sleft){ // incomplete scrolling code
41165 this.createScrollButtons();
41168 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41171 if(this.currentTabWidth < this.preferredTabWidth){
41172 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41178 * Returns the number of tabs in this TabPanel.
41181 getCount : function(){
41182 return this.items.length;
41186 * Resizes all the tabs to the passed width
41187 * @param {Number} The new width
41189 setTabWidth : function(width){
41190 this.currentTabWidth = width;
41191 for(var i = 0, len = this.items.length; i < len; i++) {
41192 if(!this.items[i].isHidden()) {
41193 this.items[i].setWidth(width);
41199 * Destroys this TabPanel
41200 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41202 destroy : function(removeEl){
41203 Roo.EventManager.removeResizeListener(this.onResize, this);
41204 for(var i = 0, len = this.items.length; i < len; i++){
41205 this.items[i].purgeListeners();
41207 if(removeEl === true){
41208 this.el.update("");
41213 createStrip : function(container)
41215 var strip = document.createElement("nav");
41216 strip.className = Roo.bootstrap.version == 4 ?
41217 "navbar-light bg-light" :
41218 "navbar navbar-default"; //"x-tabs-wrap";
41219 container.appendChild(strip);
41223 createStripList : function(strip)
41225 // div wrapper for retard IE
41226 // returns the "tr" element.
41227 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41228 //'<div class="x-tabs-strip-wrap">'+
41229 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41230 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41231 return strip.firstChild; //.firstChild.firstChild.firstChild;
41233 createBody : function(container)
41235 var body = document.createElement("div");
41236 Roo.id(body, "tab-body");
41237 //Roo.fly(body).addClass("x-tabs-body");
41238 Roo.fly(body).addClass("tab-content");
41239 container.appendChild(body);
41242 createItemBody :function(bodyEl, id){
41243 var body = Roo.getDom(id);
41245 body = document.createElement("div");
41248 //Roo.fly(body).addClass("x-tabs-item-body");
41249 Roo.fly(body).addClass("tab-pane");
41250 bodyEl.insertBefore(body, bodyEl.firstChild);
41254 createStripElements : function(stripEl, text, closable, tpl)
41256 var td = document.createElement("li"); // was td..
41257 td.className = 'nav-item';
41259 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41262 stripEl.appendChild(td);
41264 td.className = "x-tabs-closable";
41265 if(!this.closeTpl){
41266 this.closeTpl = new Roo.Template(
41267 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41268 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41269 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41272 var el = this.closeTpl.overwrite(td, {"text": text});
41273 var close = el.getElementsByTagName("div")[0];
41274 var inner = el.getElementsByTagName("em")[0];
41275 return {"el": el, "close": close, "inner": inner};
41278 // not sure what this is..
41279 // if(!this.tabTpl){
41280 //this.tabTpl = new Roo.Template(
41281 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41282 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41284 // this.tabTpl = new Roo.Template(
41285 // '<a href="#">' +
41286 // '<span unselectable="on"' +
41287 // (this.disableTooltips ? '' : ' title="{text}"') +
41288 // ' >{text}</span></a>'
41294 var template = tpl || this.tabTpl || false;
41297 template = new Roo.Template(
41298 Roo.bootstrap.version == 4 ?
41300 '<a class="nav-link" href="#" unselectable="on"' +
41301 (this.disableTooltips ? '' : ' title="{text}"') +
41304 '<a class="nav-link" href="#">' +
41305 '<span unselectable="on"' +
41306 (this.disableTooltips ? '' : ' title="{text}"') +
41307 ' >{text}</span></a>'
41312 switch (typeof(template)) {
41316 template = new Roo.Template(template);
41322 var el = template.overwrite(td, {"text": text});
41324 var inner = el.getElementsByTagName("span")[0];
41326 return {"el": el, "inner": inner};
41334 * @class Roo.TabPanelItem
41335 * @extends Roo.util.Observable
41336 * Represents an individual item (tab plus body) in a TabPanel.
41337 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41338 * @param {String} id The id of this TabPanelItem
41339 * @param {String} text The text for the tab of this TabPanelItem
41340 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41342 Roo.bootstrap.panel.TabItem = function(config){
41344 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41345 * @type Roo.TabPanel
41347 this.tabPanel = config.panel;
41349 * The id for this TabPanelItem
41352 this.id = config.id;
41354 this.disabled = false;
41356 this.text = config.text;
41358 this.loaded = false;
41359 this.closable = config.closable;
41362 * The body element for this TabPanelItem.
41363 * @type Roo.Element
41365 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41366 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41367 this.bodyEl.setStyle("display", "block");
41368 this.bodyEl.setStyle("zoom", "1");
41369 //this.hideAction();
41371 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41373 this.el = Roo.get(els.el);
41374 this.inner = Roo.get(els.inner, true);
41375 this.textEl = Roo.bootstrap.version == 4 ?
41376 this.el : Roo.get(this.el.dom.firstChild, true);
41378 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41379 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41382 // this.el.on("mousedown", this.onTabMouseDown, this);
41383 this.el.on("click", this.onTabClick, this);
41385 if(config.closable){
41386 var c = Roo.get(els.close, true);
41387 c.dom.title = this.closeText;
41388 c.addClassOnOver("close-over");
41389 c.on("click", this.closeClick, this);
41395 * Fires when this tab becomes the active tab.
41396 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41397 * @param {Roo.TabPanelItem} this
41401 * @event beforeclose
41402 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41403 * @param {Roo.TabPanelItem} this
41404 * @param {Object} e Set cancel to true on this object to cancel the close.
41406 "beforeclose": true,
41409 * Fires when this tab is closed.
41410 * @param {Roo.TabPanelItem} this
41414 * @event deactivate
41415 * Fires when this tab is no longer the active tab.
41416 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41417 * @param {Roo.TabPanelItem} this
41419 "deactivate" : true
41421 this.hidden = false;
41423 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41426 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41428 purgeListeners : function(){
41429 Roo.util.Observable.prototype.purgeListeners.call(this);
41430 this.el.removeAllListeners();
41433 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41436 this.status_node.addClass("active");
41439 this.tabPanel.stripWrap.repaint();
41441 this.fireEvent("activate", this.tabPanel, this);
41445 * Returns true if this tab is the active tab.
41446 * @return {Boolean}
41448 isActive : function(){
41449 return this.tabPanel.getActiveTab() == this;
41453 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41456 this.status_node.removeClass("active");
41458 this.fireEvent("deactivate", this.tabPanel, this);
41461 hideAction : function(){
41462 this.bodyEl.hide();
41463 this.bodyEl.setStyle("position", "absolute");
41464 this.bodyEl.setLeft("-20000px");
41465 this.bodyEl.setTop("-20000px");
41468 showAction : function(){
41469 this.bodyEl.setStyle("position", "relative");
41470 this.bodyEl.setTop("");
41471 this.bodyEl.setLeft("");
41472 this.bodyEl.show();
41476 * Set the tooltip for the tab.
41477 * @param {String} tooltip The tab's tooltip
41479 setTooltip : function(text){
41480 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41481 this.textEl.dom.qtip = text;
41482 this.textEl.dom.removeAttribute('title');
41484 this.textEl.dom.title = text;
41488 onTabClick : function(e){
41489 e.preventDefault();
41490 this.tabPanel.activate(this.id);
41493 onTabMouseDown : function(e){
41494 e.preventDefault();
41495 this.tabPanel.activate(this.id);
41498 getWidth : function(){
41499 return this.inner.getWidth();
41502 setWidth : function(width){
41503 var iwidth = width - this.linode.getPadding("lr");
41504 this.inner.setWidth(iwidth);
41505 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41506 this.linode.setWidth(width);
41510 * Show or hide the tab
41511 * @param {Boolean} hidden True to hide or false to show.
41513 setHidden : function(hidden){
41514 this.hidden = hidden;
41515 this.linode.setStyle("display", hidden ? "none" : "");
41519 * Returns true if this tab is "hidden"
41520 * @return {Boolean}
41522 isHidden : function(){
41523 return this.hidden;
41527 * Returns the text for this tab
41530 getText : function(){
41534 autoSize : function(){
41535 //this.el.beginMeasure();
41536 this.textEl.setWidth(1);
41538 * #2804 [new] Tabs in Roojs
41539 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41541 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41542 //this.el.endMeasure();
41546 * Sets the text for the tab (Note: this also sets the tooltip text)
41547 * @param {String} text The tab's text and tooltip
41549 setText : function(text){
41551 this.textEl.update(text);
41552 this.setTooltip(text);
41553 //if(!this.tabPanel.resizeTabs){
41554 // this.autoSize();
41558 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41560 activate : function(){
41561 this.tabPanel.activate(this.id);
41565 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41567 disable : function(){
41568 if(this.tabPanel.active != this){
41569 this.disabled = true;
41570 this.status_node.addClass("disabled");
41575 * Enables this TabPanelItem if it was previously disabled.
41577 enable : function(){
41578 this.disabled = false;
41579 this.status_node.removeClass("disabled");
41583 * Sets the content for this TabPanelItem.
41584 * @param {String} content The content
41585 * @param {Boolean} loadScripts true to look for and load scripts
41587 setContent : function(content, loadScripts){
41588 this.bodyEl.update(content, loadScripts);
41592 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41593 * @return {Roo.UpdateManager} The UpdateManager
41595 getUpdateManager : function(){
41596 return this.bodyEl.getUpdateManager();
41600 * Set a URL to be used to load the content for this TabPanelItem.
41601 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41602 * @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)
41603 * @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)
41604 * @return {Roo.UpdateManager} The UpdateManager
41606 setUrl : function(url, params, loadOnce){
41607 if(this.refreshDelegate){
41608 this.un('activate', this.refreshDelegate);
41610 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41611 this.on("activate", this.refreshDelegate);
41612 return this.bodyEl.getUpdateManager();
41616 _handleRefresh : function(url, params, loadOnce){
41617 if(!loadOnce || !this.loaded){
41618 var updater = this.bodyEl.getUpdateManager();
41619 updater.update(url, params, this._setLoaded.createDelegate(this));
41624 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41625 * Will fail silently if the setUrl method has not been called.
41626 * This does not activate the panel, just updates its content.
41628 refresh : function(){
41629 if(this.refreshDelegate){
41630 this.loaded = false;
41631 this.refreshDelegate();
41636 _setLoaded : function(){
41637 this.loaded = true;
41641 closeClick : function(e){
41644 this.fireEvent("beforeclose", this, o);
41645 if(o.cancel !== true){
41646 this.tabPanel.removeTab(this.id);
41650 * The text displayed in the tooltip for the close icon.
41653 closeText : "Close this tab"
41656 * This script refer to:
41657 * Title: International Telephone Input
41658 * Author: Jack O'Connor
41659 * Code version: v12.1.12
41660 * Availability: https://github.com/jackocnr/intl-tel-input.git
41663 Roo.bootstrap.PhoneInputData = function() {
41666 "Afghanistan (افغانستان)",
41671 "Albania (Shqipëri)",
41676 "Algeria (الجزائر)",
41701 "Antigua and Barbuda",
41711 "Armenia (Հայաստան)",
41727 "Austria (Österreich)",
41732 "Azerbaijan (Azərbaycan)",
41742 "Bahrain (البحرين)",
41747 "Bangladesh (বাংলাদেশ)",
41757 "Belarus (Беларусь)",
41762 "Belgium (België)",
41792 "Bosnia and Herzegovina (Босна и Херцеговина)",
41807 "British Indian Ocean Territory",
41812 "British Virgin Islands",
41822 "Bulgaria (България)",
41832 "Burundi (Uburundi)",
41837 "Cambodia (កម្ពុជា)",
41842 "Cameroon (Cameroun)",
41851 ["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"]
41854 "Cape Verde (Kabu Verdi)",
41859 "Caribbean Netherlands",
41870 "Central African Republic (République centrafricaine)",
41890 "Christmas Island",
41896 "Cocos (Keeling) Islands",
41907 "Comoros (جزر القمر)",
41912 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41917 "Congo (Republic) (Congo-Brazzaville)",
41937 "Croatia (Hrvatska)",
41958 "Czech Republic (Česká republika)",
41963 "Denmark (Danmark)",
41978 "Dominican Republic (República Dominicana)",
41982 ["809", "829", "849"]
42000 "Equatorial Guinea (Guinea Ecuatorial)",
42020 "Falkland Islands (Islas Malvinas)",
42025 "Faroe Islands (Føroyar)",
42046 "French Guiana (Guyane française)",
42051 "French Polynesia (Polynésie française)",
42066 "Georgia (საქართველო)",
42071 "Germany (Deutschland)",
42091 "Greenland (Kalaallit Nunaat)",
42128 "Guinea-Bissau (Guiné Bissau)",
42153 "Hungary (Magyarország)",
42158 "Iceland (Ísland)",
42178 "Iraq (العراق)",
42194 "Israel (ישראל)",
42221 "Jordan (الأردن)",
42226 "Kazakhstan (Казахстан)",
42247 "Kuwait (الكويت)",
42252 "Kyrgyzstan (Кыргызстан)",
42262 "Latvia (Latvija)",
42267 "Lebanon (لبنان)",
42282 "Libya (ليبيا)",
42292 "Lithuania (Lietuva)",
42307 "Macedonia (FYROM) (Македонија)",
42312 "Madagascar (Madagasikara)",
42342 "Marshall Islands",
42352 "Mauritania (موريتانيا)",
42357 "Mauritius (Moris)",
42378 "Moldova (Republica Moldova)",
42388 "Mongolia (Монгол)",
42393 "Montenegro (Crna Gora)",
42403 "Morocco (المغرب)",
42409 "Mozambique (Moçambique)",
42414 "Myanmar (Burma) (မြန်မာ)",
42419 "Namibia (Namibië)",
42434 "Netherlands (Nederland)",
42439 "New Caledonia (Nouvelle-Calédonie)",
42474 "North Korea (조선 민주주의 인민 공화국)",
42479 "Northern Mariana Islands",
42495 "Pakistan (پاکستان)",
42505 "Palestine (فلسطين)",
42515 "Papua New Guinea",
42557 "Réunion (La Réunion)",
42563 "Romania (România)",
42579 "Saint Barthélemy",
42590 "Saint Kitts and Nevis",
42600 "Saint Martin (Saint-Martin (partie française))",
42606 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42611 "Saint Vincent and the Grenadines",
42626 "São Tomé and Príncipe (São Tomé e Príncipe)",
42631 "Saudi Arabia (المملكة العربية السعودية)",
42636 "Senegal (Sénégal)",
42666 "Slovakia (Slovensko)",
42671 "Slovenia (Slovenija)",
42681 "Somalia (Soomaaliya)",
42691 "South Korea (대한민국)",
42696 "South Sudan (جنوب السودان)",
42706 "Sri Lanka (ශ්රී ලංකාව)",
42711 "Sudan (السودان)",
42721 "Svalbard and Jan Mayen",
42732 "Sweden (Sverige)",
42737 "Switzerland (Schweiz)",
42742 "Syria (سوريا)",
42787 "Trinidad and Tobago",
42792 "Tunisia (تونس)",
42797 "Turkey (Türkiye)",
42807 "Turks and Caicos Islands",
42817 "U.S. Virgin Islands",
42827 "Ukraine (Україна)",
42832 "United Arab Emirates (الإمارات العربية المتحدة)",
42854 "Uzbekistan (Oʻzbekiston)",
42864 "Vatican City (Città del Vaticano)",
42875 "Vietnam (Việt Nam)",
42880 "Wallis and Futuna (Wallis-et-Futuna)",
42885 "Western Sahara (الصحراء الغربية)",
42891 "Yemen (اليمن)",
42915 * This script refer to:
42916 * Title: International Telephone Input
42917 * Author: Jack O'Connor
42918 * Code version: v12.1.12
42919 * Availability: https://github.com/jackocnr/intl-tel-input.git
42923 * @class Roo.bootstrap.PhoneInput
42924 * @extends Roo.bootstrap.TriggerField
42925 * An input with International dial-code selection
42927 * @cfg {String} defaultDialCode default '+852'
42928 * @cfg {Array} preferedCountries default []
42931 * Create a new PhoneInput.
42932 * @param {Object} config Configuration options
42935 Roo.bootstrap.PhoneInput = function(config) {
42936 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42939 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42941 listWidth: undefined,
42943 selectedClass: 'active',
42945 invalidClass : "has-warning",
42947 validClass: 'has-success',
42949 allowed: '0123456789',
42954 * @cfg {String} defaultDialCode The default dial code when initializing the input
42956 defaultDialCode: '+852',
42959 * @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
42961 preferedCountries: false,
42963 getAutoCreate : function()
42965 var data = Roo.bootstrap.PhoneInputData();
42966 var align = this.labelAlign || this.parentLabelAlign();
42969 this.allCountries = [];
42970 this.dialCodeMapping = [];
42972 for (var i = 0; i < data.length; i++) {
42974 this.allCountries[i] = {
42978 priority: c[3] || 0,
42979 areaCodes: c[4] || null
42981 this.dialCodeMapping[c[2]] = {
42984 priority: c[3] || 0,
42985 areaCodes: c[4] || null
42997 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42998 maxlength: this.max_length,
42999 cls : 'form-control tel-input',
43000 autocomplete: 'new-password'
43003 var hiddenInput = {
43006 cls: 'hidden-tel-input'
43010 hiddenInput.name = this.name;
43013 if (this.disabled) {
43014 input.disabled = true;
43017 var flag_container = {
43034 cls: this.hasFeedback ? 'has-feedback' : '',
43040 cls: 'dial-code-holder',
43047 cls: 'roo-select2-container input-group',
43054 if (this.fieldLabel.length) {
43057 tooltip: 'This field is required'
43063 cls: 'control-label',
43069 html: this.fieldLabel
43072 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43078 if(this.indicatorpos == 'right') {
43079 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43086 if(align == 'left') {
43094 if(this.labelWidth > 12){
43095 label.style = "width: " + this.labelWidth + 'px';
43097 if(this.labelWidth < 13 && this.labelmd == 0){
43098 this.labelmd = this.labelWidth;
43100 if(this.labellg > 0){
43101 label.cls += ' col-lg-' + this.labellg;
43102 input.cls += ' col-lg-' + (12 - this.labellg);
43104 if(this.labelmd > 0){
43105 label.cls += ' col-md-' + this.labelmd;
43106 container.cls += ' col-md-' + (12 - this.labelmd);
43108 if(this.labelsm > 0){
43109 label.cls += ' col-sm-' + this.labelsm;
43110 container.cls += ' col-sm-' + (12 - this.labelsm);
43112 if(this.labelxs > 0){
43113 label.cls += ' col-xs-' + this.labelxs;
43114 container.cls += ' col-xs-' + (12 - this.labelxs);
43124 var settings = this;
43126 ['xs','sm','md','lg'].map(function(size){
43127 if (settings[size]) {
43128 cfg.cls += ' col-' + size + '-' + settings[size];
43132 this.store = new Roo.data.Store({
43133 proxy : new Roo.data.MemoryProxy({}),
43134 reader : new Roo.data.JsonReader({
43145 'name' : 'dialCode',
43149 'name' : 'priority',
43153 'name' : 'areaCodes',
43160 if(!this.preferedCountries) {
43161 this.preferedCountries = [
43168 var p = this.preferedCountries.reverse();
43171 for (var i = 0; i < p.length; i++) {
43172 for (var j = 0; j < this.allCountries.length; j++) {
43173 if(this.allCountries[j].iso2 == p[i]) {
43174 var t = this.allCountries[j];
43175 this.allCountries.splice(j,1);
43176 this.allCountries.unshift(t);
43182 this.store.proxy.data = {
43184 data: this.allCountries
43190 initEvents : function()
43193 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43195 this.indicator = this.indicatorEl();
43196 this.flag = this.flagEl();
43197 this.dialCodeHolder = this.dialCodeHolderEl();
43199 this.trigger = this.el.select('div.flag-box',true).first();
43200 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43205 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43206 _this.list.setWidth(lw);
43209 this.list.on('mouseover', this.onViewOver, this);
43210 this.list.on('mousemove', this.onViewMove, this);
43211 this.inputEl().on("keyup", this.onKeyUp, this);
43212 this.inputEl().on("keypress", this.onKeyPress, this);
43214 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43216 this.view = new Roo.View(this.list, this.tpl, {
43217 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43220 this.view.on('click', this.onViewClick, this);
43221 this.setValue(this.defaultDialCode);
43224 onTriggerClick : function(e)
43226 Roo.log('trigger click');
43231 if(this.isExpanded()){
43233 this.hasFocus = false;
43235 this.store.load({});
43236 this.hasFocus = true;
43241 isExpanded : function()
43243 return this.list.isVisible();
43246 collapse : function()
43248 if(!this.isExpanded()){
43252 Roo.get(document).un('mousedown', this.collapseIf, this);
43253 Roo.get(document).un('mousewheel', this.collapseIf, this);
43254 this.fireEvent('collapse', this);
43258 expand : function()
43262 if(this.isExpanded() || !this.hasFocus){
43266 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43267 this.list.setWidth(lw);
43270 this.restrictHeight();
43272 Roo.get(document).on('mousedown', this.collapseIf, this);
43273 Roo.get(document).on('mousewheel', this.collapseIf, this);
43275 this.fireEvent('expand', this);
43278 restrictHeight : function()
43280 this.list.alignTo(this.inputEl(), this.listAlign);
43281 this.list.alignTo(this.inputEl(), this.listAlign);
43284 onViewOver : function(e, t)
43286 if(this.inKeyMode){
43289 var item = this.view.findItemFromChild(t);
43292 var index = this.view.indexOf(item);
43293 this.select(index, false);
43298 onViewClick : function(view, doFocus, el, e)
43300 var index = this.view.getSelectedIndexes()[0];
43302 var r = this.store.getAt(index);
43305 this.onSelect(r, index);
43307 if(doFocus !== false && !this.blockFocus){
43308 this.inputEl().focus();
43312 onViewMove : function(e, t)
43314 this.inKeyMode = false;
43317 select : function(index, scrollIntoView)
43319 this.selectedIndex = index;
43320 this.view.select(index);
43321 if(scrollIntoView !== false){
43322 var el = this.view.getNode(index);
43324 this.list.scrollChildIntoView(el, false);
43329 createList : function()
43331 this.list = Roo.get(document.body).createChild({
43333 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43334 style: 'display:none'
43337 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43340 collapseIf : function(e)
43342 var in_combo = e.within(this.el);
43343 var in_list = e.within(this.list);
43344 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43346 if (in_combo || in_list || is_list) {
43352 onSelect : function(record, index)
43354 if(this.fireEvent('beforeselect', this, record, index) !== false){
43356 this.setFlagClass(record.data.iso2);
43357 this.setDialCode(record.data.dialCode);
43358 this.hasFocus = false;
43360 this.fireEvent('select', this, record, index);
43364 flagEl : function()
43366 var flag = this.el.select('div.flag',true).first();
43373 dialCodeHolderEl : function()
43375 var d = this.el.select('input.dial-code-holder',true).first();
43382 setDialCode : function(v)
43384 this.dialCodeHolder.dom.value = '+'+v;
43387 setFlagClass : function(n)
43389 this.flag.dom.className = 'flag '+n;
43392 getValue : function()
43394 var v = this.inputEl().getValue();
43395 if(this.dialCodeHolder) {
43396 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43401 setValue : function(v)
43403 var d = this.getDialCode(v);
43405 //invalid dial code
43406 if(v.length == 0 || !d || d.length == 0) {
43408 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43409 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43415 this.setFlagClass(this.dialCodeMapping[d].iso2);
43416 this.setDialCode(d);
43417 this.inputEl().dom.value = v.replace('+'+d,'');
43418 this.hiddenEl().dom.value = this.getValue();
43423 getDialCode : function(v)
43427 if (v.length == 0) {
43428 return this.dialCodeHolder.dom.value;
43432 if (v.charAt(0) != "+") {
43435 var numericChars = "";
43436 for (var i = 1; i < v.length; i++) {
43437 var c = v.charAt(i);
43440 if (this.dialCodeMapping[numericChars]) {
43441 dialCode = v.substr(1, i);
43443 if (numericChars.length == 4) {
43453 this.setValue(this.defaultDialCode);
43457 hiddenEl : function()
43459 return this.el.select('input.hidden-tel-input',true).first();
43462 // after setting val
43463 onKeyUp : function(e){
43464 this.setValue(this.getValue());
43467 onKeyPress : function(e){
43468 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43475 * @class Roo.bootstrap.MoneyField
43476 * @extends Roo.bootstrap.ComboBox
43477 * Bootstrap MoneyField class
43480 * Create a new MoneyField.
43481 * @param {Object} config Configuration options
43484 Roo.bootstrap.MoneyField = function(config) {
43486 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43490 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43493 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43495 allowDecimals : true,
43497 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43499 decimalSeparator : ".",
43501 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43503 decimalPrecision : 0,
43505 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43507 allowNegative : true,
43509 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43513 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43515 minValue : Number.NEGATIVE_INFINITY,
43517 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43519 maxValue : Number.MAX_VALUE,
43521 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43523 minText : "The minimum value for this field is {0}",
43525 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43527 maxText : "The maximum value for this field is {0}",
43529 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43530 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43532 nanText : "{0} is not a valid number",
43534 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43538 * @cfg {String} defaults currency of the MoneyField
43539 * value should be in lkey
43541 defaultCurrency : false,
43543 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43545 thousandsDelimiter : false,
43547 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43558 getAutoCreate : function()
43560 var align = this.labelAlign || this.parentLabelAlign();
43572 cls : 'form-control roo-money-amount-input',
43573 autocomplete: 'new-password'
43576 var hiddenInput = {
43580 cls: 'hidden-number-input'
43583 if(this.max_length) {
43584 input.maxlength = this.max_length;
43588 hiddenInput.name = this.name;
43591 if (this.disabled) {
43592 input.disabled = true;
43595 var clg = 12 - this.inputlg;
43596 var cmd = 12 - this.inputmd;
43597 var csm = 12 - this.inputsm;
43598 var cxs = 12 - this.inputxs;
43602 cls : 'row roo-money-field',
43606 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43610 cls: 'roo-select2-container input-group',
43614 cls : 'form-control roo-money-currency-input',
43615 autocomplete: 'new-password',
43617 name : this.currencyName
43621 cls : 'input-group-addon',
43635 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43639 cls: this.hasFeedback ? 'has-feedback' : '',
43650 if (this.fieldLabel.length) {
43653 tooltip: 'This field is required'
43659 cls: 'control-label',
43665 html: this.fieldLabel
43668 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43674 if(this.indicatorpos == 'right') {
43675 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43682 if(align == 'left') {
43690 if(this.labelWidth > 12){
43691 label.style = "width: " + this.labelWidth + 'px';
43693 if(this.labelWidth < 13 && this.labelmd == 0){
43694 this.labelmd = this.labelWidth;
43696 if(this.labellg > 0){
43697 label.cls += ' col-lg-' + this.labellg;
43698 input.cls += ' col-lg-' + (12 - this.labellg);
43700 if(this.labelmd > 0){
43701 label.cls += ' col-md-' + this.labelmd;
43702 container.cls += ' col-md-' + (12 - this.labelmd);
43704 if(this.labelsm > 0){
43705 label.cls += ' col-sm-' + this.labelsm;
43706 container.cls += ' col-sm-' + (12 - this.labelsm);
43708 if(this.labelxs > 0){
43709 label.cls += ' col-xs-' + this.labelxs;
43710 container.cls += ' col-xs-' + (12 - this.labelxs);
43721 var settings = this;
43723 ['xs','sm','md','lg'].map(function(size){
43724 if (settings[size]) {
43725 cfg.cls += ' col-' + size + '-' + settings[size];
43732 initEvents : function()
43734 this.indicator = this.indicatorEl();
43736 this.initCurrencyEvent();
43738 this.initNumberEvent();
43741 initCurrencyEvent : function()
43744 throw "can not find store for combo";
43747 this.store = Roo.factory(this.store, Roo.data);
43748 this.store.parent = this;
43752 this.triggerEl = this.el.select('.input-group-addon', true).first();
43754 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43759 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43760 _this.list.setWidth(lw);
43763 this.list.on('mouseover', this.onViewOver, this);
43764 this.list.on('mousemove', this.onViewMove, this);
43765 this.list.on('scroll', this.onViewScroll, this);
43768 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43771 this.view = new Roo.View(this.list, this.tpl, {
43772 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43775 this.view.on('click', this.onViewClick, this);
43777 this.store.on('beforeload', this.onBeforeLoad, this);
43778 this.store.on('load', this.onLoad, this);
43779 this.store.on('loadexception', this.onLoadException, this);
43781 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43782 "up" : function(e){
43783 this.inKeyMode = true;
43787 "down" : function(e){
43788 if(!this.isExpanded()){
43789 this.onTriggerClick();
43791 this.inKeyMode = true;
43796 "enter" : function(e){
43799 if(this.fireEvent("specialkey", this, e)){
43800 this.onViewClick(false);
43806 "esc" : function(e){
43810 "tab" : function(e){
43813 if(this.fireEvent("specialkey", this, e)){
43814 this.onViewClick(false);
43822 doRelay : function(foo, bar, hname){
43823 if(hname == 'down' || this.scope.isExpanded()){
43824 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43832 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43836 initNumberEvent : function(e)
43838 this.inputEl().on("keydown" , this.fireKey, this);
43839 this.inputEl().on("focus", this.onFocus, this);
43840 this.inputEl().on("blur", this.onBlur, this);
43842 this.inputEl().relayEvent('keyup', this);
43844 if(this.indicator){
43845 this.indicator.addClass('invisible');
43848 this.originalValue = this.getValue();
43850 if(this.validationEvent == 'keyup'){
43851 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43852 this.inputEl().on('keyup', this.filterValidation, this);
43854 else if(this.validationEvent !== false){
43855 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43858 if(this.selectOnFocus){
43859 this.on("focus", this.preFocus, this);
43862 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43863 this.inputEl().on("keypress", this.filterKeys, this);
43865 this.inputEl().relayEvent('keypress', this);
43868 var allowed = "0123456789";
43870 if(this.allowDecimals){
43871 allowed += this.decimalSeparator;
43874 if(this.allowNegative){
43878 if(this.thousandsDelimiter) {
43882 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43884 var keyPress = function(e){
43886 var k = e.getKey();
43888 var c = e.getCharCode();
43891 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43892 allowed.indexOf(String.fromCharCode(c)) === -1
43898 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43902 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43907 this.inputEl().on("keypress", keyPress, this);
43911 onTriggerClick : function(e)
43918 this.loadNext = false;
43920 if(this.isExpanded()){
43925 this.hasFocus = true;
43927 if(this.triggerAction == 'all') {
43928 this.doQuery(this.allQuery, true);
43932 this.doQuery(this.getRawValue());
43935 getCurrency : function()
43937 var v = this.currencyEl().getValue();
43942 restrictHeight : function()
43944 this.list.alignTo(this.currencyEl(), this.listAlign);
43945 this.list.alignTo(this.currencyEl(), this.listAlign);
43948 onViewClick : function(view, doFocus, el, e)
43950 var index = this.view.getSelectedIndexes()[0];
43952 var r = this.store.getAt(index);
43955 this.onSelect(r, index);
43959 onSelect : function(record, index){
43961 if(this.fireEvent('beforeselect', this, record, index) !== false){
43963 this.setFromCurrencyData(index > -1 ? record.data : false);
43967 this.fireEvent('select', this, record, index);
43971 setFromCurrencyData : function(o)
43975 this.lastCurrency = o;
43977 if (this.currencyField) {
43978 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43980 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43983 this.lastSelectionText = currency;
43985 //setting default currency
43986 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43987 this.setCurrency(this.defaultCurrency);
43991 this.setCurrency(currency);
43994 setFromData : function(o)
43998 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44000 this.setFromCurrencyData(c);
44005 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44007 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44010 this.setValue(value);
44014 setCurrency : function(v)
44016 this.currencyValue = v;
44019 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44024 setValue : function(v)
44026 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44032 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44034 this.inputEl().dom.value = (v == '') ? '' :
44035 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44037 if(!this.allowZero && v === '0') {
44038 this.hiddenEl().dom.value = '';
44039 this.inputEl().dom.value = '';
44046 getRawValue : function()
44048 var v = this.inputEl().getValue();
44053 getValue : function()
44055 return this.fixPrecision(this.parseValue(this.getRawValue()));
44058 parseValue : function(value)
44060 if(this.thousandsDelimiter) {
44062 r = new RegExp(",", "g");
44063 value = value.replace(r, "");
44066 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44067 return isNaN(value) ? '' : value;
44071 fixPrecision : function(value)
44073 if(this.thousandsDelimiter) {
44075 r = new RegExp(",", "g");
44076 value = value.replace(r, "");
44079 var nan = isNaN(value);
44081 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44082 return nan ? '' : value;
44084 return parseFloat(value).toFixed(this.decimalPrecision);
44087 decimalPrecisionFcn : function(v)
44089 return Math.floor(v);
44092 validateValue : function(value)
44094 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44098 var num = this.parseValue(value);
44101 this.markInvalid(String.format(this.nanText, value));
44105 if(num < this.minValue){
44106 this.markInvalid(String.format(this.minText, this.minValue));
44110 if(num > this.maxValue){
44111 this.markInvalid(String.format(this.maxText, this.maxValue));
44118 validate : function()
44120 if(this.disabled || this.allowBlank){
44125 var currency = this.getCurrency();
44127 if(this.validateValue(this.getRawValue()) && currency.length){
44132 this.markInvalid();
44136 getName: function()
44141 beforeBlur : function()
44147 var v = this.parseValue(this.getRawValue());
44154 onBlur : function()
44158 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44159 //this.el.removeClass(this.focusClass);
44162 this.hasFocus = false;
44164 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44168 var v = this.getValue();
44170 if(String(v) !== String(this.startValue)){
44171 this.fireEvent('change', this, v, this.startValue);
44174 this.fireEvent("blur", this);
44177 inputEl : function()
44179 return this.el.select('.roo-money-amount-input', true).first();
44182 currencyEl : function()
44184 return this.el.select('.roo-money-currency-input', true).first();
44187 hiddenEl : function()
44189 return this.el.select('input.hidden-number-input',true).first();
44193 * @class Roo.bootstrap.BezierSignature
44194 * @extends Roo.bootstrap.Component
44195 * Bootstrap BezierSignature class
44196 * This script refer to:
44197 * Title: Signature Pad
44199 * Availability: https://github.com/szimek/signature_pad
44202 * Create a new BezierSignature
44203 * @param {Object} config The config object
44206 Roo.bootstrap.BezierSignature = function(config){
44207 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44213 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44220 mouse_btn_down: true,
44223 * @cfg {int} canvas height
44225 canvas_height: '200px',
44228 * @cfg {float|function} Radius of a single dot.
44233 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44238 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44243 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44248 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44253 * @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.
44255 bg_color: 'rgba(0, 0, 0, 0)',
44258 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44260 dot_color: 'black',
44263 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44265 velocity_filter_weight: 0.7,
44268 * @cfg {function} Callback when stroke begin.
44273 * @cfg {function} Callback when stroke end.
44277 getAutoCreate : function()
44279 var cls = 'roo-signature column';
44282 cls += ' ' + this.cls;
44292 for(var i = 0; i < col_sizes.length; i++) {
44293 if(this[col_sizes[i]]) {
44294 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44304 cls: 'roo-signature-body',
44308 cls: 'roo-signature-body-canvas',
44309 height: this.canvas_height,
44310 width: this.canvas_width
44317 style: 'display: none'
44325 initEvents: function()
44327 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44329 var canvas = this.canvasEl();
44331 // mouse && touch event swapping...
44332 canvas.dom.style.touchAction = 'none';
44333 canvas.dom.style.msTouchAction = 'none';
44335 this.mouse_btn_down = false;
44336 canvas.on('mousedown', this._handleMouseDown, this);
44337 canvas.on('mousemove', this._handleMouseMove, this);
44338 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44340 if (window.PointerEvent) {
44341 canvas.on('pointerdown', this._handleMouseDown, this);
44342 canvas.on('pointermove', this._handleMouseMove, this);
44343 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44346 if ('ontouchstart' in window) {
44347 canvas.on('touchstart', this._handleTouchStart, this);
44348 canvas.on('touchmove', this._handleTouchMove, this);
44349 canvas.on('touchend', this._handleTouchEnd, this);
44352 Roo.EventManager.onWindowResize(this.resize, this, true);
44354 // file input event
44355 this.fileEl().on('change', this.uploadImage, this);
44362 resize: function(){
44364 var canvas = this.canvasEl().dom;
44365 var ctx = this.canvasElCtx();
44366 var img_data = false;
44368 if(canvas.width > 0) {
44369 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44371 // setting canvas width will clean img data
44374 var style = window.getComputedStyle ?
44375 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44377 var padding_left = parseInt(style.paddingLeft) || 0;
44378 var padding_right = parseInt(style.paddingRight) || 0;
44380 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44383 ctx.putImageData(img_data, 0, 0);
44387 _handleMouseDown: function(e)
44389 if (e.browserEvent.which === 1) {
44390 this.mouse_btn_down = true;
44391 this.strokeBegin(e);
44395 _handleMouseMove: function (e)
44397 if (this.mouse_btn_down) {
44398 this.strokeMoveUpdate(e);
44402 _handleMouseUp: function (e)
44404 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44405 this.mouse_btn_down = false;
44410 _handleTouchStart: function (e) {
44412 e.preventDefault();
44413 if (e.browserEvent.targetTouches.length === 1) {
44414 // var touch = e.browserEvent.changedTouches[0];
44415 // this.strokeBegin(touch);
44417 this.strokeBegin(e); // assume e catching the correct xy...
44421 _handleTouchMove: function (e) {
44422 e.preventDefault();
44423 // var touch = event.targetTouches[0];
44424 // _this._strokeMoveUpdate(touch);
44425 this.strokeMoveUpdate(e);
44428 _handleTouchEnd: function (e) {
44429 var wasCanvasTouched = e.target === this.canvasEl().dom;
44430 if (wasCanvasTouched) {
44431 e.preventDefault();
44432 // var touch = event.changedTouches[0];
44433 // _this._strokeEnd(touch);
44438 reset: function () {
44439 this._lastPoints = [];
44440 this._lastVelocity = 0;
44441 this._lastWidth = (this.min_width + this.max_width) / 2;
44442 this.canvasElCtx().fillStyle = this.dot_color;
44445 strokeMoveUpdate: function(e)
44447 this.strokeUpdate(e);
44449 if (this.throttle) {
44450 this.throttleStroke(this.strokeUpdate, this.throttle);
44453 this.strokeUpdate(e);
44457 strokeBegin: function(e)
44459 var newPointGroup = {
44460 color: this.dot_color,
44464 if (typeof this.onBegin === 'function') {
44468 this.curve_data.push(newPointGroup);
44470 this.strokeUpdate(e);
44473 strokeUpdate: function(e)
44475 var rect = this.canvasEl().dom.getBoundingClientRect();
44476 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44477 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44478 var lastPoints = lastPointGroup.points;
44479 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44480 var isLastPointTooClose = lastPoint
44481 ? point.distanceTo(lastPoint) <= this.min_distance
44483 var color = lastPointGroup.color;
44484 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44485 var curve = this.addPoint(point);
44487 this.drawDot({color: color, point: point});
44490 this.drawCurve({color: color, curve: curve});
44500 strokeEnd: function(e)
44502 this.strokeUpdate(e);
44503 if (typeof this.onEnd === 'function') {
44508 addPoint: function (point) {
44509 var _lastPoints = this._lastPoints;
44510 _lastPoints.push(point);
44511 if (_lastPoints.length > 2) {
44512 if (_lastPoints.length === 3) {
44513 _lastPoints.unshift(_lastPoints[0]);
44515 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44516 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44517 _lastPoints.shift();
44523 calculateCurveWidths: function (startPoint, endPoint) {
44524 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44525 (1 - this.velocity_filter_weight) * this._lastVelocity;
44527 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44530 start: this._lastWidth
44533 this._lastVelocity = velocity;
44534 this._lastWidth = newWidth;
44538 drawDot: function (_a) {
44539 var color = _a.color, point = _a.point;
44540 var ctx = this.canvasElCtx();
44541 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44543 this.drawCurveSegment(point.x, point.y, width);
44545 ctx.fillStyle = color;
44549 drawCurve: function (_a) {
44550 var color = _a.color, curve = _a.curve;
44551 var ctx = this.canvasElCtx();
44552 var widthDelta = curve.endWidth - curve.startWidth;
44553 var drawSteps = Math.floor(curve.length()) * 2;
44555 ctx.fillStyle = color;
44556 for (var i = 0; i < drawSteps; i += 1) {
44557 var t = i / drawSteps;
44563 var x = uuu * curve.startPoint.x;
44564 x += 3 * uu * t * curve.control1.x;
44565 x += 3 * u * tt * curve.control2.x;
44566 x += ttt * curve.endPoint.x;
44567 var y = uuu * curve.startPoint.y;
44568 y += 3 * uu * t * curve.control1.y;
44569 y += 3 * u * tt * curve.control2.y;
44570 y += ttt * curve.endPoint.y;
44571 var width = curve.startWidth + ttt * widthDelta;
44572 this.drawCurveSegment(x, y, width);
44578 drawCurveSegment: function (x, y, width) {
44579 var ctx = this.canvasElCtx();
44581 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44582 this.is_empty = false;
44587 var ctx = this.canvasElCtx();
44588 var canvas = this.canvasEl().dom;
44589 ctx.fillStyle = this.bg_color;
44590 ctx.clearRect(0, 0, canvas.width, canvas.height);
44591 ctx.fillRect(0, 0, canvas.width, canvas.height);
44592 this.curve_data = [];
44594 this.is_empty = true;
44599 return this.el.select('input',true).first();
44602 canvasEl: function()
44604 return this.el.select('canvas',true).first();
44607 canvasElCtx: function()
44609 return this.el.select('canvas',true).first().dom.getContext('2d');
44612 getImage: function(type)
44614 if(this.is_empty) {
44619 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44622 drawFromImage: function(img_src)
44624 var img = new Image();
44626 img.onload = function(){
44627 this.canvasElCtx().drawImage(img, 0, 0);
44632 this.is_empty = false;
44635 selectImage: function()
44637 this.fileEl().dom.click();
44640 uploadImage: function(e)
44642 var reader = new FileReader();
44644 reader.onload = function(e){
44645 var img = new Image();
44646 img.onload = function(){
44648 this.canvasElCtx().drawImage(img, 0, 0);
44650 img.src = e.target.result;
44653 reader.readAsDataURL(e.target.files[0]);
44656 // Bezier Point Constructor
44657 Point: (function () {
44658 function Point(x, y, time) {
44661 this.time = time || Date.now();
44663 Point.prototype.distanceTo = function (start) {
44664 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44666 Point.prototype.equals = function (other) {
44667 return this.x === other.x && this.y === other.y && this.time === other.time;
44669 Point.prototype.velocityFrom = function (start) {
44670 return this.time !== start.time
44671 ? this.distanceTo(start) / (this.time - start.time)
44678 // Bezier Constructor
44679 Bezier: (function () {
44680 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44681 this.startPoint = startPoint;
44682 this.control2 = control2;
44683 this.control1 = control1;
44684 this.endPoint = endPoint;
44685 this.startWidth = startWidth;
44686 this.endWidth = endWidth;
44688 Bezier.fromPoints = function (points, widths, scope) {
44689 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44690 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44691 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44693 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44694 var dx1 = s1.x - s2.x;
44695 var dy1 = s1.y - s2.y;
44696 var dx2 = s2.x - s3.x;
44697 var dy2 = s2.y - s3.y;
44698 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44699 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44700 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44701 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44702 var dxm = m1.x - m2.x;
44703 var dym = m1.y - m2.y;
44704 var k = l2 / (l1 + l2);
44705 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44706 var tx = s2.x - cm.x;
44707 var ty = s2.y - cm.y;
44709 c1: new scope.Point(m1.x + tx, m1.y + ty),
44710 c2: new scope.Point(m2.x + tx, m2.y + ty)
44713 Bezier.prototype.length = function () {
44718 for (var i = 0; i <= steps; i += 1) {
44720 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44721 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44723 var xdiff = cx - px;
44724 var ydiff = cy - py;
44725 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44732 Bezier.prototype.point = function (t, start, c1, c2, end) {
44733 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44734 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44735 + (3.0 * c2 * (1.0 - t) * t * t)
44736 + (end * t * t * t);
44741 throttleStroke: function(fn, wait) {
44742 if (wait === void 0) { wait = 250; }
44744 var timeout = null;
44748 var later = function () {
44749 previous = Date.now();
44751 result = fn.apply(storedContext, storedArgs);
44753 storedContext = null;
44757 return function wrapper() {
44759 for (var _i = 0; _i < arguments.length; _i++) {
44760 args[_i] = arguments[_i];
44762 var now = Date.now();
44763 var remaining = wait - (now - previous);
44764 storedContext = this;
44766 if (remaining <= 0 || remaining > wait) {
44768 clearTimeout(timeout);
44772 result = fn.apply(storedContext, storedArgs);
44774 storedContext = null;
44778 else if (!timeout) {
44779 timeout = window.setTimeout(later, remaining);