2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
366 * Fetch the element to display the tooltip on.
367 * @return {Roo.Element} defaults to this.el
369 tooltipEl : function()
374 addxtype : function(tree,cntr)
378 cn = Roo.factory(tree);
379 //Roo.log(['addxtype', cn]);
381 cn.parentType = this.xtype; //??
382 cn.parentId = this.id;
384 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385 if (typeof(cn.container_method) == 'string') {
386 cntr = cn.container_method;
390 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
392 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
394 var build_from_html = Roo.XComponent.build_from_html;
396 var is_body = (tree.xtype == 'Body') ;
398 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
400 var self_cntr_el = Roo.get(this[cntr](false));
402 // do not try and build conditional elements
403 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
407 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409 return this.addxtypeChild(tree,cntr, is_body);
412 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
415 return this.addxtypeChild(Roo.apply({}, tree),cntr);
418 Roo.log('skipping render');
424 if (!build_from_html) {
428 // this i think handles overlaying multiple children of the same type
429 // with the sam eelement.. - which might be buggy..
431 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
441 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448 addxtypeChild : function (tree, cntr, is_body)
450 Roo.debug && Roo.log('addxtypeChild:' + cntr);
452 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
455 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456 (typeof(tree['flexy:foreach']) != 'undefined');
460 skip_children = false;
461 // render the element if it's not BODY.
464 // if parent was disabled, then do not try and create the children..
465 if(!this[cntr](true)){
470 cn = Roo.factory(tree);
472 cn.parentType = this.xtype; //??
473 cn.parentId = this.id;
475 var build_from_html = Roo.XComponent.build_from_html;
478 // does the container contain child eleemnts with 'xtype' attributes.
479 // that match this xtype..
480 // note - when we render we create these as well..
481 // so we should check to see if body has xtype set.
482 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
484 var self_cntr_el = Roo.get(this[cntr](false));
485 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
487 //Roo.log(Roo.XComponent.build_from_html);
488 //Roo.log("got echild:");
491 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492 // and are not displayed -this causes this to use up the wrong element when matching.
493 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
496 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503 //echild.dom.removeAttribute('xtype');
505 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506 Roo.debug && Roo.log(self_cntr_el);
507 Roo.debug && Roo.log(echild);
508 Roo.debug && Roo.log(cn);
514 // if object has flexy:if - then it may or may not be rendered.
515 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
516 // skip a flexy if element.
517 Roo.debug && Roo.log('skipping render');
518 Roo.debug && Roo.log(tree);
520 Roo.debug && Roo.log('skipping all children');
521 skip_children = true;
526 // actually if flexy:foreach is found, we really want to create
527 // multiple copies here...
529 //Roo.log(this[cntr]());
530 // some elements do not have render methods.. like the layouts...
532 if(this[cntr](true) === false){
537 cn.render && cn.render(this[cntr](true));
540 // then add the element..
547 if (typeof (tree.menu) != 'undefined') {
548 tree.menu.parentType = cn.xtype;
549 tree.menu.triggerEl = cn.el;
550 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
554 if (!tree.items || !tree.items.length) {
556 //Roo.log(["no children", this]);
561 var items = tree.items;
564 //Roo.log(items.length);
566 if (!skip_children) {
567 for(var i =0;i < items.length;i++) {
568 // Roo.log(['add child', items[i]]);
569 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575 //Roo.log("fire childrenrendered");
577 cn.fireEvent('childrenrendered', this);
583 * Set the element that will be used to show or hide
585 setVisibilityEl : function(el)
587 this.visibilityEl = el;
591 * Get the element that will be used to show or hide
593 getVisibilityEl : function()
595 if (typeof(this.visibilityEl) == 'object') {
596 return this.visibilityEl;
599 if (typeof(this.visibilityEl) == 'string') {
600 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607 * Show a component - removes 'hidden' class
611 if(!this.getVisibilityEl()){
615 this.getVisibilityEl().removeClass(['hidden','d-none']);
617 this.fireEvent('show', this);
622 * Hide a component - adds 'hidden' class
626 if(!this.getVisibilityEl()){
630 this.getVisibilityEl().addClass(['hidden','d-none']);
632 this.fireEvent('hide', this);
645 * @class Roo.bootstrap.Element
646 * @extends Roo.bootstrap.Component
647 * Bootstrap Element class
648 * @cfg {String} html contents of the element
649 * @cfg {String} tag tag of the element
650 * @cfg {String} cls class of the element
651 * @cfg {Boolean} preventDefault (true|false) default false
652 * @cfg {Boolean} clickable (true|false) default false
655 * Create a new Element
656 * @param {Object} config The config object
659 Roo.bootstrap.Element = function(config){
660 Roo.bootstrap.Element.superclass.constructor.call(this, config);
666 * When a element is chick
667 * @param {Roo.bootstrap.Element} this
668 * @param {Roo.EventObject} e
676 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
681 preventDefault: false,
685 getAutoCreate : function(){
689 // cls: this.cls, double assign in parent class Component.js :: onRender
696 initEvents: function()
698 Roo.bootstrap.Element.superclass.initEvents.call(this);
701 this.el.on('click', this.onClick, this);
707 onClick : function(e)
709 if(this.preventDefault){
713 this.fireEvent('dblclick', this, e);
721 getValue : function()
723 return this.el.dom.innerHTML;
726 setValue : function(value)
728 this.el.dom.innerHTML = value;
743 * @class Roo.bootstrap.DropTarget
744 * @extends Roo.bootstrap.Element
745 * Bootstrap DropTarget class
747 * @cfg {string} name dropable name
750 * Create a new Dropable Area
751 * @param {Object} config The config object
754 Roo.bootstrap.DropTarget = function(config){
755 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
761 * When a element is chick
762 * @param {Roo.bootstrap.Element} this
763 * @param {Roo.EventObject} e
769 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
772 getAutoCreate : function(){
777 initEvents: function()
779 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
780 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
783 drop : this.dragDrop.createDelegate(this),
784 enter : this.dragEnter.createDelegate(this),
785 out : this.dragOut.createDelegate(this),
786 over : this.dragOver.createDelegate(this)
790 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
793 dragDrop : function(source,e,data)
795 // user has to decide how to impliment this.
798 //this.fireEvent('drop', this, source, e ,data);
802 dragEnter : function(n, dd, e, data)
804 // probably want to resize the element to match the dropped element..
806 this.originalSize = this.el.getSize();
807 this.el.setSize( n.el.getSize());
808 this.dropZone.DDM.refreshCache(this.name);
809 Roo.log([n, dd, e, data]);
812 dragOut : function(value)
814 // resize back to normal
816 this.el.setSize(this.originalSize);
817 this.dropZone.resetConstraints();
820 dragOver : function()
837 * @class Roo.bootstrap.Body
838 * @extends Roo.bootstrap.Component
839 * Bootstrap Body class
843 * @param {Object} config The config object
846 Roo.bootstrap.Body = function(config){
848 config = config || {};
850 Roo.bootstrap.Body.superclass.constructor.call(this, config);
851 this.el = Roo.get(config.el ? config.el : document.body );
852 if (this.cls && this.cls.length) {
853 Roo.get(document.body).addClass(this.cls);
857 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
859 is_body : true,// just to make sure it's constructed?
864 onRender : function(ct, position)
866 /* Roo.log("Roo.bootstrap.Body - onRender");
867 if (this.cls && this.cls.length) {
868 Roo.get(document.body).addClass(this.cls);
887 * @class Roo.bootstrap.ButtonGroup
888 * @extends Roo.bootstrap.Component
889 * Bootstrap ButtonGroup class
890 * @cfg {String} size lg | sm | xs (default empty normal)
891 * @cfg {String} align vertical | justified (default none)
892 * @cfg {String} direction up | down (default down)
893 * @cfg {Boolean} toolbar false | true
894 * @cfg {Boolean} btn true | false
899 * @param {Object} config The config object
902 Roo.bootstrap.ButtonGroup = function(config){
903 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
906 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
914 getAutoCreate : function(){
920 cfg.html = this.html || cfg.html;
931 if (['vertical','justified'].indexOf(this.align)!==-1) {
932 cfg.cls = 'btn-group-' + this.align;
934 if (this.align == 'justified') {
935 console.log(this.items);
939 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
940 cfg.cls += ' btn-group-' + this.size;
943 if (this.direction == 'up') {
944 cfg.cls += ' dropup' ;
950 * Add a button to the group (similar to NavItem API.)
952 addItem : function(cfg)
954 var cn = new Roo.bootstrap.Button(cfg);
956 cn.parentId = this.id;
957 cn.onRender(this.el, null);
971 * @class Roo.bootstrap.Button
972 * @extends Roo.bootstrap.Component
973 * Bootstrap Button class
974 * @cfg {String} html The button content
975 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
976 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
977 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
978 * @cfg {String} size (lg|sm|xs)
979 * @cfg {String} tag (a|input|submit)
980 * @cfg {String} href empty or href
981 * @cfg {Boolean} disabled default false;
982 * @cfg {Boolean} isClose default false;
983 * @cfg {String} glyphicon depricated - use fa
984 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
985 * @cfg {String} badge text for badge
986 * @cfg {String} theme (default|glow)
987 * @cfg {Boolean} inverse dark themed version
988 * @cfg {Boolean} toggle is it a slidy toggle button
989 * @cfg {Boolean} pressed default null - if the button ahs active state
990 * @cfg {String} ontext text for on slidy toggle state
991 * @cfg {String} offtext text for off slidy toggle state
992 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
993 * @cfg {Boolean} removeClass remove the standard class..
994 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
995 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
998 * Create a new button
999 * @param {Object} config The config object
1003 Roo.bootstrap.Button = function(config){
1004 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1010 * When a button is pressed
1011 * @param {Roo.bootstrap.Button} btn
1012 * @param {Roo.EventObject} e
1017 * When a button is double clicked
1018 * @param {Roo.bootstrap.Button} btn
1019 * @param {Roo.EventObject} e
1024 * After the button has been toggles
1025 * @param {Roo.bootstrap.Button} btn
1026 * @param {Roo.EventObject} e
1027 * @param {boolean} pressed (also available as button.pressed)
1033 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1054 preventDefault: true,
1063 getAutoCreate : function(){
1071 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1072 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1073 this.tag = 'button';
1077 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1079 if (this.toggle == true) {
1082 cls: 'slider-frame roo-button',
1086 'data-on-text':'ON',
1087 'data-off-text':'OFF',
1088 cls: 'slider-button',
1093 // why are we validating the weights?
1094 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1095 cfg.cls += ' ' + this.weight;
1102 cfg.cls += ' close';
1104 cfg["aria-hidden"] = true;
1106 cfg.html = "×";
1112 if (this.theme==='default') {
1113 cfg.cls = 'btn roo-button';
1115 //if (this.parentType != 'Navbar') {
1116 this.weight = this.weight.length ? this.weight : 'default';
1118 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1120 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1121 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1122 cfg.cls += ' btn-' + outline + weight;
1123 if (this.weight == 'default') {
1125 cfg.cls += ' btn-' + this.weight;
1128 } else if (this.theme==='glow') {
1131 cfg.cls = 'btn-glow roo-button';
1133 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1135 cfg.cls += ' ' + this.weight;
1141 this.cls += ' inverse';
1145 if (this.active || this.pressed === true) {
1146 cfg.cls += ' active';
1149 if (this.disabled) {
1150 cfg.disabled = 'disabled';
1154 Roo.log('changing to ul' );
1156 this.glyphicon = 'caret';
1157 if (Roo.bootstrap.version == 4) {
1158 this.fa = 'caret-down';
1163 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1165 //gsRoo.log(this.parentType);
1166 if (this.parentType === 'Navbar' && !this.parent().bar) {
1167 Roo.log('changing to li?');
1176 href : this.href || '#'
1179 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1180 cfg.cls += ' dropdown';
1187 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1189 if (this.glyphicon) {
1190 cfg.html = ' ' + cfg.html;
1195 cls: 'glyphicon glyphicon-' + this.glyphicon
1200 cfg.html = ' ' + cfg.html;
1205 cls: 'fa fas fa-' + this.fa
1215 // cfg.cls='btn roo-button';
1219 var value = cfg.html;
1224 cls: 'glyphicon glyphicon-' + this.glyphicon,
1231 cls: 'fa fas fa-' + this.fa,
1236 var bw = this.badge_weight.length ? this.badge_weight :
1237 (this.weight.length ? this.weight : 'secondary');
1238 bw = bw == 'default' ? 'secondary' : bw;
1244 cls: 'badge badge-' + bw,
1253 cfg.cls += ' dropdown';
1254 cfg.html = typeof(cfg.html) != 'undefined' ?
1255 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1258 if (cfg.tag !== 'a' && this.href !== '') {
1259 throw "Tag must be a to set href.";
1260 } else if (this.href.length > 0) {
1261 cfg.href = this.href;
1264 if(this.removeClass){
1269 cfg.target = this.target;
1274 initEvents: function() {
1275 // Roo.log('init events?');
1276 // Roo.log(this.el.dom);
1279 if (typeof (this.menu) != 'undefined') {
1280 this.menu.parentType = this.xtype;
1281 this.menu.triggerEl = this.el;
1282 this.addxtype(Roo.apply({}, this.menu));
1286 if (this.el.hasClass('roo-button')) {
1287 this.el.on('click', this.onClick, this);
1288 this.el.on('dblclick', this.onDblClick, this);
1290 this.el.select('.roo-button').on('click', this.onClick, this);
1291 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1295 if(this.removeClass){
1296 this.el.on('click', this.onClick, this);
1299 if (this.group === true) {
1300 if (this.pressed === false || this.pressed === true) {
1303 this.pressed = false;
1304 this.setActive(this.pressed);
1309 this.el.enableDisplayMode();
1312 onClick : function(e)
1314 if (this.disabled) {
1318 Roo.log('button on click ');
1319 if(this.preventDefault){
1328 this.setActive(true);
1329 var pi = this.parent().items;
1330 for (var i = 0;i < pi.length;i++) {
1331 if (this == pi[i]) {
1334 if (pi[i].el.hasClass('roo-button')) {
1335 pi[i].setActive(false);
1338 this.fireEvent('click', this, e);
1342 if (this.pressed === true || this.pressed === false) {
1343 this.toggleActive(e);
1347 this.fireEvent('click', this, e);
1349 onDblClick: function(e)
1351 if (this.disabled) {
1354 if(this.preventDefault){
1357 this.fireEvent('dblclick', this, e);
1360 * Enables this button
1364 this.disabled = false;
1365 this.el.removeClass('disabled');
1366 this.el.dom.removeAttribute("disabled");
1370 * Disable this button
1372 disable : function()
1374 this.disabled = true;
1375 this.el.addClass('disabled');
1376 this.el.attr("disabled", "disabled")
1379 * sets the active state on/off,
1380 * @param {Boolean} state (optional) Force a particular state
1382 setActive : function(v) {
1384 this.el[v ? 'addClass' : 'removeClass']('active');
1388 * toggles the current active state
1390 toggleActive : function(e)
1392 this.setActive(!this.pressed); // this modifies pressed...
1393 this.fireEvent('toggle', this, e, this.pressed);
1396 * get the current active state
1397 * @return {boolean} true if it's active
1399 isActive : function()
1401 return this.el.hasClass('active');
1404 * set the text of the first selected button
1406 setText : function(str)
1408 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1411 * get the text of the first selected button
1413 getText : function()
1415 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1418 setWeight : function(str)
1420 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1421 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1423 var outline = this.outline ? 'outline-' : '';
1424 if (str == 'default') {
1425 this.el.addClass('btn-default btn-outline-secondary');
1428 this.el.addClass('btn-' + outline + str);
1433 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1435 Roo.bootstrap.Button.weights = [
1455 * @class Roo.bootstrap.Column
1456 * @extends Roo.bootstrap.Component
1457 * Bootstrap Column class
1458 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1459 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1460 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1461 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1462 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1463 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1464 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1465 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1468 * @cfg {Boolean} hidden (true|false) hide the element
1469 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1470 * @cfg {String} fa (ban|check|...) font awesome icon
1471 * @cfg {Number} fasize (1|2|....) font awsome size
1473 * @cfg {String} icon (info-sign|check|...) glyphicon name
1475 * @cfg {String} html content of column.
1478 * Create a new Column
1479 * @param {Object} config The config object
1482 Roo.bootstrap.Column = function(config){
1483 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1486 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1504 getAutoCreate : function(){
1505 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1513 var sizes = ['xs','sm','md','lg'];
1514 sizes.map(function(size ,ix){
1515 //Roo.log( size + ':' + settings[size]);
1517 if (settings[size+'off'] !== false) {
1518 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1521 if (settings[size] === false) {
1525 if (!settings[size]) { // 0 = hidden
1526 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1528 for (var i = ix; i > -1; i--) {
1529 cfg.cls += ' d-' + sizes[i] + '-none';
1535 cfg.cls += ' col-' + size + '-' + settings[size] + (
1536 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1542 cfg.cls += ' hidden';
1545 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1546 cfg.cls +=' alert alert-' + this.alert;
1550 if (this.html.length) {
1551 cfg.html = this.html;
1555 if (this.fasize > 1) {
1556 fasize = ' fa-' + this.fasize + 'x';
1558 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1563 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1582 * @class Roo.bootstrap.Container
1583 * @extends Roo.bootstrap.Component
1584 * Bootstrap Container class
1585 * @cfg {Boolean} jumbotron is it a jumbotron element
1586 * @cfg {String} html content of element
1587 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1588 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1589 * @cfg {String} header content of header (for panel)
1590 * @cfg {String} footer content of footer (for panel)
1591 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1592 * @cfg {String} tag (header|aside|section) type of HTML tag.
1593 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1594 * @cfg {String} fa font awesome icon
1595 * @cfg {String} icon (info-sign|check|...) glyphicon name
1596 * @cfg {Boolean} hidden (true|false) hide the element
1597 * @cfg {Boolean} expandable (true|false) default false
1598 * @cfg {Boolean} expanded (true|false) default true
1599 * @cfg {String} rheader contet on the right of header
1600 * @cfg {Boolean} clickable (true|false) default false
1604 * Create a new Container
1605 * @param {Object} config The config object
1608 Roo.bootstrap.Container = function(config){
1609 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1615 * After the panel has been expand
1617 * @param {Roo.bootstrap.Container} this
1622 * After the panel has been collapsed
1624 * @param {Roo.bootstrap.Container} this
1629 * When a element is chick
1630 * @param {Roo.bootstrap.Container} this
1631 * @param {Roo.EventObject} e
1637 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1655 getChildContainer : function() {
1661 if (this.panel.length) {
1662 return this.el.select('.panel-body',true).first();
1669 getAutoCreate : function(){
1672 tag : this.tag || 'div',
1676 if (this.jumbotron) {
1677 cfg.cls = 'jumbotron';
1682 // - this is applied by the parent..
1684 // cfg.cls = this.cls + '';
1687 if (this.sticky.length) {
1689 var bd = Roo.get(document.body);
1690 if (!bd.hasClass('bootstrap-sticky')) {
1691 bd.addClass('bootstrap-sticky');
1692 Roo.select('html',true).setStyle('height', '100%');
1695 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1699 if (this.well.length) {
1700 switch (this.well) {
1703 cfg.cls +=' well well-' +this.well;
1712 cfg.cls += ' hidden';
1716 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1717 cfg.cls +=' alert alert-' + this.alert;
1722 if (this.panel.length) {
1723 cfg.cls += ' panel panel-' + this.panel;
1725 if (this.header.length) {
1729 if(this.expandable){
1731 cfg.cls = cfg.cls + ' expandable';
1735 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1743 cls : 'panel-title',
1744 html : (this.expandable ? ' ' : '') + this.header
1748 cls: 'panel-header-right',
1754 cls : 'panel-heading',
1755 style : this.expandable ? 'cursor: pointer' : '',
1763 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1768 if (this.footer.length) {
1770 cls : 'panel-footer',
1779 body.html = this.html || cfg.html;
1780 // prefix with the icons..
1782 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1785 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1790 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1791 cfg.cls = 'container';
1797 initEvents: function()
1799 if(this.expandable){
1800 var headerEl = this.headerEl();
1803 headerEl.on('click', this.onToggleClick, this);
1808 this.el.on('click', this.onClick, this);
1813 onToggleClick : function()
1815 var headerEl = this.headerEl();
1831 if(this.fireEvent('expand', this)) {
1833 this.expanded = true;
1835 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1837 this.el.select('.panel-body',true).first().removeClass('hide');
1839 var toggleEl = this.toggleEl();
1845 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1850 collapse : function()
1852 if(this.fireEvent('collapse', this)) {
1854 this.expanded = false;
1856 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1857 this.el.select('.panel-body',true).first().addClass('hide');
1859 var toggleEl = this.toggleEl();
1865 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1869 toggleEl : function()
1871 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1875 return this.el.select('.panel-heading .fa',true).first();
1878 headerEl : function()
1880 if(!this.el || !this.panel.length || !this.header.length){
1884 return this.el.select('.panel-heading',true).first()
1889 if(!this.el || !this.panel.length){
1893 return this.el.select('.panel-body',true).first()
1896 titleEl : function()
1898 if(!this.el || !this.panel.length || !this.header.length){
1902 return this.el.select('.panel-title',true).first();
1905 setTitle : function(v)
1907 var titleEl = this.titleEl();
1913 titleEl.dom.innerHTML = v;
1916 getTitle : function()
1919 var titleEl = this.titleEl();
1925 return titleEl.dom.innerHTML;
1928 setRightTitle : function(v)
1930 var t = this.el.select('.panel-header-right',true).first();
1936 t.dom.innerHTML = v;
1939 onClick : function(e)
1943 this.fireEvent('click', this, e);
1950 * This is BS4's Card element.. - similar to our containers probably..
1954 * @class Roo.bootstrap.Card
1955 * @extends Roo.bootstrap.Component
1956 * Bootstrap Card class
1959 * possible... may not be implemented..
1960 * @cfg {String} header_image src url of image.
1961 * @cfg {String|Object} header
1962 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1963 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1965 * @cfg {String} title
1966 * @cfg {String} subtitle
1967 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1968 * @cfg {String} footer
1970 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1972 * @cfg {String} margin (0|1|2|3|4|5|auto)
1973 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1974 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1975 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1976 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1977 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1978 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1980 * @cfg {String} padding (0|1|2|3|4|5)
1981 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1982 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1983 * @cfg {String} padding_left (0|1|2|3|4|5)
1984 * @cfg {String} padding_right (0|1|2|3|4|5)
1985 * @cfg {String} padding_x (0|1|2|3|4|5)
1986 * @cfg {String} padding_y (0|1|2|3|4|5)
1988 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1989 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1990 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1991 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1992 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1994 * @config {Boolean} dragable if this card can be dragged.
1995 * @config {String} drag_group group for drag
1996 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1997 * @config {String} drop_group group for drag
1999 * @config {Boolean} collapsable can the body be collapsed.
2000 * @config {Boolean} collapsed is the body collapsed when rendered...
2001 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2002 * @config {Boolean} rotated is the body rotated when rendered...
2005 * Create a new Container
2006 * @param {Object} config The config object
2009 Roo.bootstrap.Card = function(config){
2010 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2016 * When a element a card is dropped
2017 * @param {Roo.bootstrap.Card} this
2020 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2021 * @param {String} position 'above' or 'below'
2022 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2028 * When a element a card is rotate
2029 * @param {Roo.bootstrap.Card} this
2030 * @param {Roo.Element} n the node being dropped?
2031 * @param {Boolean} rotate status
2036 * When a card element is dragged over ready to drop (return false to block dropable)
2037 * @param {Roo.bootstrap.Card} this
2038 * @param {Object} data from dragdrop
2046 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2051 margin: '', /// may be better in component?
2081 collapsable : false,
2090 childContainer : false,
2091 dropEl : false, /// the dom placeholde element that indicates drop location.
2092 containerEl: false, // body container
2093 bodyEl: false, // card-body
2094 headerContainerEl : false, //
2096 header_imageEl : false,
2099 layoutCls : function()
2103 Roo.log(this.margin_bottom.length);
2104 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2105 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2107 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2108 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2110 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2111 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2115 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2116 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2117 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2121 // more generic support?
2129 // Roo.log("Call onRender: " + this.xtype);
2130 /* We are looking at something like this.
2132 <img src="..." class="card-img-top" alt="...">
2133 <div class="card-body">
2134 <h5 class="card-title">Card title</h5>
2135 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2137 >> this bit is really the body...
2138 <div> << we will ad dthis in hopefully it will not break shit.
2140 ** card text does not actually have any styling...
2142 <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>
2145 <a href="#" class="card-link">Card link</a>
2148 <div class="card-footer">
2149 <small class="text-muted">Last updated 3 mins ago</small>
2153 getAutoCreate : function(){
2161 if (this.weight.length && this.weight != 'light') {
2162 cfg.cls += ' text-white';
2164 cfg.cls += ' text-dark'; // need as it's nested..
2166 if (this.weight.length) {
2167 cfg.cls += ' bg-' + this.weight;
2170 cfg.cls += ' ' + this.layoutCls();
2173 var hdr_ctr = false;
2174 if (this.header.length) {
2176 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2177 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2185 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2191 if (this.collapsable) {
2194 cls : 'd-block user-select-none',
2198 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2203 hdr.cn.push(hdr_ctr);
2208 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2213 if (this.header_image.length) {
2216 cls : 'card-img-top',
2217 src: this.header_image // escape?
2222 cls : 'card-img-top d-none'
2228 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2232 if (this.collapsable || this.rotateable) {
2235 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2242 if (this.title.length) {
2246 src: this.title // escape?
2250 if (this.subtitle.length) {
2254 src: this.subtitle // escape?
2260 cls : 'roo-card-body-ctr'
2263 if (this.html.length) {
2269 // fixme ? handle objects?
2271 if (this.footer.length) {
2274 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2279 cfg.cn.push({cls : 'card-footer d-none'});
2288 getCardHeader : function()
2290 var ret = this.el.select('.card-header',true).first();
2291 if (ret.hasClass('d-none')) {
2292 ret.removeClass('d-none');
2297 getCardFooter : function()
2299 var ret = this.el.select('.card-footer',true).first();
2300 if (ret.hasClass('d-none')) {
2301 ret.removeClass('d-none');
2306 getCardImageTop : function()
2308 var ret = this.header_imageEl;
2309 if (ret.hasClass('d-none')) {
2310 ret.removeClass('d-none');
2316 getChildContainer : function()
2322 return this.el.select('.roo-card-body-ctr',true).first();
2325 initEvents: function()
2327 this.bodyEl = this.el.select('.card-body',true).first();
2328 this.containerEl = this.getChildContainer();
2330 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2331 containerScroll: true,
2332 ddGroup: this.drag_group || 'default_card_drag_group'
2334 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2336 if (this.dropable) {
2337 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2338 containerScroll: true,
2339 ddGroup: this.drop_group || 'default_card_drag_group'
2341 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2342 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2343 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2344 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2345 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2348 if (this.collapsable) {
2349 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2351 if (this.rotateable) {
2352 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2354 this.collapsableEl = this.el.select('.roo-collapsable').first();
2356 this.footerEl = this.el.select('.card-footer',true).first();
2357 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2358 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2359 this.headerEl = this.el.select('.card-header',true).first();
2362 this.el.addClass('roo-card-rotated');
2363 this.fireEvent('rotate', this, true);
2365 this.header_imageEl = this.el.select('.card-img-top',true).first();
2366 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2369 getDragData : function(e)
2371 var target = this.getEl();
2373 //this.handleSelection(e);
2378 nodes: this.getEl(),
2383 dragData.ddel = target.dom ; // the div element
2384 Roo.log(target.getWidth( ));
2385 dragData.ddel.style.width = target.getWidth() + 'px';
2392 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2393 * whole Element becomes the target, and this causes the drop gesture to append.
2395 * Returns an object:
2398 position : 'below' or 'above'
2399 card : relateive to card OBJECT (or true for no cards listed)
2400 items_n : relative to nth item in list
2401 card_n : relative to nth card in list
2406 getTargetFromEvent : function(e, dragged_card_el)
2408 var target = e.getTarget();
2409 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2410 target = target.parentNode;
2421 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2422 // see if target is one of the 'cards'...
2425 //Roo.log(this.items.length);
2428 var last_card_n = 0;
2430 for (var i = 0;i< this.items.length;i++) {
2432 if (!this.items[i].el.hasClass('card')) {
2435 pos = this.getDropPoint(e, this.items[i].el.dom);
2437 cards_len = ret.cards.length;
2438 //Roo.log(this.items[i].el.dom.id);
2439 ret.cards.push(this.items[i]);
2441 if (ret.card_n < 0 && pos == 'above') {
2442 ret.position = cards_len > 0 ? 'below' : pos;
2443 ret.items_n = i > 0 ? i - 1 : 0;
2444 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2445 ret.card = ret.cards[ret.card_n];
2448 if (!ret.cards.length) {
2450 ret.position = 'below';
2454 // could not find a card.. stick it at the end..
2455 if (ret.card_n < 0) {
2456 ret.card_n = last_card_n;
2457 ret.card = ret.cards[last_card_n];
2458 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2459 ret.position = 'below';
2462 if (this.items[ret.items_n].el == dragged_card_el) {
2466 if (ret.position == 'below') {
2467 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2469 if (card_after && card_after.el == dragged_card_el) {
2476 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2478 if (card_before && card_before.el == dragged_card_el) {
2485 onNodeEnter : function(n, dd, e, data){
2488 onNodeOver : function(n, dd, e, data)
2491 var target_info = this.getTargetFromEvent(e,data.source.el);
2492 if (target_info === false) {
2493 this.dropPlaceHolder('hide');
2496 Roo.log(['getTargetFromEvent', target_info ]);
2499 if (this.fireEvent('cardover', this, [ data ]) === false) {
2503 this.dropPlaceHolder('show', target_info,data);
2507 onNodeOut : function(n, dd, e, data){
2508 this.dropPlaceHolder('hide');
2511 onNodeDrop : function(n, dd, e, data)
2514 // call drop - return false if
2516 // this could actually fail - if the Network drops..
2517 // we will ignore this at present..- client should probably reload
2518 // the whole set of cards if stuff like that fails.
2521 var info = this.getTargetFromEvent(e,data.source.el);
2522 if (info === false) {
2525 this.dropPlaceHolder('hide');
2529 this.acceptCard(data.source, info.position, info.card, info.items_n);
2533 firstChildCard : function()
2535 for (var i = 0;i< this.items.length;i++) {
2537 if (!this.items[i].el.hasClass('card')) {
2540 return this.items[i];
2542 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2547 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2549 acceptCard : function(move_card, position, next_to_card )
2551 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2555 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2557 move_card.parent().removeCard(move_card);
2560 var dom = move_card.el.dom;
2561 dom.style.width = ''; // clear with - which is set by drag.
2563 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2564 var cardel = next_to_card.el.dom;
2566 if (position == 'above' ) {
2567 cardel.parentNode.insertBefore(dom, cardel);
2568 } else if (cardel.nextSibling) {
2569 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2571 cardel.parentNode.append(dom);
2574 // card container???
2575 this.containerEl.dom.append(dom);
2578 //FIXME HANDLE card = true
2580 // add this to the correct place in items.
2582 // remove Card from items.
2585 if (this.items.length) {
2587 //Roo.log([info.items_n, info.position, this.items.length]);
2588 for (var i =0; i < this.items.length; i++) {
2589 if (i == to_items_n && position == 'above') {
2590 nitems.push(move_card);
2592 nitems.push(this.items[i]);
2593 if (i == to_items_n && position == 'below') {
2594 nitems.push(move_card);
2597 this.items = nitems;
2598 Roo.log(this.items);
2600 this.items.push(move_card);
2603 move_card.parentId = this.id;
2609 removeCard : function(c)
2611 this.items = this.items.filter(function(e) { return e != c });
2614 dom.parentNode.removeChild(dom);
2615 dom.style.width = ''; // clear with - which is set by drag.
2620 /** Decide whether to drop above or below a View node. */
2621 getDropPoint : function(e, n, dd)
2626 if (n == this.containerEl.dom) {
2629 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2630 var c = t + (b - t) / 2;
2631 var y = Roo.lib.Event.getPageY(e);
2638 onToggleCollapse : function(e)
2640 if (this.collapsed) {
2641 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2642 this.collapsableEl.addClass('show');
2643 this.collapsed = false;
2646 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2647 this.collapsableEl.removeClass('show');
2648 this.collapsed = true;
2653 onToggleRotate : function(e)
2655 this.collapsableEl.removeClass('show');
2656 this.footerEl.removeClass('d-none');
2657 this.el.removeClass('roo-card-rotated');
2658 this.el.removeClass('d-none');
2661 this.collapsableEl.addClass('show');
2662 this.rotated = false;
2663 this.fireEvent('rotate', this, this.rotated);
2666 this.el.addClass('roo-card-rotated');
2667 this.footerEl.addClass('d-none');
2668 this.el.select('.roo-collapsable').removeClass('show');
2670 this.rotated = true;
2671 this.fireEvent('rotate', this, this.rotated);
2675 dropPlaceHolder: function (action, info, data)
2677 if (this.dropEl === false) {
2678 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2682 this.dropEl.removeClass(['d-none', 'd-block']);
2683 if (action == 'hide') {
2685 this.dropEl.addClass('d-none');
2688 // FIXME - info.card == true!!!
2689 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2691 if (info.card !== true) {
2692 var cardel = info.card.el.dom;
2694 if (info.position == 'above') {
2695 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2696 } else if (cardel.nextSibling) {
2697 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2699 cardel.parentNode.append(this.dropEl.dom);
2702 // card container???
2703 this.containerEl.dom.append(this.dropEl.dom);
2706 this.dropEl.addClass('d-block roo-card-dropzone');
2708 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2715 setHeaderText: function(html)
2718 if (this.headerContainerEl) {
2719 this.headerContainerEl.dom.innerHTML = html;
2722 onHeaderImageLoad : function(ev, he)
2724 if (!this.header_image_fit_square) {
2728 var hw = he.naturalHeight / he.naturalWidth;
2731 //var w = he.dom.naturalWidth;
2734 he.style.position = 'relative';
2736 var nw = (ww * (1/hw));
2737 Roo.get(he).setSize( ww * (1/hw), ww);
2738 he.style.left = ((ww - nw)/ 2) + 'px';
2739 he.style.position = 'relative';
2750 * Card header - holder for the card header elements.
2755 * @class Roo.bootstrap.CardHeader
2756 * @extends Roo.bootstrap.Element
2757 * Bootstrap CardHeader class
2759 * Create a new Card Header - that you can embed children into
2760 * @param {Object} config The config object
2763 Roo.bootstrap.CardHeader = function(config){
2764 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2767 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2770 container_method : 'getCardHeader'
2783 * Card footer - holder for the card footer elements.
2788 * @class Roo.bootstrap.CardFooter
2789 * @extends Roo.bootstrap.Element
2790 * Bootstrap CardFooter class
2792 * Create a new Card Footer - that you can embed children into
2793 * @param {Object} config The config object
2796 Roo.bootstrap.CardFooter = function(config){
2797 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2800 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2803 container_method : 'getCardFooter'
2816 * Card header - holder for the card header elements.
2821 * @class Roo.bootstrap.CardImageTop
2822 * @extends Roo.bootstrap.Element
2823 * Bootstrap CardImageTop class
2825 * Create a new Card Image Top container
2826 * @param {Object} config The config object
2829 Roo.bootstrap.CardImageTop = function(config){
2830 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2833 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2836 container_method : 'getCardImageTop'
2854 * @class Roo.bootstrap.Img
2855 * @extends Roo.bootstrap.Component
2856 * Bootstrap Img class
2857 * @cfg {Boolean} imgResponsive false | true
2858 * @cfg {String} border rounded | circle | thumbnail
2859 * @cfg {String} src image source
2860 * @cfg {String} alt image alternative text
2861 * @cfg {String} href a tag href
2862 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2863 * @cfg {String} xsUrl xs image source
2864 * @cfg {String} smUrl sm image source
2865 * @cfg {String} mdUrl md image source
2866 * @cfg {String} lgUrl lg image source
2869 * Create a new Input
2870 * @param {Object} config The config object
2873 Roo.bootstrap.Img = function(config){
2874 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2880 * The img click event for the img.
2881 * @param {Roo.EventObject} e
2887 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2889 imgResponsive: true,
2899 getAutoCreate : function()
2901 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2902 return this.createSingleImg();
2907 cls: 'roo-image-responsive-group',
2912 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2914 if(!_this[size + 'Url']){
2920 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2921 html: _this.html || cfg.html,
2922 src: _this[size + 'Url']
2925 img.cls += ' roo-image-responsive-' + size;
2927 var s = ['xs', 'sm', 'md', 'lg'];
2929 s.splice(s.indexOf(size), 1);
2931 Roo.each(s, function(ss){
2932 img.cls += ' hidden-' + ss;
2935 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2936 cfg.cls += ' img-' + _this.border;
2940 cfg.alt = _this.alt;
2953 a.target = _this.target;
2957 cfg.cn.push((_this.href) ? a : img);
2964 createSingleImg : function()
2968 cls: (this.imgResponsive) ? 'img-responsive' : '',
2970 src : 'about:blank' // just incase src get's set to undefined?!?
2973 cfg.html = this.html || cfg.html;
2975 cfg.src = this.src || cfg.src;
2977 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2978 cfg.cls += ' img-' + this.border;
2995 a.target = this.target;
3000 return (this.href) ? a : cfg;
3003 initEvents: function()
3006 this.el.on('click', this.onClick, this);
3011 onClick : function(e)
3013 Roo.log('img onclick');
3014 this.fireEvent('click', this, e);
3017 * Sets the url of the image - used to update it
3018 * @param {String} url the url of the image
3021 setSrc : function(url)
3025 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3026 this.el.dom.src = url;
3030 this.el.select('img', true).first().dom.src = url;
3046 * @class Roo.bootstrap.Link
3047 * @extends Roo.bootstrap.Component
3048 * Bootstrap Link Class
3049 * @cfg {String} alt image alternative text
3050 * @cfg {String} href a tag href
3051 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3052 * @cfg {String} html the content of the link.
3053 * @cfg {String} anchor name for the anchor link
3054 * @cfg {String} fa - favicon
3056 * @cfg {Boolean} preventDefault (true | false) default false
3060 * Create a new Input
3061 * @param {Object} config The config object
3064 Roo.bootstrap.Link = function(config){
3065 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3071 * The img click event for the img.
3072 * @param {Roo.EventObject} e
3078 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3082 preventDefault: false,
3088 getAutoCreate : function()
3090 var html = this.html || '';
3092 if (this.fa !== false) {
3093 html = '<i class="fa fa-' + this.fa + '"></i>';
3098 // anchor's do not require html/href...
3099 if (this.anchor === false) {
3101 cfg.href = this.href || '#';
3103 cfg.name = this.anchor;
3104 if (this.html !== false || this.fa !== false) {
3107 if (this.href !== false) {
3108 cfg.href = this.href;
3112 if(this.alt !== false){
3117 if(this.target !== false) {
3118 cfg.target = this.target;
3124 initEvents: function() {
3126 if(!this.href || this.preventDefault){
3127 this.el.on('click', this.onClick, this);
3131 onClick : function(e)
3133 if(this.preventDefault){
3136 //Roo.log('img onclick');
3137 this.fireEvent('click', this, e);
3150 * @class Roo.bootstrap.Header
3151 * @extends Roo.bootstrap.Component
3152 * Bootstrap Header class
3153 * @cfg {String} html content of header
3154 * @cfg {Number} level (1|2|3|4|5|6) default 1
3157 * Create a new Header
3158 * @param {Object} config The config object
3162 Roo.bootstrap.Header = function(config){
3163 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3166 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3174 getAutoCreate : function(){
3179 tag: 'h' + (1 *this.level),
3180 html: this.html || ''
3192 * Ext JS Library 1.1.1
3193 * Copyright(c) 2006-2007, Ext JS, LLC.
3195 * Originally Released Under LGPL - original licence link has changed is not relivant.
3198 * <script type="text/javascript">
3202 * @class Roo.bootstrap.MenuMgr
3203 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3206 Roo.bootstrap.MenuMgr = function(){
3207 var menus, active, groups = {}, attached = false, lastShow = new Date();
3209 // private - called when first menu is created
3212 active = new Roo.util.MixedCollection();
3213 Roo.get(document).addKeyListener(27, function(){
3214 if(active.length > 0){
3222 if(active && active.length > 0){
3223 var c = active.clone();
3233 if(active.length < 1){
3234 Roo.get(document).un("mouseup", onMouseDown);
3242 var last = active.last();
3243 lastShow = new Date();
3246 Roo.get(document).on("mouseup", onMouseDown);
3251 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3252 m.parentMenu.activeChild = m;
3253 }else if(last && last.isVisible()){
3254 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3259 function onBeforeHide(m){
3261 m.activeChild.hide();
3263 if(m.autoHideTimer){
3264 clearTimeout(m.autoHideTimer);
3265 delete m.autoHideTimer;
3270 function onBeforeShow(m){
3271 var pm = m.parentMenu;
3272 if(!pm && !m.allowOtherMenus){
3274 }else if(pm && pm.activeChild && active != m){
3275 pm.activeChild.hide();
3279 // private this should really trigger on mouseup..
3280 function onMouseDown(e){
3281 Roo.log("on Mouse Up");
3283 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3284 Roo.log("MenuManager hideAll");
3293 function onBeforeCheck(mi, state){
3295 var g = groups[mi.group];
3296 for(var i = 0, l = g.length; i < l; i++){
3298 g[i].setChecked(false);
3307 * Hides all menus that are currently visible
3309 hideAll : function(){
3314 register : function(menu){
3318 menus[menu.id] = menu;
3319 menu.on("beforehide", onBeforeHide);
3320 menu.on("hide", onHide);
3321 menu.on("beforeshow", onBeforeShow);
3322 menu.on("show", onShow);
3324 if(g && menu.events["checkchange"]){
3328 groups[g].push(menu);
3329 menu.on("checkchange", onCheck);
3334 * Returns a {@link Roo.menu.Menu} object
3335 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3336 * be used to generate and return a new Menu instance.
3338 get : function(menu){
3339 if(typeof menu == "string"){ // menu id
3341 }else if(menu.events){ // menu instance
3344 /*else if(typeof menu.length == 'number'){ // array of menu items?
3345 return new Roo.bootstrap.Menu({items:menu});
3346 }else{ // otherwise, must be a config
3347 return new Roo.bootstrap.Menu(menu);
3354 unregister : function(menu){
3355 delete menus[menu.id];
3356 menu.un("beforehide", onBeforeHide);
3357 menu.un("hide", onHide);
3358 menu.un("beforeshow", onBeforeShow);
3359 menu.un("show", onShow);
3361 if(g && menu.events["checkchange"]){
3362 groups[g].remove(menu);
3363 menu.un("checkchange", onCheck);
3368 registerCheckable : function(menuItem){
3369 var g = menuItem.group;
3374 groups[g].push(menuItem);
3375 menuItem.on("beforecheckchange", onBeforeCheck);
3380 unregisterCheckable : function(menuItem){
3381 var g = menuItem.group;
3383 groups[g].remove(menuItem);
3384 menuItem.un("beforecheckchange", onBeforeCheck);
3396 * @class Roo.bootstrap.Menu
3397 * @extends Roo.bootstrap.Component
3398 * Bootstrap Menu class - container for MenuItems
3399 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3400 * @cfg {bool} hidden if the menu should be hidden when rendered.
3401 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3402 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3406 * @param {Object} config The config object
3410 Roo.bootstrap.Menu = function(config){
3411 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3412 if (this.registerMenu && this.type != 'treeview') {
3413 Roo.bootstrap.MenuMgr.register(this);
3420 * Fires before this menu is displayed (return false to block)
3421 * @param {Roo.menu.Menu} this
3426 * Fires before this menu is hidden (return false to block)
3427 * @param {Roo.menu.Menu} this
3432 * Fires after this menu is displayed
3433 * @param {Roo.menu.Menu} this
3438 * Fires after this menu is hidden
3439 * @param {Roo.menu.Menu} this
3444 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3445 * @param {Roo.menu.Menu} this
3446 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3447 * @param {Roo.EventObject} e
3452 * Fires when the mouse is hovering over this menu
3453 * @param {Roo.menu.Menu} this
3454 * @param {Roo.EventObject} e
3455 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3460 * Fires when the mouse exits this menu
3461 * @param {Roo.menu.Menu} this
3462 * @param {Roo.EventObject} e
3463 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3468 * Fires when a menu item contained in this menu is clicked
3469 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3470 * @param {Roo.EventObject} e
3474 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3477 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3481 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3484 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3486 registerMenu : true,
3488 menuItems :false, // stores the menu items..
3498 getChildContainer : function() {
3502 getAutoCreate : function(){
3504 //if (['right'].indexOf(this.align)!==-1) {
3505 // cfg.cn[1].cls += ' pull-right'
3511 cls : 'dropdown-menu' ,
3512 style : 'z-index:1000'
3516 if (this.type === 'submenu') {
3517 cfg.cls = 'submenu active';
3519 if (this.type === 'treeview') {
3520 cfg.cls = 'treeview-menu';
3525 initEvents : function() {
3527 // Roo.log("ADD event");
3528 // Roo.log(this.triggerEl.dom);
3530 this.triggerEl.on('click', this.onTriggerClick, this);
3532 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3535 if (this.triggerEl.hasClass('nav-item')) {
3536 // dropdown toggle on the 'a' in BS4?
3537 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3539 this.triggerEl.addClass('dropdown-toggle');
3542 this.el.on('touchstart' , this.onTouch, this);
3544 this.el.on('click' , this.onClick, this);
3546 this.el.on("mouseover", this.onMouseOver, this);
3547 this.el.on("mouseout", this.onMouseOut, this);
3551 findTargetItem : function(e)
3553 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3557 //Roo.log(t); Roo.log(t.id);
3559 //Roo.log(this.menuitems);
3560 return this.menuitems.get(t.id);
3562 //return this.items.get(t.menuItemId);
3568 onTouch : function(e)
3570 Roo.log("menu.onTouch");
3571 //e.stopEvent(); this make the user popdown broken
3575 onClick : function(e)
3577 Roo.log("menu.onClick");
3579 var t = this.findTargetItem(e);
3580 if(!t || t.isContainer){
3585 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3586 if(t == this.activeItem && t.shouldDeactivate(e)){
3587 this.activeItem.deactivate();
3588 delete this.activeItem;
3592 this.setActiveItem(t, true);
3600 Roo.log('pass click event');
3604 this.fireEvent("click", this, t, e);
3608 if(!t.href.length || t.href == '#'){
3609 (function() { _this.hide(); }).defer(100);
3614 onMouseOver : function(e){
3615 var t = this.findTargetItem(e);
3618 // if(t.canActivate && !t.disabled){
3619 // this.setActiveItem(t, true);
3623 this.fireEvent("mouseover", this, e, t);
3625 isVisible : function(){
3626 return !this.hidden;
3628 onMouseOut : function(e){
3629 var t = this.findTargetItem(e);
3632 // if(t == this.activeItem && t.shouldDeactivate(e)){
3633 // this.activeItem.deactivate();
3634 // delete this.activeItem;
3637 this.fireEvent("mouseout", this, e, t);
3642 * Displays this menu relative to another element
3643 * @param {String/HTMLElement/Roo.Element} element The element to align to
3644 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3645 * the element (defaults to this.defaultAlign)
3646 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3648 show : function(el, pos, parentMenu)
3650 if (false === this.fireEvent("beforeshow", this)) {
3651 Roo.log("show canceled");
3654 this.parentMenu = parentMenu;
3659 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3662 * Displays this menu at a specific xy position
3663 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3664 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3666 showAt : function(xy, parentMenu, /* private: */_e){
3667 this.parentMenu = parentMenu;
3672 this.fireEvent("beforeshow", this);
3673 //xy = this.el.adjustForConstraints(xy);
3677 this.hideMenuItems();
3678 this.hidden = false;
3679 this.triggerEl.addClass('open');
3680 this.el.addClass('show');
3682 // reassign x when hitting right
3683 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3684 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3687 // reassign y when hitting bottom
3688 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3689 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3692 // but the list may align on trigger left or trigger top... should it be a properity?
3694 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3699 this.fireEvent("show", this);
3705 this.doFocus.defer(50, this);
3709 doFocus : function(){
3711 this.focusEl.focus();
3716 * Hides this menu and optionally all parent menus
3717 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3719 hide : function(deep)
3721 if (false === this.fireEvent("beforehide", this)) {
3722 Roo.log("hide canceled");
3725 this.hideMenuItems();
3726 if(this.el && this.isVisible()){
3728 if(this.activeItem){
3729 this.activeItem.deactivate();
3730 this.activeItem = null;
3732 this.triggerEl.removeClass('open');;
3733 this.el.removeClass('show');
3735 this.fireEvent("hide", this);
3737 if(deep === true && this.parentMenu){
3738 this.parentMenu.hide(true);
3742 onTriggerClick : function(e)
3744 Roo.log('trigger click');
3746 var target = e.getTarget();
3748 Roo.log(target.nodeName.toLowerCase());
3750 if(target.nodeName.toLowerCase() === 'i'){
3756 onTriggerPress : function(e)
3758 Roo.log('trigger press');
3759 //Roo.log(e.getTarget());
3760 // Roo.log(this.triggerEl.dom);
3762 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3763 var pel = Roo.get(e.getTarget());
3764 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3765 Roo.log('is treeview or dropdown?');
3769 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3773 if (this.isVisible()) {
3778 this.show(this.triggerEl, '?', false);
3781 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3788 hideMenuItems : function()
3790 Roo.log("hide Menu Items");
3795 this.el.select('.open',true).each(function(aa) {
3797 aa.removeClass('open');
3801 addxtypeChild : function (tree, cntr) {
3802 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3804 this.menuitems.add(comp);
3816 this.getEl().dom.innerHTML = '';
3817 this.menuitems.clear();
3831 * @class Roo.bootstrap.MenuItem
3832 * @extends Roo.bootstrap.Component
3833 * Bootstrap MenuItem class
3834 * @cfg {String} html the menu label
3835 * @cfg {String} href the link
3836 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3837 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3838 * @cfg {Boolean} active used on sidebars to highlight active itesm
3839 * @cfg {String} fa favicon to show on left of menu item.
3840 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3844 * Create a new MenuItem
3845 * @param {Object} config The config object
3849 Roo.bootstrap.MenuItem = function(config){
3850 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3855 * The raw click event for the entire grid.
3856 * @param {Roo.bootstrap.MenuItem} this
3857 * @param {Roo.EventObject} e
3863 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3867 preventDefault: false,
3868 isContainer : false,
3872 getAutoCreate : function(){
3874 if(this.isContainer){
3877 cls: 'dropdown-menu-item '
3887 cls : 'dropdown-item',
3892 if (this.fa !== false) {
3895 cls : 'fa fa-' + this.fa
3904 cls: 'dropdown-menu-item',
3907 if (this.parent().type == 'treeview') {
3908 cfg.cls = 'treeview-menu';
3911 cfg.cls += ' active';
3916 anc.href = this.href || cfg.cn[0].href ;
3917 ctag.html = this.html || cfg.cn[0].html ;
3921 initEvents: function()
3923 if (this.parent().type == 'treeview') {
3924 this.el.select('a').on('click', this.onClick, this);
3928 this.menu.parentType = this.xtype;
3929 this.menu.triggerEl = this.el;
3930 this.menu = this.addxtype(Roo.apply({}, this.menu));
3934 onClick : function(e)
3936 Roo.log('item on click ');
3938 if(this.preventDefault){
3941 //this.parent().hideMenuItems();
3943 this.fireEvent('click', this, e);
3962 * @class Roo.bootstrap.MenuSeparator
3963 * @extends Roo.bootstrap.Component
3964 * Bootstrap MenuSeparator class
3967 * Create a new MenuItem
3968 * @param {Object} config The config object
3972 Roo.bootstrap.MenuSeparator = function(config){
3973 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3976 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3978 getAutoCreate : function(){
3997 * @class Roo.bootstrap.Modal
3998 * @extends Roo.bootstrap.Component
3999 * Bootstrap Modal class
4000 * @cfg {String} title Title of dialog
4001 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4002 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4003 * @cfg {Boolean} specificTitle default false
4004 * @cfg {Array} buttons Array of buttons or standard button set..
4005 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4006 * @cfg {Boolean} animate default true
4007 * @cfg {Boolean} allow_close default true
4008 * @cfg {Boolean} fitwindow default false
4009 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4010 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4011 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4012 * @cfg {String} size (sm|lg|xl) default empty
4013 * @cfg {Number} max_width set the max width of modal
4014 * @cfg {Boolean} editableTitle can the title be edited
4019 * Create a new Modal Dialog
4020 * @param {Object} config The config object
4023 Roo.bootstrap.Modal = function(config){
4024 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4029 * The raw btnclick event for the button
4030 * @param {Roo.EventObject} e
4035 * Fire when dialog resize
4036 * @param {Roo.bootstrap.Modal} this
4037 * @param {Roo.EventObject} e
4041 * @event titlechanged
4042 * Fire when the editable title has been changed
4043 * @param {Roo.bootstrap.Modal} this
4044 * @param {Roo.EventObject} value
4046 "titlechanged" : true
4049 this.buttons = this.buttons || [];
4052 this.tmpl = Roo.factory(this.tmpl);
4057 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4059 title : 'test dialog',
4069 specificTitle: false,
4071 buttonPosition: 'right',
4093 editableTitle : false,
4095 onRender : function(ct, position)
4097 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4100 var cfg = Roo.apply({}, this.getAutoCreate());
4103 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4105 //if (!cfg.name.length) {
4109 cfg.cls += ' ' + this.cls;
4112 cfg.style = this.style;
4114 this.el = Roo.get(document.body).createChild(cfg, position);
4116 //var type = this.el.dom.type;
4119 if(this.tabIndex !== undefined){
4120 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4123 this.dialogEl = this.el.select('.modal-dialog',true).first();
4124 this.bodyEl = this.el.select('.modal-body',true).first();
4125 this.closeEl = this.el.select('.modal-header .close', true).first();
4126 this.headerEl = this.el.select('.modal-header',true).first();
4127 this.titleEl = this.el.select('.modal-title',true).first();
4128 this.footerEl = this.el.select('.modal-footer',true).first();
4130 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4132 //this.el.addClass("x-dlg-modal");
4134 if (this.buttons.length) {
4135 Roo.each(this.buttons, function(bb) {
4136 var b = Roo.apply({}, bb);
4137 b.xns = b.xns || Roo.bootstrap;
4138 b.xtype = b.xtype || 'Button';
4139 if (typeof(b.listeners) == 'undefined') {
4140 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4143 var btn = Roo.factory(b);
4145 btn.render(this.getButtonContainer());
4149 // render the children.
4152 if(typeof(this.items) != 'undefined'){
4153 var items = this.items;
4156 for(var i =0;i < items.length;i++) {
4157 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4161 this.items = nitems;
4163 // where are these used - they used to be body/close/footer
4167 //this.el.addClass([this.fieldClass, this.cls]);
4171 getAutoCreate : function()
4173 // we will default to modal-body-overflow - might need to remove or make optional later.
4175 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4176 html : this.html || ''
4181 cls : 'modal-title',
4185 if(this.specificTitle){ // WTF is this?
4190 if (this.allow_close && Roo.bootstrap.version == 3) {
4200 if (this.editableTitle) {
4202 cls: 'form-control roo-editable-title d-none',
4208 if (this.allow_close && Roo.bootstrap.version == 4) {
4218 if(this.size.length){
4219 size = 'modal-' + this.size;
4222 var footer = Roo.bootstrap.version == 3 ?
4224 cls : 'modal-footer',
4228 cls: 'btn-' + this.buttonPosition
4233 { // BS4 uses mr-auto on left buttons....
4234 cls : 'modal-footer'
4245 cls: "modal-dialog " + size,
4248 cls : "modal-content",
4251 cls : 'modal-header',
4266 modal.cls += ' fade';
4272 getChildContainer : function() {
4277 getButtonContainer : function() {
4279 return Roo.bootstrap.version == 4 ?
4280 this.el.select('.modal-footer',true).first()
4281 : this.el.select('.modal-footer div',true).first();
4284 initEvents : function()
4286 if (this.allow_close) {
4287 this.closeEl.on('click', this.hide, this);
4289 Roo.EventManager.onWindowResize(this.resize, this, true);
4290 if (this.editableTitle) {
4291 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4292 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4293 this.headerEditEl.on('keyup', function(e) {
4294 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4295 this.toggleHeaderInput(false)
4298 this.headerEditEl.on('blur', function(e) {
4299 this.toggleHeaderInput(false)
4308 this.maskEl.setSize(
4309 Roo.lib.Dom.getViewWidth(true),
4310 Roo.lib.Dom.getViewHeight(true)
4313 if (this.fitwindow) {
4315 this.dialogEl.setStyle( { 'max-width' : '100%' });
4317 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4318 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4323 if(this.max_width !== 0) {
4325 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4328 this.setSize(w, this.height);
4332 if(this.max_height) {
4333 this.setSize(w,Math.min(
4335 Roo.lib.Dom.getViewportHeight(true) - 60
4341 if(!this.fit_content) {
4342 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4346 this.setSize(w, Math.min(
4348 this.headerEl.getHeight() +
4349 this.footerEl.getHeight() +
4350 this.getChildHeight(this.bodyEl.dom.childNodes),
4351 Roo.lib.Dom.getViewportHeight(true) - 60)
4357 setSize : function(w,h)
4368 if (!this.rendered) {
4371 this.toggleHeaderInput(false);
4372 //this.el.setStyle('display', 'block');
4373 this.el.removeClass('hideing');
4374 this.el.dom.style.display='block';
4376 Roo.get(document.body).addClass('modal-open');
4378 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4381 this.el.addClass('show');
4382 this.el.addClass('in');
4385 this.el.addClass('show');
4386 this.el.addClass('in');
4389 // not sure how we can show data in here..
4391 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4394 Roo.get(document.body).addClass("x-body-masked");
4396 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4397 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4398 this.maskEl.dom.style.display = 'block';
4399 this.maskEl.addClass('show');
4404 this.fireEvent('show', this);
4406 // set zindex here - otherwise it appears to be ignored...
4407 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4410 this.items.forEach( function(e) {
4411 e.layout ? e.layout() : false;
4419 if(this.fireEvent("beforehide", this) !== false){
4421 this.maskEl.removeClass('show');
4423 this.maskEl.dom.style.display = '';
4424 Roo.get(document.body).removeClass("x-body-masked");
4425 this.el.removeClass('in');
4426 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4428 if(this.animate){ // why
4429 this.el.addClass('hideing');
4430 this.el.removeClass('show');
4432 if (!this.el.hasClass('hideing')) {
4433 return; // it's been shown again...
4436 this.el.dom.style.display='';
4438 Roo.get(document.body).removeClass('modal-open');
4439 this.el.removeClass('hideing');
4443 this.el.removeClass('show');
4444 this.el.dom.style.display='';
4445 Roo.get(document.body).removeClass('modal-open');
4448 this.fireEvent('hide', this);
4451 isVisible : function()
4454 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4458 addButton : function(str, cb)
4462 var b = Roo.apply({}, { html : str } );
4463 b.xns = b.xns || Roo.bootstrap;
4464 b.xtype = b.xtype || 'Button';
4465 if (typeof(b.listeners) == 'undefined') {
4466 b.listeners = { click : cb.createDelegate(this) };
4469 var btn = Roo.factory(b);
4471 btn.render(this.getButtonContainer());
4477 setDefaultButton : function(btn)
4479 //this.el.select('.modal-footer').()
4482 resizeTo: function(w,h)
4484 this.dialogEl.setWidth(w);
4486 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4488 this.bodyEl.setHeight(h - diff);
4490 this.fireEvent('resize', this);
4493 setContentSize : function(w, h)
4497 onButtonClick: function(btn,e)
4500 this.fireEvent('btnclick', btn.name, e);
4503 * Set the title of the Dialog
4504 * @param {String} str new Title
4506 setTitle: function(str) {
4507 this.titleEl.dom.innerHTML = str;
4511 * Set the body of the Dialog
4512 * @param {String} str new Title
4514 setBody: function(str) {
4515 this.bodyEl.dom.innerHTML = str;
4518 * Set the body of the Dialog using the template
4519 * @param {Obj} data - apply this data to the template and replace the body contents.
4521 applyBody: function(obj)
4524 Roo.log("Error - using apply Body without a template");
4527 this.tmpl.overwrite(this.bodyEl, obj);
4530 getChildHeight : function(child_nodes)
4534 child_nodes.length == 0
4539 var child_height = 0;
4541 for(var i = 0; i < child_nodes.length; i++) {
4544 * for modal with tabs...
4545 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4547 var layout_childs = child_nodes[i].childNodes;
4549 for(var j = 0; j < layout_childs.length; j++) {
4551 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4553 var layout_body_childs = layout_childs[j].childNodes;
4555 for(var k = 0; k < layout_body_childs.length; k++) {
4557 if(layout_body_childs[k].classList.contains('navbar')) {
4558 child_height += layout_body_childs[k].offsetHeight;
4562 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4564 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4566 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4568 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4569 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4584 child_height += child_nodes[i].offsetHeight;
4585 // Roo.log(child_nodes[i].offsetHeight);
4588 return child_height;
4590 toggleHeaderInput : function(is_edit)
4592 if (!this.editableTitle) {
4593 return; // not editable.
4595 if (is_edit && this.is_header_editing) {
4596 return; // already editing..
4600 this.headerEditEl.dom.value = this.title;
4601 this.headerEditEl.removeClass('d-none');
4602 this.headerEditEl.dom.focus();
4603 this.titleEl.addClass('d-none');
4605 this.is_header_editing = true;
4608 // flip back to not editing.
4609 this.title = this.headerEditEl.dom.value;
4610 this.headerEditEl.addClass('d-none');
4611 this.titleEl.removeClass('d-none');
4612 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4613 this.is_header_editing = false;
4614 this.fireEvent('titlechanged', this, this.title);
4623 Roo.apply(Roo.bootstrap.Modal, {
4625 * Button config that displays a single OK button
4634 * Button config that displays Yes and No buttons
4650 * Button config that displays OK and Cancel buttons
4665 * Button config that displays Yes, No and Cancel buttons
4690 * messagebox - can be used as a replace
4694 * @class Roo.MessageBox
4695 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4699 Roo.Msg.alert('Status', 'Changes saved successfully.');
4701 // Prompt for user data:
4702 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4704 // process text value...
4708 // Show a dialog using config options:
4710 title:'Save Changes?',
4711 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4712 buttons: Roo.Msg.YESNOCANCEL,
4719 Roo.bootstrap.MessageBox = function(){
4720 var dlg, opt, mask, waitTimer;
4721 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4722 var buttons, activeTextEl, bwidth;
4726 var handleButton = function(button){
4728 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4732 var handleHide = function(){
4734 dlg.el.removeClass(opt.cls);
4737 // Roo.TaskMgr.stop(waitTimer);
4738 // waitTimer = null;
4743 var updateButtons = function(b){
4746 buttons["ok"].hide();
4747 buttons["cancel"].hide();
4748 buttons["yes"].hide();
4749 buttons["no"].hide();
4750 dlg.footerEl.hide();
4754 dlg.footerEl.show();
4755 for(var k in buttons){
4756 if(typeof buttons[k] != "function"){
4759 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4760 width += buttons[k].el.getWidth()+15;
4770 var handleEsc = function(d, k, e){
4771 if(opt && opt.closable !== false){
4781 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4782 * @return {Roo.BasicDialog} The BasicDialog element
4784 getDialog : function(){
4786 dlg = new Roo.bootstrap.Modal( {
4789 //constraintoviewport:false,
4791 //collapsible : false,
4796 //buttonAlign:"center",
4797 closeClick : function(){
4798 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4801 handleButton("cancel");
4806 dlg.on("hide", handleHide);
4808 //dlg.addKeyListener(27, handleEsc);
4810 this.buttons = buttons;
4811 var bt = this.buttonText;
4812 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4813 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4814 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4815 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4817 bodyEl = dlg.bodyEl.createChild({
4819 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4820 '<textarea class="roo-mb-textarea"></textarea>' +
4821 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4823 msgEl = bodyEl.dom.firstChild;
4824 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4825 textboxEl.enableDisplayMode();
4826 textboxEl.addKeyListener([10,13], function(){
4827 if(dlg.isVisible() && opt && opt.buttons){
4830 }else if(opt.buttons.yes){
4831 handleButton("yes");
4835 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4836 textareaEl.enableDisplayMode();
4837 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4838 progressEl.enableDisplayMode();
4840 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4841 var pf = progressEl.dom.firstChild;
4843 pp = Roo.get(pf.firstChild);
4844 pp.setHeight(pf.offsetHeight);
4852 * Updates the message box body text
4853 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4854 * the XHTML-compliant non-breaking space character '&#160;')
4855 * @return {Roo.MessageBox} This message box
4857 updateText : function(text)
4859 if(!dlg.isVisible() && !opt.width){
4860 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4861 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4863 msgEl.innerHTML = text || ' ';
4865 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4866 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4868 Math.min(opt.width || cw , this.maxWidth),
4869 Math.max(opt.minWidth || this.minWidth, bwidth)
4872 activeTextEl.setWidth(w);
4874 if(dlg.isVisible()){
4875 dlg.fixedcenter = false;
4877 // to big, make it scroll. = But as usual stupid IE does not support
4880 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4881 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4882 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4884 bodyEl.dom.style.height = '';
4885 bodyEl.dom.style.overflowY = '';
4888 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4890 bodyEl.dom.style.overflowX = '';
4893 dlg.setContentSize(w, bodyEl.getHeight());
4894 if(dlg.isVisible()){
4895 dlg.fixedcenter = true;
4901 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4902 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4903 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4904 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4905 * @return {Roo.MessageBox} This message box
4907 updateProgress : function(value, text){
4909 this.updateText(text);
4912 if (pp) { // weird bug on my firefox - for some reason this is not defined
4913 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4914 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4920 * Returns true if the message box is currently displayed
4921 * @return {Boolean} True if the message box is visible, else false
4923 isVisible : function(){
4924 return dlg && dlg.isVisible();
4928 * Hides the message box if it is displayed
4931 if(this.isVisible()){
4937 * Displays a new message box, or reinitializes an existing message box, based on the config options
4938 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4939 * The following config object properties are supported:
4941 Property Type Description
4942 ---------- --------------- ------------------------------------------------------------------------------------
4943 animEl String/Element An id or Element from which the message box should animate as it opens and
4944 closes (defaults to undefined)
4945 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4946 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4947 closable Boolean False to hide the top-right close button (defaults to true). Note that
4948 progress and wait dialogs will ignore this property and always hide the
4949 close button as they can only be closed programmatically.
4950 cls String A custom CSS class to apply to the message box element
4951 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4952 displayed (defaults to 75)
4953 fn Function A callback function to execute after closing the dialog. The arguments to the
4954 function will be btn (the name of the button that was clicked, if applicable,
4955 e.g. "ok"), and text (the value of the active text field, if applicable).
4956 Progress and wait dialogs will ignore this option since they do not respond to
4957 user actions and can only be closed programmatically, so any required function
4958 should be called by the same code after it closes the dialog.
4959 icon String A CSS class that provides a background image to be used as an icon for
4960 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4961 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4962 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4963 modal Boolean False to allow user interaction with the page while the message box is
4964 displayed (defaults to true)
4965 msg String A string that will replace the existing message box body text (defaults
4966 to the XHTML-compliant non-breaking space character ' ')
4967 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4968 progress Boolean True to display a progress bar (defaults to false)
4969 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4970 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4971 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4972 title String The title text
4973 value String The string value to set into the active textbox element if displayed
4974 wait Boolean True to display a progress bar (defaults to false)
4975 width Number The width of the dialog in pixels
4982 msg: 'Please enter your address:',
4984 buttons: Roo.MessageBox.OKCANCEL,
4987 animEl: 'addAddressBtn'
4990 * @param {Object} config Configuration options
4991 * @return {Roo.MessageBox} This message box
4993 show : function(options)
4996 // this causes nightmares if you show one dialog after another
4997 // especially on callbacks..
4999 if(this.isVisible()){
5002 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5003 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5004 Roo.log("New Dialog Message:" + options.msg )
5005 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5006 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5009 var d = this.getDialog();
5011 d.setTitle(opt.title || " ");
5012 d.closeEl.setDisplayed(opt.closable !== false);
5013 activeTextEl = textboxEl;
5014 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5019 textareaEl.setHeight(typeof opt.multiline == "number" ?
5020 opt.multiline : this.defaultTextHeight);
5021 activeTextEl = textareaEl;
5030 progressEl.setDisplayed(opt.progress === true);
5032 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5034 this.updateProgress(0);
5035 activeTextEl.dom.value = opt.value || "";
5037 dlg.setDefaultButton(activeTextEl);
5039 var bs = opt.buttons;
5043 }else if(bs && bs.yes){
5044 db = buttons["yes"];
5046 dlg.setDefaultButton(db);
5048 bwidth = updateButtons(opt.buttons);
5049 this.updateText(opt.msg);
5051 d.el.addClass(opt.cls);
5053 d.proxyDrag = opt.proxyDrag === true;
5054 d.modal = opt.modal !== false;
5055 d.mask = opt.modal !== false ? mask : false;
5057 // force it to the end of the z-index stack so it gets a cursor in FF
5058 document.body.appendChild(dlg.el.dom);
5059 d.animateTarget = null;
5060 d.show(options.animEl);
5066 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5067 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5068 * and closing the message box when the process is complete.
5069 * @param {String} title The title bar text
5070 * @param {String} msg The message box body text
5071 * @return {Roo.MessageBox} This message box
5073 progress : function(title, msg){
5080 minWidth: this.minProgressWidth,
5087 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5088 * If a callback function is passed it will be called after the user clicks the button, and the
5089 * id of the button that was clicked will be passed as the only parameter to the callback
5090 * (could also be the top-right close button).
5091 * @param {String} title The title bar text
5092 * @param {String} msg The message box body text
5093 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5094 * @param {Object} scope (optional) The scope of the callback function
5095 * @return {Roo.MessageBox} This message box
5097 alert : function(title, msg, fn, scope)
5112 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5113 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5114 * You are responsible for closing the message box when the process is complete.
5115 * @param {String} msg The message box body text
5116 * @param {String} title (optional) The title bar text
5117 * @return {Roo.MessageBox} This message box
5119 wait : function(msg, title){
5130 waitTimer = Roo.TaskMgr.start({
5132 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5140 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5141 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5142 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5143 * @param {String} title The title bar text
5144 * @param {String} msg The message box body text
5145 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5146 * @param {Object} scope (optional) The scope of the callback function
5147 * @return {Roo.MessageBox} This message box
5149 confirm : function(title, msg, fn, scope){
5153 buttons: this.YESNO,
5162 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5163 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5164 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5165 * (could also be the top-right close button) and the text that was entered will be passed as the two
5166 * parameters to the callback.
5167 * @param {String} title The title bar text
5168 * @param {String} msg The message box body text
5169 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5170 * @param {Object} scope (optional) The scope of the callback function
5171 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5172 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5173 * @return {Roo.MessageBox} This message box
5175 prompt : function(title, msg, fn, scope, multiline){
5179 buttons: this.OKCANCEL,
5184 multiline: multiline,
5191 * Button config that displays a single OK button
5196 * Button config that displays Yes and No buttons
5199 YESNO : {yes:true, no:true},
5201 * Button config that displays OK and Cancel buttons
5204 OKCANCEL : {ok:true, cancel:true},
5206 * Button config that displays Yes, No and Cancel buttons
5209 YESNOCANCEL : {yes:true, no:true, cancel:true},
5212 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5215 defaultTextHeight : 75,
5217 * The maximum width in pixels of the message box (defaults to 600)
5222 * The minimum width in pixels of the message box (defaults to 100)
5227 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5228 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5231 minProgressWidth : 250,
5233 * An object containing the default button text strings that can be overriden for localized language support.
5234 * Supported properties are: ok, cancel, yes and no.
5235 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5248 * Shorthand for {@link Roo.MessageBox}
5250 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5251 Roo.Msg = Roo.Msg || Roo.MessageBox;
5260 * @class Roo.bootstrap.Navbar
5261 * @extends Roo.bootstrap.Component
5262 * Bootstrap Navbar class
5265 * Create a new Navbar
5266 * @param {Object} config The config object
5270 Roo.bootstrap.Navbar = function(config){
5271 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5275 * @event beforetoggle
5276 * Fire before toggle the menu
5277 * @param {Roo.EventObject} e
5279 "beforetoggle" : true
5283 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5292 getAutoCreate : function(){
5295 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5299 initEvents :function ()
5301 //Roo.log(this.el.select('.navbar-toggle',true));
5302 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5309 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5311 var size = this.el.getSize();
5312 this.maskEl.setSize(size.width, size.height);
5313 this.maskEl.enableDisplayMode("block");
5322 getChildContainer : function()
5324 if (this.el && this.el.select('.collapse').getCount()) {
5325 return this.el.select('.collapse',true).first();
5340 onToggle : function()
5343 if(this.fireEvent('beforetoggle', this) === false){
5346 var ce = this.el.select('.navbar-collapse',true).first();
5348 if (!ce.hasClass('show')) {
5358 * Expand the navbar pulldown
5360 expand : function ()
5363 var ce = this.el.select('.navbar-collapse',true).first();
5364 if (ce.hasClass('collapsing')) {
5367 ce.dom.style.height = '';
5369 ce.addClass('in'); // old...
5370 ce.removeClass('collapse');
5371 ce.addClass('show');
5372 var h = ce.getHeight();
5374 ce.removeClass('show');
5375 // at this point we should be able to see it..
5376 ce.addClass('collapsing');
5378 ce.setHeight(0); // resize it ...
5379 ce.on('transitionend', function() {
5380 //Roo.log('done transition');
5381 ce.removeClass('collapsing');
5382 ce.addClass('show');
5383 ce.removeClass('collapse');
5385 ce.dom.style.height = '';
5386 }, this, { single: true} );
5388 ce.dom.scrollTop = 0;
5391 * Collapse the navbar pulldown
5393 collapse : function()
5395 var ce = this.el.select('.navbar-collapse',true).first();
5397 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5398 // it's collapsed or collapsing..
5401 ce.removeClass('in'); // old...
5402 ce.setHeight(ce.getHeight());
5403 ce.removeClass('show');
5404 ce.addClass('collapsing');
5406 ce.on('transitionend', function() {
5407 ce.dom.style.height = '';
5408 ce.removeClass('collapsing');
5409 ce.addClass('collapse');
5410 }, this, { single: true} );
5430 * @class Roo.bootstrap.NavSimplebar
5431 * @extends Roo.bootstrap.Navbar
5432 * Bootstrap Sidebar class
5434 * @cfg {Boolean} inverse is inverted color
5436 * @cfg {String} type (nav | pills | tabs)
5437 * @cfg {Boolean} arrangement stacked | justified
5438 * @cfg {String} align (left | right) alignment
5440 * @cfg {Boolean} main (true|false) main nav bar? default false
5441 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5443 * @cfg {String} tag (header|footer|nav|div) default is nav
5445 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5449 * Create a new Sidebar
5450 * @param {Object} config The config object
5454 Roo.bootstrap.NavSimplebar = function(config){
5455 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5458 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5474 getAutoCreate : function(){
5478 tag : this.tag || 'div',
5479 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5481 if (['light','white'].indexOf(this.weight) > -1) {
5482 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5484 cfg.cls += ' bg-' + this.weight;
5487 cfg.cls += ' navbar-inverse';
5491 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5493 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5502 cls: 'nav nav-' + this.xtype,
5508 this.type = this.type || 'nav';
5509 if (['tabs','pills'].indexOf(this.type) != -1) {
5510 cfg.cn[0].cls += ' nav-' + this.type
5514 if (this.type!=='nav') {
5515 Roo.log('nav type must be nav/tabs/pills')
5517 cfg.cn[0].cls += ' navbar-nav'
5523 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5524 cfg.cn[0].cls += ' nav-' + this.arrangement;
5528 if (this.align === 'right') {
5529 cfg.cn[0].cls += ' navbar-right';
5554 * navbar-expand-md fixed-top
5558 * @class Roo.bootstrap.NavHeaderbar
5559 * @extends Roo.bootstrap.NavSimplebar
5560 * Bootstrap Sidebar class
5562 * @cfg {String} brand what is brand
5563 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5564 * @cfg {String} brand_href href of the brand
5565 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5566 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5567 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5568 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5571 * Create a new Sidebar
5572 * @param {Object} config The config object
5576 Roo.bootstrap.NavHeaderbar = function(config){
5577 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5581 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5588 desktopCenter : false,
5591 getAutoCreate : function(){
5594 tag: this.nav || 'nav',
5595 cls: 'navbar navbar-expand-md',
5601 if (this.desktopCenter) {
5602 cn.push({cls : 'container', cn : []});
5610 cls: 'navbar-toggle navbar-toggler',
5611 'data-toggle': 'collapse',
5616 html: 'Toggle navigation'
5620 cls: 'icon-bar navbar-toggler-icon'
5633 cn.push( Roo.bootstrap.version == 4 ? btn : {
5635 cls: 'navbar-header',
5644 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5648 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5650 if (['light','white'].indexOf(this.weight) > -1) {
5651 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5653 cfg.cls += ' bg-' + this.weight;
5656 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5657 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5659 // tag can override this..
5661 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5664 if (this.brand !== '') {
5665 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5666 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5668 href: this.brand_href ? this.brand_href : '#',
5669 cls: 'navbar-brand',
5677 cfg.cls += ' main-nav';
5685 getHeaderChildContainer : function()
5687 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5688 return this.el.select('.navbar-header',true).first();
5691 return this.getChildContainer();
5694 getChildContainer : function()
5697 return this.el.select('.roo-navbar-collapse',true).first();
5702 initEvents : function()
5704 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5706 if (this.autohide) {
5711 Roo.get(document).on('scroll',function(e) {
5712 var ns = Roo.get(document).getScroll().top;
5713 var os = prevScroll;
5717 ft.removeClass('slideDown');
5718 ft.addClass('slideUp');
5721 ft.removeClass('slideUp');
5722 ft.addClass('slideDown');
5743 * @class Roo.bootstrap.NavSidebar
5744 * @extends Roo.bootstrap.Navbar
5745 * Bootstrap Sidebar class
5748 * Create a new Sidebar
5749 * @param {Object} config The config object
5753 Roo.bootstrap.NavSidebar = function(config){
5754 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5757 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5759 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5761 getAutoCreate : function(){
5766 cls: 'sidebar sidebar-nav'
5788 * @class Roo.bootstrap.NavGroup
5789 * @extends Roo.bootstrap.Component
5790 * Bootstrap NavGroup class
5791 * @cfg {String} align (left|right)
5792 * @cfg {Boolean} inverse
5793 * @cfg {String} type (nav|pills|tab) default nav
5794 * @cfg {String} navId - reference Id for navbar.
5795 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5798 * Create a new nav group
5799 * @param {Object} config The config object
5802 Roo.bootstrap.NavGroup = function(config){
5803 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5806 Roo.bootstrap.NavGroup.register(this);
5810 * Fires when the active item changes
5811 * @param {Roo.bootstrap.NavGroup} this
5812 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5813 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5820 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5832 getAutoCreate : function()
5834 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5840 if (Roo.bootstrap.version == 4) {
5841 if (['tabs','pills'].indexOf(this.type) != -1) {
5842 cfg.cls += ' nav-' + this.type;
5844 // trying to remove so header bar can right align top?
5845 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5846 // do not use on header bar...
5847 cfg.cls += ' navbar-nav';
5852 if (['tabs','pills'].indexOf(this.type) != -1) {
5853 cfg.cls += ' nav-' + this.type
5855 if (this.type !== 'nav') {
5856 Roo.log('nav type must be nav/tabs/pills')
5858 cfg.cls += ' navbar-nav'
5862 if (this.parent() && this.parent().sidebar) {
5865 cls: 'dashboard-menu sidebar-menu'
5871 if (this.form === true) {
5874 cls: 'navbar-form form-inline'
5876 //nav navbar-right ml-md-auto
5877 if (this.align === 'right') {
5878 cfg.cls += ' navbar-right ml-md-auto';
5880 cfg.cls += ' navbar-left';
5884 if (this.align === 'right') {
5885 cfg.cls += ' navbar-right ml-md-auto';
5887 cfg.cls += ' mr-auto';
5891 cfg.cls += ' navbar-inverse';
5899 * sets the active Navigation item
5900 * @param {Roo.bootstrap.NavItem} the new current navitem
5902 setActiveItem : function(item)
5905 Roo.each(this.navItems, function(v){
5910 v.setActive(false, true);
5917 item.setActive(true, true);
5918 this.fireEvent('changed', this, item, prev);
5923 * gets the active Navigation item
5924 * @return {Roo.bootstrap.NavItem} the current navitem
5926 getActive : function()
5930 Roo.each(this.navItems, function(v){
5941 indexOfNav : function()
5945 Roo.each(this.navItems, function(v,i){
5956 * adds a Navigation item
5957 * @param {Roo.bootstrap.NavItem} the navitem to add
5959 addItem : function(cfg)
5961 if (this.form && Roo.bootstrap.version == 4) {
5964 var cn = new Roo.bootstrap.NavItem(cfg);
5966 cn.parentId = this.id;
5967 cn.onRender(this.el, null);
5971 * register a Navigation item
5972 * @param {Roo.bootstrap.NavItem} the navitem to add
5974 register : function(item)
5976 this.navItems.push( item);
5977 item.navId = this.navId;
5982 * clear all the Navigation item
5985 clearAll : function()
5988 this.el.dom.innerHTML = '';
5991 getNavItem: function(tabId)
5994 Roo.each(this.navItems, function(e) {
5995 if (e.tabId == tabId) {
6005 setActiveNext : function()
6007 var i = this.indexOfNav(this.getActive());
6008 if (i > this.navItems.length) {
6011 this.setActiveItem(this.navItems[i+1]);
6013 setActivePrev : function()
6015 var i = this.indexOfNav(this.getActive());
6019 this.setActiveItem(this.navItems[i-1]);
6021 clearWasActive : function(except) {
6022 Roo.each(this.navItems, function(e) {
6023 if (e.tabId != except.tabId && e.was_active) {
6024 e.was_active = false;
6031 getWasActive : function ()
6034 Roo.each(this.navItems, function(e) {
6049 Roo.apply(Roo.bootstrap.NavGroup, {
6053 * register a Navigation Group
6054 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6056 register : function(navgrp)
6058 this.groups[navgrp.navId] = navgrp;
6062 * fetch a Navigation Group based on the navigation ID
6063 * @param {string} the navgroup to add
6064 * @returns {Roo.bootstrap.NavGroup} the navgroup
6066 get: function(navId) {
6067 if (typeof(this.groups[navId]) == 'undefined') {
6069 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6071 return this.groups[navId] ;
6086 * @class Roo.bootstrap.NavItem
6087 * @extends Roo.bootstrap.Component
6088 * Bootstrap Navbar.NavItem class
6089 * @cfg {String} href link to
6090 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6091 * @cfg {Boolean} button_outline show and outlined button
6092 * @cfg {String} html content of button
6093 * @cfg {String} badge text inside badge
6094 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6095 * @cfg {String} glyphicon DEPRICATED - use fa
6096 * @cfg {String} icon DEPRICATED - use fa
6097 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6098 * @cfg {Boolean} active Is item active
6099 * @cfg {Boolean} disabled Is item disabled
6100 * @cfg {String} linkcls Link Class
6101 * @cfg {Boolean} preventDefault (true | false) default false
6102 * @cfg {String} tabId the tab that this item activates.
6103 * @cfg {String} tagtype (a|span) render as a href or span?
6104 * @cfg {Boolean} animateRef (true|false) link to element default false
6107 * Create a new Navbar Item
6108 * @param {Object} config The config object
6110 Roo.bootstrap.NavItem = function(config){
6111 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6116 * The raw click event for the entire grid.
6117 * @param {Roo.EventObject} e
6122 * Fires when the active item active state changes
6123 * @param {Roo.bootstrap.NavItem} this
6124 * @param {boolean} state the new state
6130 * Fires when scroll to element
6131 * @param {Roo.bootstrap.NavItem} this
6132 * @param {Object} options
6133 * @param {Roo.EventObject} e
6141 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6150 preventDefault : false,
6158 button_outline : false,
6162 getAutoCreate : function(){
6169 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6172 cfg.cls += ' active' ;
6174 if (this.disabled) {
6175 cfg.cls += ' disabled';
6179 if (this.button_weight.length) {
6180 cfg.tag = this.href ? 'a' : 'button';
6181 cfg.html = this.html || '';
6182 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6184 cfg.href = this.href;
6187 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6189 cfg.cls += " nav-html";
6192 // menu .. should add dropdown-menu class - so no need for carat..
6194 if (this.badge !== '') {
6196 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6201 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6205 href : this.href || "#",
6206 html: this.html || '',
6210 if (this.tagtype == 'a') {
6211 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6215 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6216 } else if (this.fa) {
6217 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6218 } else if(this.glyphicon) {
6219 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6221 cfg.cn[0].cls += " nav-html";
6225 cfg.cn[0].html += " <span class='caret'></span>";
6229 if (this.badge !== '') {
6230 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6238 onRender : function(ct, position)
6240 // Roo.log("Call onRender: " + this.xtype);
6241 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6245 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6246 this.navLink = this.el.select('.nav-link',true).first();
6247 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6252 initEvents: function()
6254 if (typeof (this.menu) != 'undefined') {
6255 this.menu.parentType = this.xtype;
6256 this.menu.triggerEl = this.el;
6257 this.menu = this.addxtype(Roo.apply({}, this.menu));
6260 this.el.on('click', this.onClick, this);
6262 //if(this.tagtype == 'span'){
6263 // this.el.select('span',true).on('click', this.onClick, this);
6266 // at this point parent should be available..
6267 this.parent().register(this);
6270 onClick : function(e)
6272 if (e.getTarget('.dropdown-menu-item')) {
6273 // did you click on a menu itemm.... - then don't trigger onclick..
6278 this.preventDefault ||
6281 Roo.log("NavItem - prevent Default?");
6285 if (this.disabled) {
6289 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6290 if (tg && tg.transition) {
6291 Roo.log("waiting for the transitionend");
6297 //Roo.log("fire event clicked");
6298 if(this.fireEvent('click', this, e) === false){
6302 if(this.tagtype == 'span'){
6306 //Roo.log(this.href);
6307 var ael = this.el.select('a',true).first();
6310 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6311 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6312 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6313 return; // ignore... - it's a 'hash' to another page.
6315 Roo.log("NavItem - prevent Default?");
6317 this.scrollToElement(e);
6321 var p = this.parent();
6323 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6324 if (typeof(p.setActiveItem) !== 'undefined') {
6325 p.setActiveItem(this);
6329 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6330 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6331 // remove the collapsed menu expand...
6332 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6336 isActive: function () {
6339 setActive : function(state, fire, is_was_active)
6341 if (this.active && !state && this.navId) {
6342 this.was_active = true;
6343 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6345 nv.clearWasActive(this);
6349 this.active = state;
6352 this.el.removeClass('active');
6353 this.navLink ? this.navLink.removeClass('active') : false;
6354 } else if (!this.el.hasClass('active')) {
6356 this.el.addClass('active');
6357 if (Roo.bootstrap.version == 4 && this.navLink ) {
6358 this.navLink.addClass('active');
6363 this.fireEvent('changed', this, state);
6366 // show a panel if it's registered and related..
6368 if (!this.navId || !this.tabId || !state || is_was_active) {
6372 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6376 var pan = tg.getPanelByName(this.tabId);
6380 // if we can not flip to new panel - go back to old nav highlight..
6381 if (false == tg.showPanel(pan)) {
6382 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6384 var onav = nv.getWasActive();
6386 onav.setActive(true, false, true);
6395 // this should not be here...
6396 setDisabled : function(state)
6398 this.disabled = state;
6400 this.el.removeClass('disabled');
6401 } else if (!this.el.hasClass('disabled')) {
6402 this.el.addClass('disabled');
6408 * Fetch the element to display the tooltip on.
6409 * @return {Roo.Element} defaults to this.el
6411 tooltipEl : function()
6413 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6416 scrollToElement : function(e)
6418 var c = document.body;
6421 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6423 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6424 c = document.documentElement;
6427 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6433 var o = target.calcOffsetsTo(c);
6440 this.fireEvent('scrollto', this, options, e);
6442 Roo.get(c).scrollTo('top', options.value, true);
6447 * Set the HTML (text content) of the item
6448 * @param {string} html content for the nav item
6450 setHtml : function(html)
6453 this.htmlEl.dom.innerHTML = html;
6465 * <span> icon </span>
6466 * <span> text </span>
6467 * <span>badge </span>
6471 * @class Roo.bootstrap.NavSidebarItem
6472 * @extends Roo.bootstrap.NavItem
6473 * Bootstrap Navbar.NavSidebarItem class
6474 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6475 * {Boolean} open is the menu open
6476 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6477 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6478 * {String} buttonSize (sm|md|lg)the extra classes for the button
6479 * {Boolean} showArrow show arrow next to the text (default true)
6481 * Create a new Navbar Button
6482 * @param {Object} config The config object
6484 Roo.bootstrap.NavSidebarItem = function(config){
6485 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6490 * The raw click event for the entire grid.
6491 * @param {Roo.EventObject} e
6496 * Fires when the active item active state changes
6497 * @param {Roo.bootstrap.NavSidebarItem} this
6498 * @param {boolean} state the new state
6506 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6508 badgeWeight : 'default',
6514 buttonWeight : 'default',
6520 getAutoCreate : function(){
6525 href : this.href || '#',
6531 if(this.buttonView){
6534 href : this.href || '#',
6535 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6548 cfg.cls += ' active';
6551 if (this.disabled) {
6552 cfg.cls += ' disabled';
6555 cfg.cls += ' open x-open';
6558 if (this.glyphicon || this.icon) {
6559 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6560 a.cn.push({ tag : 'i', cls : c }) ;
6563 if(!this.buttonView){
6566 html : this.html || ''
6573 if (this.badge !== '') {
6574 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6580 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6583 a.cls += ' dropdown-toggle treeview' ;
6589 initEvents : function()
6591 if (typeof (this.menu) != 'undefined') {
6592 this.menu.parentType = this.xtype;
6593 this.menu.triggerEl = this.el;
6594 this.menu = this.addxtype(Roo.apply({}, this.menu));
6597 this.el.on('click', this.onClick, this);
6599 if(this.badge !== ''){
6600 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6605 onClick : function(e)
6612 if(this.preventDefault){
6616 this.fireEvent('click', this, e);
6619 disable : function()
6621 this.setDisabled(true);
6626 this.setDisabled(false);
6629 setDisabled : function(state)
6631 if(this.disabled == state){
6635 this.disabled = state;
6638 this.el.addClass('disabled');
6642 this.el.removeClass('disabled');
6647 setActive : function(state)
6649 if(this.active == state){
6653 this.active = state;
6656 this.el.addClass('active');
6660 this.el.removeClass('active');
6665 isActive: function ()
6670 setBadge : function(str)
6676 this.badgeEl.dom.innerHTML = str;
6691 Roo.namespace('Roo.bootstrap.breadcrumb');
6695 * @class Roo.bootstrap.breadcrumb.Nav
6696 * @extends Roo.bootstrap.Component
6697 * Bootstrap Breadcrumb Nav Class
6699 * @children Roo.bootstrap.breadcrumb.Item
6702 * Create a new breadcrumb.Nav
6703 * @param {Object} config The config object
6707 Roo.bootstrap.breadcrumb.Nav = function(config){
6708 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6713 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6715 getAutoCreate : function()
6732 initEvents: function()
6734 this.olEl = this.el.select('ol',true).first();
6736 getChildContainer : function()
6752 * @class Roo.bootstrap.breadcrumb.Nav
6753 * @extends Roo.bootstrap.Component
6754 * Bootstrap Breadcrumb Nav Class
6756 * @children Roo.bootstrap.breadcrumb.Component
6757 * @cfg {String} html the content of the link.
6758 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6759 * @cfg {Boolean} active is it active
6763 * Create a new breadcrumb.Nav
6764 * @param {Object} config The config object
6767 Roo.bootstrap.breadcrumb.Item = function(config){
6768 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6773 * The img click event for the img.
6774 * @param {Roo.EventObject} e
6781 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6786 getAutoCreate : function()
6791 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6793 if (this.href !== false) {
6800 cfg.html = this.html;
6806 initEvents: function()
6809 this.el.select('a', true).first().on('click',this.onClick, this)
6813 onClick : function(e)
6816 this.fireEvent('click',this, e);
6829 * @class Roo.bootstrap.Row
6830 * @extends Roo.bootstrap.Component
6831 * Bootstrap Row class (contains columns...)
6835 * @param {Object} config The config object
6838 Roo.bootstrap.Row = function(config){
6839 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6842 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6844 getAutoCreate : function(){
6863 * @class Roo.bootstrap.Pagination
6864 * @extends Roo.bootstrap.Component
6865 * Bootstrap Pagination class
6866 * @cfg {String} size xs | sm | md | lg
6867 * @cfg {Boolean} inverse false | true
6870 * Create a new Pagination
6871 * @param {Object} config The config object
6874 Roo.bootstrap.Pagination = function(config){
6875 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6878 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6884 getAutoCreate : function(){
6890 cfg.cls += ' inverse';
6896 cfg.cls += " " + this.cls;
6914 * @class Roo.bootstrap.PaginationItem
6915 * @extends Roo.bootstrap.Component
6916 * Bootstrap PaginationItem class
6917 * @cfg {String} html text
6918 * @cfg {String} href the link
6919 * @cfg {Boolean} preventDefault (true | false) default true
6920 * @cfg {Boolean} active (true | false) default false
6921 * @cfg {Boolean} disabled default false
6925 * Create a new PaginationItem
6926 * @param {Object} config The config object
6930 Roo.bootstrap.PaginationItem = function(config){
6931 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6936 * The raw click event for the entire grid.
6937 * @param {Roo.EventObject} e
6943 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6947 preventDefault: true,
6952 getAutoCreate : function(){
6958 href : this.href ? this.href : '#',
6959 html : this.html ? this.html : ''
6969 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6973 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6979 initEvents: function() {
6981 this.el.on('click', this.onClick, this);
6984 onClick : function(e)
6986 Roo.log('PaginationItem on click ');
6987 if(this.preventDefault){
6995 this.fireEvent('click', this, e);
7011 * @class Roo.bootstrap.Slider
7012 * @extends Roo.bootstrap.Component
7013 * Bootstrap Slider class
7016 * Create a new Slider
7017 * @param {Object} config The config object
7020 Roo.bootstrap.Slider = function(config){
7021 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7024 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7026 getAutoCreate : function(){
7030 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7034 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7046 * Ext JS Library 1.1.1
7047 * Copyright(c) 2006-2007, Ext JS, LLC.
7049 * Originally Released Under LGPL - original licence link has changed is not relivant.
7052 * <script type="text/javascript">
7057 * @class Roo.grid.ColumnModel
7058 * @extends Roo.util.Observable
7059 * This is the default implementation of a ColumnModel used by the Grid. It defines
7060 * the columns in the grid.
7063 var colModel = new Roo.grid.ColumnModel([
7064 {header: "Ticker", width: 60, sortable: true, locked: true},
7065 {header: "Company Name", width: 150, sortable: true},
7066 {header: "Market Cap.", width: 100, sortable: true},
7067 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7068 {header: "Employees", width: 100, sortable: true, resizable: false}
7073 * The config options listed for this class are options which may appear in each
7074 * individual column definition.
7075 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7077 * @param {Object} config An Array of column config objects. See this class's
7078 * config objects for details.
7080 Roo.grid.ColumnModel = function(config){
7082 * The config passed into the constructor
7084 this.config = config;
7087 // if no id, create one
7088 // if the column does not have a dataIndex mapping,
7089 // map it to the order it is in the config
7090 for(var i = 0, len = config.length; i < len; i++){
7092 if(typeof c.dataIndex == "undefined"){
7095 if(typeof c.renderer == "string"){
7096 c.renderer = Roo.util.Format[c.renderer];
7098 if(typeof c.id == "undefined"){
7101 if(c.editor && c.editor.xtype){
7102 c.editor = Roo.factory(c.editor, Roo.grid);
7104 if(c.editor && c.editor.isFormField){
7105 c.editor = new Roo.grid.GridEditor(c.editor);
7107 this.lookup[c.id] = c;
7111 * The width of columns which have no width specified (defaults to 100)
7114 this.defaultWidth = 100;
7117 * Default sortable of columns which have no sortable specified (defaults to false)
7120 this.defaultSortable = false;
7124 * @event widthchange
7125 * Fires when the width of a column changes.
7126 * @param {ColumnModel} this
7127 * @param {Number} columnIndex The column index
7128 * @param {Number} newWidth The new width
7130 "widthchange": true,
7132 * @event headerchange
7133 * Fires when the text of a header changes.
7134 * @param {ColumnModel} this
7135 * @param {Number} columnIndex The column index
7136 * @param {Number} newText The new header text
7138 "headerchange": true,
7140 * @event hiddenchange
7141 * Fires when a column is hidden or "unhidden".
7142 * @param {ColumnModel} this
7143 * @param {Number} columnIndex The column index
7144 * @param {Boolean} hidden true if hidden, false otherwise
7146 "hiddenchange": true,
7148 * @event columnmoved
7149 * Fires when a column is moved.
7150 * @param {ColumnModel} this
7151 * @param {Number} oldIndex
7152 * @param {Number} newIndex
7154 "columnmoved" : true,
7156 * @event columlockchange
7157 * Fires when a column's locked state is changed
7158 * @param {ColumnModel} this
7159 * @param {Number} colIndex
7160 * @param {Boolean} locked true if locked
7162 "columnlockchange" : true
7164 Roo.grid.ColumnModel.superclass.constructor.call(this);
7166 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7168 * @cfg {String} header The header text to display in the Grid view.
7171 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7172 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7173 * specified, the column's index is used as an index into the Record's data Array.
7176 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7177 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7180 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7181 * Defaults to the value of the {@link #defaultSortable} property.
7182 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7185 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7188 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7191 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7194 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7197 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7198 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7199 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7200 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7203 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7206 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7209 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7212 * @cfg {String} cursor (Optional)
7215 * @cfg {String} tooltip (Optional)
7218 * @cfg {Number} xs (Optional)
7221 * @cfg {Number} sm (Optional)
7224 * @cfg {Number} md (Optional)
7227 * @cfg {Number} lg (Optional)
7230 * Returns the id of the column at the specified index.
7231 * @param {Number} index The column index
7232 * @return {String} the id
7234 getColumnId : function(index){
7235 return this.config[index].id;
7239 * Returns the column for a specified id.
7240 * @param {String} id The column id
7241 * @return {Object} the column
7243 getColumnById : function(id){
7244 return this.lookup[id];
7249 * Returns the column for a specified dataIndex.
7250 * @param {String} dataIndex The column dataIndex
7251 * @return {Object|Boolean} the column or false if not found
7253 getColumnByDataIndex: function(dataIndex){
7254 var index = this.findColumnIndex(dataIndex);
7255 return index > -1 ? this.config[index] : false;
7259 * Returns the index for a specified column id.
7260 * @param {String} id The column id
7261 * @return {Number} the index, or -1 if not found
7263 getIndexById : function(id){
7264 for(var i = 0, len = this.config.length; i < len; i++){
7265 if(this.config[i].id == id){
7273 * Returns the index for a specified column dataIndex.
7274 * @param {String} dataIndex The column dataIndex
7275 * @return {Number} the index, or -1 if not found
7278 findColumnIndex : function(dataIndex){
7279 for(var i = 0, len = this.config.length; i < len; i++){
7280 if(this.config[i].dataIndex == dataIndex){
7288 moveColumn : function(oldIndex, newIndex){
7289 var c = this.config[oldIndex];
7290 this.config.splice(oldIndex, 1);
7291 this.config.splice(newIndex, 0, c);
7292 this.dataMap = null;
7293 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7296 isLocked : function(colIndex){
7297 return this.config[colIndex].locked === true;
7300 setLocked : function(colIndex, value, suppressEvent){
7301 if(this.isLocked(colIndex) == value){
7304 this.config[colIndex].locked = value;
7306 this.fireEvent("columnlockchange", this, colIndex, value);
7310 getTotalLockedWidth : function(){
7312 for(var i = 0; i < this.config.length; i++){
7313 if(this.isLocked(i) && !this.isHidden(i)){
7314 this.totalWidth += this.getColumnWidth(i);
7320 getLockedCount : function(){
7321 for(var i = 0, len = this.config.length; i < len; i++){
7322 if(!this.isLocked(i)){
7327 return this.config.length;
7331 * Returns the number of columns.
7334 getColumnCount : function(visibleOnly){
7335 if(visibleOnly === true){
7337 for(var i = 0, len = this.config.length; i < len; i++){
7338 if(!this.isHidden(i)){
7344 return this.config.length;
7348 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7349 * @param {Function} fn
7350 * @param {Object} scope (optional)
7351 * @return {Array} result
7353 getColumnsBy : function(fn, scope){
7355 for(var i = 0, len = this.config.length; i < len; i++){
7356 var c = this.config[i];
7357 if(fn.call(scope||this, c, i) === true){
7365 * Returns true if the specified column is sortable.
7366 * @param {Number} col The column index
7369 isSortable : function(col){
7370 if(typeof this.config[col].sortable == "undefined"){
7371 return this.defaultSortable;
7373 return this.config[col].sortable;
7377 * Returns the rendering (formatting) function defined for the column.
7378 * @param {Number} col The column index.
7379 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7381 getRenderer : function(col){
7382 if(!this.config[col].renderer){
7383 return Roo.grid.ColumnModel.defaultRenderer;
7385 return this.config[col].renderer;
7389 * Sets the rendering (formatting) function for a column.
7390 * @param {Number} col The column index
7391 * @param {Function} fn The function to use to process the cell's raw data
7392 * to return HTML markup for the grid view. The render function is called with
7393 * the following parameters:<ul>
7394 * <li>Data value.</li>
7395 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7396 * <li>css A CSS style string to apply to the table cell.</li>
7397 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7398 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7399 * <li>Row index</li>
7400 * <li>Column index</li>
7401 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7403 setRenderer : function(col, fn){
7404 this.config[col].renderer = fn;
7408 * Returns the width for the specified column.
7409 * @param {Number} col The column index
7412 getColumnWidth : function(col){
7413 return this.config[col].width * 1 || this.defaultWidth;
7417 * Sets the width for a column.
7418 * @param {Number} col The column index
7419 * @param {Number} width The new width
7421 setColumnWidth : function(col, width, suppressEvent){
7422 this.config[col].width = width;
7423 this.totalWidth = null;
7425 this.fireEvent("widthchange", this, col, width);
7430 * Returns the total width of all columns.
7431 * @param {Boolean} includeHidden True to include hidden column widths
7434 getTotalWidth : function(includeHidden){
7435 if(!this.totalWidth){
7436 this.totalWidth = 0;
7437 for(var i = 0, len = this.config.length; i < len; i++){
7438 if(includeHidden || !this.isHidden(i)){
7439 this.totalWidth += this.getColumnWidth(i);
7443 return this.totalWidth;
7447 * Returns the header for the specified column.
7448 * @param {Number} col The column index
7451 getColumnHeader : function(col){
7452 return this.config[col].header;
7456 * Sets the header for a column.
7457 * @param {Number} col The column index
7458 * @param {String} header The new header
7460 setColumnHeader : function(col, header){
7461 this.config[col].header = header;
7462 this.fireEvent("headerchange", this, col, header);
7466 * Returns the tooltip for the specified column.
7467 * @param {Number} col The column index
7470 getColumnTooltip : function(col){
7471 return this.config[col].tooltip;
7474 * Sets the tooltip for a column.
7475 * @param {Number} col The column index
7476 * @param {String} tooltip The new tooltip
7478 setColumnTooltip : function(col, tooltip){
7479 this.config[col].tooltip = tooltip;
7483 * Returns the dataIndex for the specified column.
7484 * @param {Number} col The column index
7487 getDataIndex : function(col){
7488 return this.config[col].dataIndex;
7492 * Sets the dataIndex for a column.
7493 * @param {Number} col The column index
7494 * @param {Number} dataIndex The new dataIndex
7496 setDataIndex : function(col, dataIndex){
7497 this.config[col].dataIndex = dataIndex;
7503 * Returns true if the cell is editable.
7504 * @param {Number} colIndex The column index
7505 * @param {Number} rowIndex The row index - this is nto actually used..?
7508 isCellEditable : function(colIndex, rowIndex){
7509 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7513 * Returns the editor defined for the cell/column.
7514 * return false or null to disable editing.
7515 * @param {Number} colIndex The column index
7516 * @param {Number} rowIndex The row index
7519 getCellEditor : function(colIndex, rowIndex){
7520 return this.config[colIndex].editor;
7524 * Sets if a column is editable.
7525 * @param {Number} col The column index
7526 * @param {Boolean} editable True if the column is editable
7528 setEditable : function(col, editable){
7529 this.config[col].editable = editable;
7534 * Returns true if the column is hidden.
7535 * @param {Number} colIndex The column index
7538 isHidden : function(colIndex){
7539 return this.config[colIndex].hidden;
7544 * Returns true if the column width cannot be changed
7546 isFixed : function(colIndex){
7547 return this.config[colIndex].fixed;
7551 * Returns true if the column can be resized
7554 isResizable : function(colIndex){
7555 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7558 * Sets if a column is hidden.
7559 * @param {Number} colIndex The column index
7560 * @param {Boolean} hidden True if the column is hidden
7562 setHidden : function(colIndex, hidden){
7563 this.config[colIndex].hidden = hidden;
7564 this.totalWidth = null;
7565 this.fireEvent("hiddenchange", this, colIndex, hidden);
7569 * Sets the editor for a column.
7570 * @param {Number} col The column index
7571 * @param {Object} editor The editor object
7573 setEditor : function(col, editor){
7574 this.config[col].editor = editor;
7578 Roo.grid.ColumnModel.defaultRenderer = function(value)
7580 if(typeof value == "object") {
7583 if(typeof value == "string" && value.length < 1){
7587 return String.format("{0}", value);
7590 // Alias for backwards compatibility
7591 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7594 * Ext JS Library 1.1.1
7595 * Copyright(c) 2006-2007, Ext JS, LLC.
7597 * Originally Released Under LGPL - original licence link has changed is not relivant.
7600 * <script type="text/javascript">
7604 * @class Roo.LoadMask
7605 * A simple utility class for generically masking elements while loading data. If the element being masked has
7606 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7607 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7608 * element's UpdateManager load indicator and will be destroyed after the initial load.
7610 * Create a new LoadMask
7611 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7612 * @param {Object} config The config object
7614 Roo.LoadMask = function(el, config){
7615 this.el = Roo.get(el);
7616 Roo.apply(this, config);
7618 this.store.on('beforeload', this.onBeforeLoad, this);
7619 this.store.on('load', this.onLoad, this);
7620 this.store.on('loadexception', this.onLoadException, this);
7621 this.removeMask = false;
7623 var um = this.el.getUpdateManager();
7624 um.showLoadIndicator = false; // disable the default indicator
7625 um.on('beforeupdate', this.onBeforeLoad, this);
7626 um.on('update', this.onLoad, this);
7627 um.on('failure', this.onLoad, this);
7628 this.removeMask = true;
7632 Roo.LoadMask.prototype = {
7634 * @cfg {Boolean} removeMask
7635 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7636 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7640 * The text to display in a centered loading message box (defaults to 'Loading...')
7644 * @cfg {String} msgCls
7645 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7647 msgCls : 'x-mask-loading',
7650 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7656 * Disables the mask to prevent it from being displayed
7658 disable : function(){
7659 this.disabled = true;
7663 * Enables the mask so that it can be displayed
7665 enable : function(){
7666 this.disabled = false;
7669 onLoadException : function()
7673 if (typeof(arguments[3]) != 'undefined') {
7674 Roo.MessageBox.alert("Error loading",arguments[3]);
7678 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7679 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7686 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7691 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7695 onBeforeLoad : function(){
7697 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7702 destroy : function(){
7704 this.store.un('beforeload', this.onBeforeLoad, this);
7705 this.store.un('load', this.onLoad, this);
7706 this.store.un('loadexception', this.onLoadException, this);
7708 var um = this.el.getUpdateManager();
7709 um.un('beforeupdate', this.onBeforeLoad, this);
7710 um.un('update', this.onLoad, this);
7711 um.un('failure', this.onLoad, this);
7722 * @class Roo.bootstrap.Table
7723 * @extends Roo.bootstrap.Component
7724 * Bootstrap Table class
7725 * @cfg {String} cls table class
7726 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7727 * @cfg {String} bgcolor Specifies the background color for a table
7728 * @cfg {Number} border Specifies whether the table cells should have borders or not
7729 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7730 * @cfg {Number} cellspacing Specifies the space between cells
7731 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7732 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7733 * @cfg {String} sortable Specifies that the table should be sortable
7734 * @cfg {String} summary Specifies a summary of the content of a table
7735 * @cfg {Number} width Specifies the width of a table
7736 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7738 * @cfg {boolean} striped Should the rows be alternative striped
7739 * @cfg {boolean} bordered Add borders to the table
7740 * @cfg {boolean} hover Add hover highlighting
7741 * @cfg {boolean} condensed Format condensed
7742 * @cfg {boolean} responsive Format condensed
7743 * @cfg {Boolean} loadMask (true|false) default false
7744 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7745 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7746 * @cfg {Boolean} rowSelection (true|false) default false
7747 * @cfg {Boolean} cellSelection (true|false) default false
7748 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7749 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7750 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7751 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7755 * Create a new Table
7756 * @param {Object} config The config object
7759 Roo.bootstrap.Table = function(config){
7760 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7765 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7766 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7767 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7768 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7770 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7772 this.sm.grid = this;
7773 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7774 this.sm = this.selModel;
7775 this.sm.xmodule = this.xmodule || false;
7778 if (this.cm && typeof(this.cm.config) == 'undefined') {
7779 this.colModel = new Roo.grid.ColumnModel(this.cm);
7780 this.cm = this.colModel;
7781 this.cm.xmodule = this.xmodule || false;
7784 this.store= Roo.factory(this.store, Roo.data);
7785 this.ds = this.store;
7786 this.ds.xmodule = this.xmodule || false;
7789 if (this.footer && this.store) {
7790 this.footer.dataSource = this.ds;
7791 this.footer = Roo.factory(this.footer);
7798 * Fires when a cell is clicked
7799 * @param {Roo.bootstrap.Table} this
7800 * @param {Roo.Element} el
7801 * @param {Number} rowIndex
7802 * @param {Number} columnIndex
7803 * @param {Roo.EventObject} e
7807 * @event celldblclick
7808 * Fires when a cell is double clicked
7809 * @param {Roo.bootstrap.Table} this
7810 * @param {Roo.Element} el
7811 * @param {Number} rowIndex
7812 * @param {Number} columnIndex
7813 * @param {Roo.EventObject} e
7815 "celldblclick" : true,
7818 * Fires when a row is clicked
7819 * @param {Roo.bootstrap.Table} this
7820 * @param {Roo.Element} el
7821 * @param {Number} rowIndex
7822 * @param {Roo.EventObject} e
7826 * @event rowdblclick
7827 * Fires when a row is double clicked
7828 * @param {Roo.bootstrap.Table} this
7829 * @param {Roo.Element} el
7830 * @param {Number} rowIndex
7831 * @param {Roo.EventObject} e
7833 "rowdblclick" : true,
7836 * Fires when a mouseover occur
7837 * @param {Roo.bootstrap.Table} this
7838 * @param {Roo.Element} el
7839 * @param {Number} rowIndex
7840 * @param {Number} columnIndex
7841 * @param {Roo.EventObject} e
7846 * Fires when a mouseout occur
7847 * @param {Roo.bootstrap.Table} this
7848 * @param {Roo.Element} el
7849 * @param {Number} rowIndex
7850 * @param {Number} columnIndex
7851 * @param {Roo.EventObject} e
7856 * Fires when a row is rendered, so you can change add a style to it.
7857 * @param {Roo.bootstrap.Table} this
7858 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7862 * @event rowsrendered
7863 * Fires when all the rows have been rendered
7864 * @param {Roo.bootstrap.Table} this
7866 'rowsrendered' : true,
7868 * @event contextmenu
7869 * The raw contextmenu event for the entire grid.
7870 * @param {Roo.EventObject} e
7872 "contextmenu" : true,
7874 * @event rowcontextmenu
7875 * Fires when a row is right clicked
7876 * @param {Roo.bootstrap.Table} this
7877 * @param {Number} rowIndex
7878 * @param {Roo.EventObject} e
7880 "rowcontextmenu" : true,
7882 * @event cellcontextmenu
7883 * Fires when a cell is right clicked
7884 * @param {Roo.bootstrap.Table} this
7885 * @param {Number} rowIndex
7886 * @param {Number} cellIndex
7887 * @param {Roo.EventObject} e
7889 "cellcontextmenu" : true,
7891 * @event headercontextmenu
7892 * Fires when a header is right clicked
7893 * @param {Roo.bootstrap.Table} this
7894 * @param {Number} columnIndex
7895 * @param {Roo.EventObject} e
7897 "headercontextmenu" : true
7901 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7927 rowSelection : false,
7928 cellSelection : false,
7931 // Roo.Element - the tbody
7933 // Roo.Element - thead element
7936 container: false, // used by gridpanel...
7942 auto_hide_footer : false,
7944 getAutoCreate : function()
7946 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7953 if (this.scrollBody) {
7954 cfg.cls += ' table-body-fixed';
7957 cfg.cls += ' table-striped';
7961 cfg.cls += ' table-hover';
7963 if (this.bordered) {
7964 cfg.cls += ' table-bordered';
7966 if (this.condensed) {
7967 cfg.cls += ' table-condensed';
7969 if (this.responsive) {
7970 cfg.cls += ' table-responsive';
7974 cfg.cls+= ' ' +this.cls;
7977 // this lot should be simplifed...
7990 ].forEach(function(k) {
7998 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8001 if(this.store || this.cm){
8002 if(this.headerShow){
8003 cfg.cn.push(this.renderHeader());
8006 cfg.cn.push(this.renderBody());
8008 if(this.footerShow){
8009 cfg.cn.push(this.renderFooter());
8011 // where does this come from?
8012 //cfg.cls+= ' TableGrid';
8015 return { cn : [ cfg ] };
8018 initEvents : function()
8020 if(!this.store || !this.cm){
8023 if (this.selModel) {
8024 this.selModel.initEvents();
8028 //Roo.log('initEvents with ds!!!!');
8030 this.mainBody = this.el.select('tbody', true).first();
8031 this.mainHead = this.el.select('thead', true).first();
8032 this.mainFoot = this.el.select('tfoot', true).first();
8038 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8039 e.on('click', _this.sort, _this);
8042 this.mainBody.on("click", this.onClick, this);
8043 this.mainBody.on("dblclick", this.onDblClick, this);
8045 // why is this done????? = it breaks dialogs??
8046 //this.parent().el.setStyle('position', 'relative');
8050 this.footer.parentId = this.id;
8051 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8054 this.el.select('tfoot tr td').first().addClass('hide');
8059 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8062 this.store.on('load', this.onLoad, this);
8063 this.store.on('beforeload', this.onBeforeLoad, this);
8064 this.store.on('update', this.onUpdate, this);
8065 this.store.on('add', this.onAdd, this);
8066 this.store.on("clear", this.clear, this);
8068 this.el.on("contextmenu", this.onContextMenu, this);
8070 this.mainBody.on('scroll', this.onBodyScroll, this);
8072 this.cm.on("headerchange", this.onHeaderChange, this);
8074 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8078 onContextMenu : function(e, t)
8080 this.processEvent("contextmenu", e);
8083 processEvent : function(name, e)
8085 if (name != 'touchstart' ) {
8086 this.fireEvent(name, e);
8089 var t = e.getTarget();
8091 var cell = Roo.get(t);
8097 if(cell.findParent('tfoot', false, true)){
8101 if(cell.findParent('thead', false, true)){
8103 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8104 cell = Roo.get(t).findParent('th', false, true);
8106 Roo.log("failed to find th in thead?");
8107 Roo.log(e.getTarget());
8112 var cellIndex = cell.dom.cellIndex;
8114 var ename = name == 'touchstart' ? 'click' : name;
8115 this.fireEvent("header" + ename, this, cellIndex, e);
8120 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8121 cell = Roo.get(t).findParent('td', false, true);
8123 Roo.log("failed to find th in tbody?");
8124 Roo.log(e.getTarget());
8129 var row = cell.findParent('tr', false, true);
8130 var cellIndex = cell.dom.cellIndex;
8131 var rowIndex = row.dom.rowIndex - 1;
8135 this.fireEvent("row" + name, this, rowIndex, e);
8139 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8145 onMouseover : function(e, el)
8147 var cell = Roo.get(el);
8153 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8154 cell = cell.findParent('td', false, true);
8157 var row = cell.findParent('tr', false, true);
8158 var cellIndex = cell.dom.cellIndex;
8159 var rowIndex = row.dom.rowIndex - 1; // start from 0
8161 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8165 onMouseout : function(e, el)
8167 var cell = Roo.get(el);
8173 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8174 cell = cell.findParent('td', false, true);
8177 var row = cell.findParent('tr', false, true);
8178 var cellIndex = cell.dom.cellIndex;
8179 var rowIndex = row.dom.rowIndex - 1; // start from 0
8181 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8185 onClick : function(e, el)
8187 var cell = Roo.get(el);
8189 if(!cell || (!this.cellSelection && !this.rowSelection)){
8193 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8194 cell = cell.findParent('td', false, true);
8197 if(!cell || typeof(cell) == 'undefined'){
8201 var row = cell.findParent('tr', false, true);
8203 if(!row || typeof(row) == 'undefined'){
8207 var cellIndex = cell.dom.cellIndex;
8208 var rowIndex = this.getRowIndex(row);
8210 // why??? - should these not be based on SelectionModel?
8211 if(this.cellSelection){
8212 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8215 if(this.rowSelection){
8216 this.fireEvent('rowclick', this, row, rowIndex, e);
8222 onDblClick : function(e,el)
8224 var cell = Roo.get(el);
8226 if(!cell || (!this.cellSelection && !this.rowSelection)){
8230 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8231 cell = cell.findParent('td', false, true);
8234 if(!cell || typeof(cell) == 'undefined'){
8238 var row = cell.findParent('tr', false, true);
8240 if(!row || typeof(row) == 'undefined'){
8244 var cellIndex = cell.dom.cellIndex;
8245 var rowIndex = this.getRowIndex(row);
8247 if(this.cellSelection){
8248 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8251 if(this.rowSelection){
8252 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8256 sort : function(e,el)
8258 var col = Roo.get(el);
8260 if(!col.hasClass('sortable')){
8264 var sort = col.attr('sort');
8267 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8271 this.store.sortInfo = {field : sort, direction : dir};
8274 Roo.log("calling footer first");
8275 this.footer.onClick('first');
8278 this.store.load({ params : { start : 0 } });
8282 renderHeader : function()
8290 this.totalWidth = 0;
8292 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8294 var config = cm.config[i];
8298 cls : 'x-hcol-' + i,
8300 html: cm.getColumnHeader(i)
8305 if(typeof(config.sortable) != 'undefined' && config.sortable){
8307 c.html = '<i class="glyphicon"></i>' + c.html;
8310 // could use BS4 hidden-..-down
8312 if(typeof(config.lgHeader) != 'undefined'){
8313 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8316 if(typeof(config.mdHeader) != 'undefined'){
8317 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8320 if(typeof(config.smHeader) != 'undefined'){
8321 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8324 if(typeof(config.xsHeader) != 'undefined'){
8325 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8332 if(typeof(config.tooltip) != 'undefined'){
8333 c.tooltip = config.tooltip;
8336 if(typeof(config.colspan) != 'undefined'){
8337 c.colspan = config.colspan;
8340 if(typeof(config.hidden) != 'undefined' && config.hidden){
8341 c.style += ' display:none;';
8344 if(typeof(config.dataIndex) != 'undefined'){
8345 c.sort = config.dataIndex;
8350 if(typeof(config.align) != 'undefined' && config.align.length){
8351 c.style += ' text-align:' + config.align + ';';
8354 if(typeof(config.width) != 'undefined'){
8355 c.style += ' width:' + config.width + 'px;';
8356 this.totalWidth += config.width;
8358 this.totalWidth += 100; // assume minimum of 100 per column?
8361 if(typeof(config.cls) != 'undefined'){
8362 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8365 ['xs','sm','md','lg'].map(function(size){
8367 if(typeof(config[size]) == 'undefined'){
8371 if (!config[size]) { // 0 = hidden
8372 // BS 4 '0' is treated as hide that column and below.
8373 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8377 c.cls += ' col-' + size + '-' + config[size] + (
8378 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8390 renderBody : function()
8400 colspan : this.cm.getColumnCount()
8410 renderFooter : function()
8420 colspan : this.cm.getColumnCount()
8434 // Roo.log('ds onload');
8439 var ds = this.store;
8441 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8442 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8443 if (_this.store.sortInfo) {
8445 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8446 e.select('i', true).addClass(['glyphicon-arrow-up']);
8449 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8450 e.select('i', true).addClass(['glyphicon-arrow-down']);
8455 var tbody = this.mainBody;
8457 if(ds.getCount() > 0){
8458 ds.data.each(function(d,rowIndex){
8459 var row = this.renderRow(cm, ds, rowIndex);
8461 tbody.createChild(row);
8465 if(row.cellObjects.length){
8466 Roo.each(row.cellObjects, function(r){
8467 _this.renderCellObject(r);
8474 var tfoot = this.el.select('tfoot', true).first();
8476 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8478 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8480 var total = this.ds.getTotalCount();
8482 if(this.footer.pageSize < total){
8483 this.mainFoot.show();
8487 Roo.each(this.el.select('tbody td', true).elements, function(e){
8488 e.on('mouseover', _this.onMouseover, _this);
8491 Roo.each(this.el.select('tbody td', true).elements, function(e){
8492 e.on('mouseout', _this.onMouseout, _this);
8494 this.fireEvent('rowsrendered', this);
8500 onUpdate : function(ds,record)
8502 this.refreshRow(record);
8506 onRemove : function(ds, record, index, isUpdate){
8507 if(isUpdate !== true){
8508 this.fireEvent("beforerowremoved", this, index, record);
8510 var bt = this.mainBody.dom;
8512 var rows = this.el.select('tbody > tr', true).elements;
8514 if(typeof(rows[index]) != 'undefined'){
8515 bt.removeChild(rows[index].dom);
8518 // if(bt.rows[index]){
8519 // bt.removeChild(bt.rows[index]);
8522 if(isUpdate !== true){
8523 //this.stripeRows(index);
8524 //this.syncRowHeights(index, index);
8526 this.fireEvent("rowremoved", this, index, record);
8530 onAdd : function(ds, records, rowIndex)
8532 //Roo.log('on Add called');
8533 // - note this does not handle multiple adding very well..
8534 var bt = this.mainBody.dom;
8535 for (var i =0 ; i < records.length;i++) {
8536 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8537 //Roo.log(records[i]);
8538 //Roo.log(this.store.getAt(rowIndex+i));
8539 this.insertRow(this.store, rowIndex + i, false);
8546 refreshRow : function(record){
8547 var ds = this.store, index;
8548 if(typeof record == 'number'){
8550 record = ds.getAt(index);
8552 index = ds.indexOf(record);
8554 return; // should not happen - but seems to
8557 this.insertRow(ds, index, true);
8559 this.onRemove(ds, record, index+1, true);
8561 //this.syncRowHeights(index, index);
8563 this.fireEvent("rowupdated", this, index, record);
8566 insertRow : function(dm, rowIndex, isUpdate){
8569 this.fireEvent("beforerowsinserted", this, rowIndex);
8571 //var s = this.getScrollState();
8572 var row = this.renderRow(this.cm, this.store, rowIndex);
8573 // insert before rowIndex..
8574 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8578 if(row.cellObjects.length){
8579 Roo.each(row.cellObjects, function(r){
8580 _this.renderCellObject(r);
8585 this.fireEvent("rowsinserted", this, rowIndex);
8586 //this.syncRowHeights(firstRow, lastRow);
8587 //this.stripeRows(firstRow);
8594 getRowDom : function(rowIndex)
8596 var rows = this.el.select('tbody > tr', true).elements;
8598 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8601 // returns the object tree for a tr..
8604 renderRow : function(cm, ds, rowIndex)
8606 var d = ds.getAt(rowIndex);
8610 cls : 'x-row-' + rowIndex,
8614 var cellObjects = [];
8616 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8617 var config = cm.config[i];
8619 var renderer = cm.getRenderer(i);
8623 if(typeof(renderer) !== 'undefined'){
8624 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8626 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8627 // and are rendered into the cells after the row is rendered - using the id for the element.
8629 if(typeof(value) === 'object'){
8639 rowIndex : rowIndex,
8644 this.fireEvent('rowclass', this, rowcfg);
8648 cls : rowcfg.rowClass + ' x-col-' + i,
8650 html: (typeof(value) === 'object') ? '' : value
8657 if(typeof(config.colspan) != 'undefined'){
8658 td.colspan = config.colspan;
8661 if(typeof(config.hidden) != 'undefined' && config.hidden){
8662 td.style += ' display:none;';
8665 if(typeof(config.align) != 'undefined' && config.align.length){
8666 td.style += ' text-align:' + config.align + ';';
8668 if(typeof(config.valign) != 'undefined' && config.valign.length){
8669 td.style += ' vertical-align:' + config.valign + ';';
8672 if(typeof(config.width) != 'undefined'){
8673 td.style += ' width:' + config.width + 'px;';
8676 if(typeof(config.cursor) != 'undefined'){
8677 td.style += ' cursor:' + config.cursor + ';';
8680 if(typeof(config.cls) != 'undefined'){
8681 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8684 ['xs','sm','md','lg'].map(function(size){
8686 if(typeof(config[size]) == 'undefined'){
8692 if (!config[size]) { // 0 = hidden
8693 // BS 4 '0' is treated as hide that column and below.
8694 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8698 td.cls += ' col-' + size + '-' + config[size] + (
8699 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8709 row.cellObjects = cellObjects;
8717 onBeforeLoad : function()
8726 this.el.select('tbody', true).first().dom.innerHTML = '';
8729 * Show or hide a row.
8730 * @param {Number} rowIndex to show or hide
8731 * @param {Boolean} state hide
8733 setRowVisibility : function(rowIndex, state)
8735 var bt = this.mainBody.dom;
8737 var rows = this.el.select('tbody > tr', true).elements;
8739 if(typeof(rows[rowIndex]) == 'undefined'){
8742 rows[rowIndex].dom.style.display = state ? '' : 'none';
8746 getSelectionModel : function(){
8748 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8750 return this.selModel;
8753 * Render the Roo.bootstrap object from renderder
8755 renderCellObject : function(r)
8759 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8761 var t = r.cfg.render(r.container);
8764 Roo.each(r.cfg.cn, function(c){
8766 container: t.getChildContainer(),
8769 _this.renderCellObject(child);
8774 getRowIndex : function(row)
8778 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8789 * Returns the grid's underlying element = used by panel.Grid
8790 * @return {Element} The element
8792 getGridEl : function(){
8796 * Forces a resize - used by panel.Grid
8797 * @return {Element} The element
8799 autoSize : function()
8801 //var ctr = Roo.get(this.container.dom.parentElement);
8802 var ctr = Roo.get(this.el.dom);
8804 var thd = this.getGridEl().select('thead',true).first();
8805 var tbd = this.getGridEl().select('tbody', true).first();
8806 var tfd = this.getGridEl().select('tfoot', true).first();
8808 var cw = ctr.getWidth();
8809 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8813 tbd.setWidth(ctr.getWidth());
8814 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8815 // this needs fixing for various usage - currently only hydra job advers I think..
8817 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8819 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8822 cw = Math.max(cw, this.totalWidth);
8823 this.getGridEl().select('tbody tr',true).setWidth(cw);
8825 // resize 'expandable coloumn?
8827 return; // we doe not have a view in this design..
8830 onBodyScroll: function()
8832 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8834 this.mainHead.setStyle({
8835 'position' : 'relative',
8836 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8842 var scrollHeight = this.mainBody.dom.scrollHeight;
8844 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8846 var height = this.mainBody.getHeight();
8848 if(scrollHeight - height == scrollTop) {
8850 var total = this.ds.getTotalCount();
8852 if(this.footer.cursor + this.footer.pageSize < total){
8854 this.footer.ds.load({
8856 start : this.footer.cursor + this.footer.pageSize,
8857 limit : this.footer.pageSize
8867 onHeaderChange : function()
8869 var header = this.renderHeader();
8870 var table = this.el.select('table', true).first();
8872 this.mainHead.remove();
8873 this.mainHead = table.createChild(header, this.mainBody, false);
8876 onHiddenChange : function(colModel, colIndex, hidden)
8878 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8879 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8881 this.CSS.updateRule(thSelector, "display", "");
8882 this.CSS.updateRule(tdSelector, "display", "");
8885 this.CSS.updateRule(thSelector, "display", "none");
8886 this.CSS.updateRule(tdSelector, "display", "none");
8889 this.onHeaderChange();
8893 setColumnWidth: function(col_index, width)
8895 // width = "md-2 xs-2..."
8896 if(!this.colModel.config[col_index]) {
8900 var w = width.split(" ");
8902 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8904 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8907 for(var j = 0; j < w.length; j++) {
8913 var size_cls = w[j].split("-");
8915 if(!Number.isInteger(size_cls[1] * 1)) {
8919 if(!this.colModel.config[col_index][size_cls[0]]) {
8923 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8927 h_row[0].classList.replace(
8928 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8929 "col-"+size_cls[0]+"-"+size_cls[1]
8932 for(var i = 0; i < rows.length; i++) {
8934 var size_cls = w[j].split("-");
8936 if(!Number.isInteger(size_cls[1] * 1)) {
8940 if(!this.colModel.config[col_index][size_cls[0]]) {
8944 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8948 rows[i].classList.replace(
8949 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8950 "col-"+size_cls[0]+"-"+size_cls[1]
8954 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8969 * @class Roo.bootstrap.TableCell
8970 * @extends Roo.bootstrap.Component
8971 * Bootstrap TableCell class
8972 * @cfg {String} html cell contain text
8973 * @cfg {String} cls cell class
8974 * @cfg {String} tag cell tag (td|th) default td
8975 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8976 * @cfg {String} align Aligns the content in a cell
8977 * @cfg {String} axis Categorizes cells
8978 * @cfg {String} bgcolor Specifies the background color of a cell
8979 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8980 * @cfg {Number} colspan Specifies the number of columns a cell should span
8981 * @cfg {String} headers Specifies one or more header cells a cell is related to
8982 * @cfg {Number} height Sets the height of a cell
8983 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8984 * @cfg {Number} rowspan Sets the number of rows a cell should span
8985 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8986 * @cfg {String} valign Vertical aligns the content in a cell
8987 * @cfg {Number} width Specifies the width of a cell
8990 * Create a new TableCell
8991 * @param {Object} config The config object
8994 Roo.bootstrap.TableCell = function(config){
8995 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8998 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9018 getAutoCreate : function(){
9019 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9039 cfg.align=this.align
9045 cfg.bgcolor=this.bgcolor
9048 cfg.charoff=this.charoff
9051 cfg.colspan=this.colspan
9054 cfg.headers=this.headers
9057 cfg.height=this.height
9060 cfg.nowrap=this.nowrap
9063 cfg.rowspan=this.rowspan
9066 cfg.scope=this.scope
9069 cfg.valign=this.valign
9072 cfg.width=this.width
9091 * @class Roo.bootstrap.TableRow
9092 * @extends Roo.bootstrap.Component
9093 * Bootstrap TableRow class
9094 * @cfg {String} cls row class
9095 * @cfg {String} align Aligns the content in a table row
9096 * @cfg {String} bgcolor Specifies a background color for a table row
9097 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9098 * @cfg {String} valign Vertical aligns the content in a table row
9101 * Create a new TableRow
9102 * @param {Object} config The config object
9105 Roo.bootstrap.TableRow = function(config){
9106 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9109 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9117 getAutoCreate : function(){
9118 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9128 cfg.align = this.align;
9131 cfg.bgcolor = this.bgcolor;
9134 cfg.charoff = this.charoff;
9137 cfg.valign = this.valign;
9155 * @class Roo.bootstrap.TableBody
9156 * @extends Roo.bootstrap.Component
9157 * Bootstrap TableBody class
9158 * @cfg {String} cls element class
9159 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9160 * @cfg {String} align Aligns the content inside the element
9161 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9162 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9165 * Create a new TableBody
9166 * @param {Object} config The config object
9169 Roo.bootstrap.TableBody = function(config){
9170 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9173 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9181 getAutoCreate : function(){
9182 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9196 cfg.align = this.align;
9199 cfg.charoff = this.charoff;
9202 cfg.valign = this.valign;
9209 // initEvents : function()
9216 // this.store = Roo.factory(this.store, Roo.data);
9217 // this.store.on('load', this.onLoad, this);
9219 // this.store.load();
9223 // onLoad: function ()
9225 // this.fireEvent('load', this);
9235 * Ext JS Library 1.1.1
9236 * Copyright(c) 2006-2007, Ext JS, LLC.
9238 * Originally Released Under LGPL - original licence link has changed is not relivant.
9241 * <script type="text/javascript">
9244 // as we use this in bootstrap.
9245 Roo.namespace('Roo.form');
9247 * @class Roo.form.Action
9248 * Internal Class used to handle form actions
9250 * @param {Roo.form.BasicForm} el The form element or its id
9251 * @param {Object} config Configuration options
9256 // define the action interface
9257 Roo.form.Action = function(form, options){
9259 this.options = options || {};
9262 * Client Validation Failed
9265 Roo.form.Action.CLIENT_INVALID = 'client';
9267 * Server Validation Failed
9270 Roo.form.Action.SERVER_INVALID = 'server';
9272 * Connect to Server Failed
9275 Roo.form.Action.CONNECT_FAILURE = 'connect';
9277 * Reading Data from Server Failed
9280 Roo.form.Action.LOAD_FAILURE = 'load';
9282 Roo.form.Action.prototype = {
9284 failureType : undefined,
9285 response : undefined,
9289 run : function(options){
9294 success : function(response){
9299 handleResponse : function(response){
9303 // default connection failure
9304 failure : function(response){
9306 this.response = response;
9307 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9308 this.form.afterAction(this, false);
9311 processResponse : function(response){
9312 this.response = response;
9313 if(!response.responseText){
9316 this.result = this.handleResponse(response);
9320 // utility functions used internally
9321 getUrl : function(appendParams){
9322 var url = this.options.url || this.form.url || this.form.el.dom.action;
9324 var p = this.getParams();
9326 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9332 getMethod : function(){
9333 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9336 getParams : function(){
9337 var bp = this.form.baseParams;
9338 var p = this.options.params;
9340 if(typeof p == "object"){
9341 p = Roo.urlEncode(Roo.applyIf(p, bp));
9342 }else if(typeof p == 'string' && bp){
9343 p += '&' + Roo.urlEncode(bp);
9346 p = Roo.urlEncode(bp);
9351 createCallback : function(){
9353 success: this.success,
9354 failure: this.failure,
9356 timeout: (this.form.timeout*1000),
9357 upload: this.form.fileUpload ? this.success : undefined
9362 Roo.form.Action.Submit = function(form, options){
9363 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9366 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9369 haveProgress : false,
9370 uploadComplete : false,
9372 // uploadProgress indicator.
9373 uploadProgress : function()
9375 if (!this.form.progressUrl) {
9379 if (!this.haveProgress) {
9380 Roo.MessageBox.progress("Uploading", "Uploading");
9382 if (this.uploadComplete) {
9383 Roo.MessageBox.hide();
9387 this.haveProgress = true;
9389 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9391 var c = new Roo.data.Connection();
9393 url : this.form.progressUrl,
9398 success : function(req){
9399 //console.log(data);
9403 rdata = Roo.decode(req.responseText)
9405 Roo.log("Invalid data from server..");
9409 if (!rdata || !rdata.success) {
9411 Roo.MessageBox.alert(Roo.encode(rdata));
9414 var data = rdata.data;
9416 if (this.uploadComplete) {
9417 Roo.MessageBox.hide();
9422 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9423 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9426 this.uploadProgress.defer(2000,this);
9429 failure: function(data) {
9430 Roo.log('progress url failed ');
9441 // run get Values on the form, so it syncs any secondary forms.
9442 this.form.getValues();
9444 var o = this.options;
9445 var method = this.getMethod();
9446 var isPost = method == 'POST';
9447 if(o.clientValidation === false || this.form.isValid()){
9449 if (this.form.progressUrl) {
9450 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9451 (new Date() * 1) + '' + Math.random());
9456 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9457 form:this.form.el.dom,
9458 url:this.getUrl(!isPost),
9460 params:isPost ? this.getParams() : null,
9461 isUpload: this.form.fileUpload,
9462 formData : this.form.formData
9465 this.uploadProgress();
9467 }else if (o.clientValidation !== false){ // client validation failed
9468 this.failureType = Roo.form.Action.CLIENT_INVALID;
9469 this.form.afterAction(this, false);
9473 success : function(response)
9475 this.uploadComplete= true;
9476 if (this.haveProgress) {
9477 Roo.MessageBox.hide();
9481 var result = this.processResponse(response);
9482 if(result === true || result.success){
9483 this.form.afterAction(this, true);
9487 this.form.markInvalid(result.errors);
9488 this.failureType = Roo.form.Action.SERVER_INVALID;
9490 this.form.afterAction(this, false);
9492 failure : function(response)
9494 this.uploadComplete= true;
9495 if (this.haveProgress) {
9496 Roo.MessageBox.hide();
9499 this.response = response;
9500 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9501 this.form.afterAction(this, false);
9504 handleResponse : function(response){
9505 if(this.form.errorReader){
9506 var rs = this.form.errorReader.read(response);
9509 for(var i = 0, len = rs.records.length; i < len; i++) {
9510 var r = rs.records[i];
9514 if(errors.length < 1){
9518 success : rs.success,
9524 ret = Roo.decode(response.responseText);
9528 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9538 Roo.form.Action.Load = function(form, options){
9539 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9540 this.reader = this.form.reader;
9543 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9548 Roo.Ajax.request(Roo.apply(
9549 this.createCallback(), {
9550 method:this.getMethod(),
9551 url:this.getUrl(false),
9552 params:this.getParams()
9556 success : function(response){
9558 var result = this.processResponse(response);
9559 if(result === true || !result.success || !result.data){
9560 this.failureType = Roo.form.Action.LOAD_FAILURE;
9561 this.form.afterAction(this, false);
9564 this.form.clearInvalid();
9565 this.form.setValues(result.data);
9566 this.form.afterAction(this, true);
9569 handleResponse : function(response){
9570 if(this.form.reader){
9571 var rs = this.form.reader.read(response);
9572 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9574 success : rs.success,
9578 return Roo.decode(response.responseText);
9582 Roo.form.Action.ACTION_TYPES = {
9583 'load' : Roo.form.Action.Load,
9584 'submit' : Roo.form.Action.Submit
9593 * @class Roo.bootstrap.Form
9594 * @extends Roo.bootstrap.Component
9595 * Bootstrap Form class
9596 * @cfg {String} method GET | POST (default POST)
9597 * @cfg {String} labelAlign top | left (default top)
9598 * @cfg {String} align left | right - for navbars
9599 * @cfg {Boolean} loadMask load mask when submit (default true)
9604 * @param {Object} config The config object
9608 Roo.bootstrap.Form = function(config){
9610 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9612 Roo.bootstrap.Form.popover.apply();
9616 * @event clientvalidation
9617 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9618 * @param {Form} this
9619 * @param {Boolean} valid true if the form has passed client-side validation
9621 clientvalidation: true,
9623 * @event beforeaction
9624 * Fires before any action is performed. Return false to cancel the action.
9625 * @param {Form} this
9626 * @param {Action} action The action to be performed
9630 * @event actionfailed
9631 * Fires when an action fails.
9632 * @param {Form} this
9633 * @param {Action} action The action that failed
9635 actionfailed : true,
9637 * @event actioncomplete
9638 * Fires when an action is completed.
9639 * @param {Form} this
9640 * @param {Action} action The action that completed
9642 actioncomplete : true
9646 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9649 * @cfg {String} method
9650 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9655 * The URL to use for form actions if one isn't supplied in the action options.
9658 * @cfg {Boolean} fileUpload
9659 * Set to true if this form is a file upload.
9663 * @cfg {Object} baseParams
9664 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9668 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9672 * @cfg {Sting} align (left|right) for navbar forms
9677 activeAction : null,
9680 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9681 * element by passing it or its id or mask the form itself by passing in true.
9684 waitMsgTarget : false,
9689 * @cfg {Boolean} errorMask (true|false) default false
9694 * @cfg {Number} maskOffset Default 100
9699 * @cfg {Boolean} maskBody
9703 getAutoCreate : function(){
9707 method : this.method || 'POST',
9708 id : this.id || Roo.id(),
9711 if (this.parent().xtype.match(/^Nav/)) {
9712 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9716 if (this.labelAlign == 'left' ) {
9717 cfg.cls += ' form-horizontal';
9723 initEvents : function()
9725 this.el.on('submit', this.onSubmit, this);
9726 // this was added as random key presses on the form where triggering form submit.
9727 this.el.on('keypress', function(e) {
9728 if (e.getCharCode() != 13) {
9731 // we might need to allow it for textareas.. and some other items.
9732 // check e.getTarget().
9734 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9738 Roo.log("keypress blocked");
9746 onSubmit : function(e){
9751 * Returns true if client-side validation on the form is successful.
9754 isValid : function(){
9755 var items = this.getItems();
9759 items.each(function(f){
9765 Roo.log('invalid field: ' + f.name);
9769 if(!target && f.el.isVisible(true)){
9775 if(this.errorMask && !valid){
9776 Roo.bootstrap.Form.popover.mask(this, target);
9783 * Returns true if any fields in this form have changed since their original load.
9786 isDirty : function(){
9788 var items = this.getItems();
9789 items.each(function(f){
9799 * Performs a predefined action (submit or load) or custom actions you define on this form.
9800 * @param {String} actionName The name of the action type
9801 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9802 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9803 * accept other config options):
9805 Property Type Description
9806 ---------------- --------------- ----------------------------------------------------------------------------------
9807 url String The url for the action (defaults to the form's url)
9808 method String The form method to use (defaults to the form's method, or POST if not defined)
9809 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9810 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9811 validate the form on the client (defaults to false)
9813 * @return {BasicForm} this
9815 doAction : function(action, options){
9816 if(typeof action == 'string'){
9817 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9819 if(this.fireEvent('beforeaction', this, action) !== false){
9820 this.beforeAction(action);
9821 action.run.defer(100, action);
9827 beforeAction : function(action){
9828 var o = action.options;
9833 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9835 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9838 // not really supported yet.. ??
9840 //if(this.waitMsgTarget === true){
9841 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9842 //}else if(this.waitMsgTarget){
9843 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9844 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9846 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9852 afterAction : function(action, success){
9853 this.activeAction = null;
9854 var o = action.options;
9859 Roo.get(document.body).unmask();
9865 //if(this.waitMsgTarget === true){
9866 // this.el.unmask();
9867 //}else if(this.waitMsgTarget){
9868 // this.waitMsgTarget.unmask();
9870 // Roo.MessageBox.updateProgress(1);
9871 // Roo.MessageBox.hide();
9878 Roo.callback(o.success, o.scope, [this, action]);
9879 this.fireEvent('actioncomplete', this, action);
9883 // failure condition..
9884 // we have a scenario where updates need confirming.
9885 // eg. if a locking scenario exists..
9886 // we look for { errors : { needs_confirm : true }} in the response.
9888 (typeof(action.result) != 'undefined') &&
9889 (typeof(action.result.errors) != 'undefined') &&
9890 (typeof(action.result.errors.needs_confirm) != 'undefined')
9893 Roo.log("not supported yet");
9896 Roo.MessageBox.confirm(
9897 "Change requires confirmation",
9898 action.result.errorMsg,
9903 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9913 Roo.callback(o.failure, o.scope, [this, action]);
9914 // show an error message if no failed handler is set..
9915 if (!this.hasListener('actionfailed')) {
9916 Roo.log("need to add dialog support");
9918 Roo.MessageBox.alert("Error",
9919 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9920 action.result.errorMsg :
9921 "Saving Failed, please check your entries or try again"
9926 this.fireEvent('actionfailed', this, action);
9931 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9932 * @param {String} id The value to search for
9935 findField : function(id){
9936 var items = this.getItems();
9937 var field = items.get(id);
9939 items.each(function(f){
9940 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9947 return field || null;
9950 * Mark fields in this form invalid in bulk.
9951 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9952 * @return {BasicForm} this
9954 markInvalid : function(errors){
9955 if(errors instanceof Array){
9956 for(var i = 0, len = errors.length; i < len; i++){
9957 var fieldError = errors[i];
9958 var f = this.findField(fieldError.id);
9960 f.markInvalid(fieldError.msg);
9966 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9967 field.markInvalid(errors[id]);
9971 //Roo.each(this.childForms || [], function (f) {
9972 // f.markInvalid(errors);
9979 * Set values for fields in this form in bulk.
9980 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9981 * @return {BasicForm} this
9983 setValues : function(values){
9984 if(values instanceof Array){ // array of objects
9985 for(var i = 0, len = values.length; i < len; i++){
9987 var f = this.findField(v.id);
9989 f.setValue(v.value);
9990 if(this.trackResetOnLoad){
9991 f.originalValue = f.getValue();
9995 }else{ // object hash
9998 if(typeof values[id] != 'function' && (field = this.findField(id))){
10000 if (field.setFromData &&
10001 field.valueField &&
10002 field.displayField &&
10003 // combos' with local stores can
10004 // be queried via setValue()
10005 // to set their value..
10006 (field.store && !field.store.isLocal)
10010 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10011 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10012 field.setFromData(sd);
10014 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10016 field.setFromData(values);
10019 field.setValue(values[id]);
10023 if(this.trackResetOnLoad){
10024 field.originalValue = field.getValue();
10030 //Roo.each(this.childForms || [], function (f) {
10031 // f.setValues(values);
10038 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10039 * they are returned as an array.
10040 * @param {Boolean} asString
10043 getValues : function(asString){
10044 //if (this.childForms) {
10045 // copy values from the child forms
10046 // Roo.each(this.childForms, function (f) {
10047 // this.setValues(f.getValues());
10053 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10054 if(asString === true){
10057 return Roo.urlDecode(fs);
10061 * Returns the fields in this form as an object with key/value pairs.
10062 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10065 getFieldValues : function(with_hidden)
10067 var items = this.getItems();
10069 items.each(function(f){
10071 if (!f.getName()) {
10075 var v = f.getValue();
10077 if (f.inputType =='radio') {
10078 if (typeof(ret[f.getName()]) == 'undefined') {
10079 ret[f.getName()] = ''; // empty..
10082 if (!f.el.dom.checked) {
10086 v = f.el.dom.value;
10090 if(f.xtype == 'MoneyField'){
10091 ret[f.currencyName] = f.getCurrency();
10094 // not sure if this supported any more..
10095 if ((typeof(v) == 'object') && f.getRawValue) {
10096 v = f.getRawValue() ; // dates..
10098 // combo boxes where name != hiddenName...
10099 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10100 ret[f.name] = f.getRawValue();
10102 ret[f.getName()] = v;
10109 * Clears all invalid messages in this form.
10110 * @return {BasicForm} this
10112 clearInvalid : function(){
10113 var items = this.getItems();
10115 items.each(function(f){
10123 * Resets this form.
10124 * @return {BasicForm} this
10126 reset : function(){
10127 var items = this.getItems();
10128 items.each(function(f){
10132 Roo.each(this.childForms || [], function (f) {
10140 getItems : function()
10142 var r=new Roo.util.MixedCollection(false, function(o){
10143 return o.id || (o.id = Roo.id());
10145 var iter = function(el) {
10152 Roo.each(el.items,function(e) {
10161 hideFields : function(items)
10163 Roo.each(items, function(i){
10165 var f = this.findField(i);
10176 showFields : function(items)
10178 Roo.each(items, function(i){
10180 var f = this.findField(i);
10193 Roo.apply(Roo.bootstrap.Form, {
10209 intervalID : false,
10215 if(this.isApplied){
10220 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10221 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10222 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10223 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10226 this.maskEl.top.enableDisplayMode("block");
10227 this.maskEl.left.enableDisplayMode("block");
10228 this.maskEl.bottom.enableDisplayMode("block");
10229 this.maskEl.right.enableDisplayMode("block");
10231 this.toolTip = new Roo.bootstrap.Tooltip({
10232 cls : 'roo-form-error-popover',
10234 'left' : ['r-l', [-2,0], 'right'],
10235 'right' : ['l-r', [2,0], 'left'],
10236 'bottom' : ['tl-bl', [0,2], 'top'],
10237 'top' : [ 'bl-tl', [0,-2], 'bottom']
10241 this.toolTip.render(Roo.get(document.body));
10243 this.toolTip.el.enableDisplayMode("block");
10245 Roo.get(document.body).on('click', function(){
10249 Roo.get(document.body).on('touchstart', function(){
10253 this.isApplied = true
10256 mask : function(form, target)
10260 this.target = target;
10262 if(!this.form.errorMask || !target.el){
10266 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10268 Roo.log(scrollable);
10270 var ot = this.target.el.calcOffsetsTo(scrollable);
10272 var scrollTo = ot[1] - this.form.maskOffset;
10274 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10276 scrollable.scrollTo('top', scrollTo);
10278 var box = this.target.el.getBox();
10280 var zIndex = Roo.bootstrap.Modal.zIndex++;
10283 this.maskEl.top.setStyle('position', 'absolute');
10284 this.maskEl.top.setStyle('z-index', zIndex);
10285 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10286 this.maskEl.top.setLeft(0);
10287 this.maskEl.top.setTop(0);
10288 this.maskEl.top.show();
10290 this.maskEl.left.setStyle('position', 'absolute');
10291 this.maskEl.left.setStyle('z-index', zIndex);
10292 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10293 this.maskEl.left.setLeft(0);
10294 this.maskEl.left.setTop(box.y - this.padding);
10295 this.maskEl.left.show();
10297 this.maskEl.bottom.setStyle('position', 'absolute');
10298 this.maskEl.bottom.setStyle('z-index', zIndex);
10299 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10300 this.maskEl.bottom.setLeft(0);
10301 this.maskEl.bottom.setTop(box.bottom + this.padding);
10302 this.maskEl.bottom.show();
10304 this.maskEl.right.setStyle('position', 'absolute');
10305 this.maskEl.right.setStyle('z-index', zIndex);
10306 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10307 this.maskEl.right.setLeft(box.right + this.padding);
10308 this.maskEl.right.setTop(box.y - this.padding);
10309 this.maskEl.right.show();
10311 this.toolTip.bindEl = this.target.el;
10313 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10315 var tip = this.target.blankText;
10317 if(this.target.getValue() !== '' ) {
10319 if (this.target.invalidText.length) {
10320 tip = this.target.invalidText;
10321 } else if (this.target.regexText.length){
10322 tip = this.target.regexText;
10326 this.toolTip.show(tip);
10328 this.intervalID = window.setInterval(function() {
10329 Roo.bootstrap.Form.popover.unmask();
10332 window.onwheel = function(){ return false;};
10334 (function(){ this.isMasked = true; }).defer(500, this);
10338 unmask : function()
10340 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10344 this.maskEl.top.setStyle('position', 'absolute');
10345 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10346 this.maskEl.top.hide();
10348 this.maskEl.left.setStyle('position', 'absolute');
10349 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10350 this.maskEl.left.hide();
10352 this.maskEl.bottom.setStyle('position', 'absolute');
10353 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10354 this.maskEl.bottom.hide();
10356 this.maskEl.right.setStyle('position', 'absolute');
10357 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10358 this.maskEl.right.hide();
10360 this.toolTip.hide();
10362 this.toolTip.el.hide();
10364 window.onwheel = function(){ return true;};
10366 if(this.intervalID){
10367 window.clearInterval(this.intervalID);
10368 this.intervalID = false;
10371 this.isMasked = false;
10381 * Ext JS Library 1.1.1
10382 * Copyright(c) 2006-2007, Ext JS, LLC.
10384 * Originally Released Under LGPL - original licence link has changed is not relivant.
10387 * <script type="text/javascript">
10390 * @class Roo.form.VTypes
10391 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10394 Roo.form.VTypes = function(){
10395 // closure these in so they are only created once.
10396 var alpha = /^[a-zA-Z_]+$/;
10397 var alphanum = /^[a-zA-Z0-9_]+$/;
10398 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10399 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10401 // All these messages and functions are configurable
10404 * The function used to validate email addresses
10405 * @param {String} value The email address
10407 'email' : function(v){
10408 return email.test(v);
10411 * The error text to display when the email validation function returns false
10414 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10416 * The keystroke filter mask to be applied on email input
10419 'emailMask' : /[a-z0-9_\.\-@]/i,
10422 * The function used to validate URLs
10423 * @param {String} value The URL
10425 'url' : function(v){
10426 return url.test(v);
10429 * The error text to display when the url validation function returns false
10432 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10435 * The function used to validate alpha values
10436 * @param {String} value The value
10438 'alpha' : function(v){
10439 return alpha.test(v);
10442 * The error text to display when the alpha validation function returns false
10445 'alphaText' : 'This field should only contain letters and _',
10447 * The keystroke filter mask to be applied on alpha input
10450 'alphaMask' : /[a-z_]/i,
10453 * The function used to validate alphanumeric values
10454 * @param {String} value The value
10456 'alphanum' : function(v){
10457 return alphanum.test(v);
10460 * The error text to display when the alphanumeric validation function returns false
10463 'alphanumText' : 'This field should only contain letters, numbers and _',
10465 * The keystroke filter mask to be applied on alphanumeric input
10468 'alphanumMask' : /[a-z0-9_]/i
10478 * @class Roo.bootstrap.Input
10479 * @extends Roo.bootstrap.Component
10480 * Bootstrap Input class
10481 * @cfg {Boolean} disabled is it disabled
10482 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10483 * @cfg {String} name name of the input
10484 * @cfg {string} fieldLabel - the label associated
10485 * @cfg {string} placeholder - placeholder to put in text.
10486 * @cfg {string} before - input group add on before
10487 * @cfg {string} after - input group add on after
10488 * @cfg {string} size - (lg|sm) or leave empty..
10489 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10490 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10491 * @cfg {Number} md colspan out of 12 for computer-sized screens
10492 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10493 * @cfg {string} value default value of the input
10494 * @cfg {Number} labelWidth set the width of label
10495 * @cfg {Number} labellg set the width of label (1-12)
10496 * @cfg {Number} labelmd set the width of label (1-12)
10497 * @cfg {Number} labelsm set the width of label (1-12)
10498 * @cfg {Number} labelxs set the width of label (1-12)
10499 * @cfg {String} labelAlign (top|left)
10500 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10501 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10502 * @cfg {String} indicatorpos (left|right) default left
10503 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10504 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10505 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10507 * @cfg {String} align (left|center|right) Default left
10508 * @cfg {Boolean} forceFeedback (true|false) Default false
10511 * Create a new Input
10512 * @param {Object} config The config object
10515 Roo.bootstrap.Input = function(config){
10517 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10522 * Fires when this field receives input focus.
10523 * @param {Roo.form.Field} this
10528 * Fires when this field loses input focus.
10529 * @param {Roo.form.Field} this
10533 * @event specialkey
10534 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10535 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10536 * @param {Roo.form.Field} this
10537 * @param {Roo.EventObject} e The event object
10542 * Fires just before the field blurs if the field value has changed.
10543 * @param {Roo.form.Field} this
10544 * @param {Mixed} newValue The new value
10545 * @param {Mixed} oldValue The original value
10550 * Fires after the field has been marked as invalid.
10551 * @param {Roo.form.Field} this
10552 * @param {String} msg The validation message
10557 * Fires after the field has been validated with no errors.
10558 * @param {Roo.form.Field} this
10563 * Fires after the key up
10564 * @param {Roo.form.Field} this
10565 * @param {Roo.EventObject} e The event Object
10570 * Fires after the user pastes into input
10571 * @param {Roo.form.Field} this
10572 * @param {Roo.EventObject} e The event Object
10578 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10580 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10581 automatic validation (defaults to "keyup").
10583 validationEvent : "keyup",
10585 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10587 validateOnBlur : true,
10589 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10591 validationDelay : 250,
10593 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10595 focusClass : "x-form-focus", // not needed???
10599 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10601 invalidClass : "has-warning",
10604 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10606 validClass : "has-success",
10609 * @cfg {Boolean} hasFeedback (true|false) default true
10611 hasFeedback : true,
10614 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10616 invalidFeedbackClass : "glyphicon-warning-sign",
10619 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10621 validFeedbackClass : "glyphicon-ok",
10624 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10626 selectOnFocus : false,
10629 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10633 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10638 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10640 disableKeyFilter : false,
10643 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10647 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10651 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10653 blankText : "Please complete this mandatory field",
10656 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10660 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10662 maxLength : Number.MAX_VALUE,
10664 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10666 minLengthText : "The minimum length for this field is {0}",
10668 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10670 maxLengthText : "The maximum length for this field is {0}",
10674 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10675 * If available, this function will be called only after the basic validators all return true, and will be passed the
10676 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10680 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10681 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10682 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10686 * @cfg {String} regexText -- Depricated - use Invalid Text
10691 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10697 autocomplete: false,
10701 inputType : 'text',
10704 placeholder: false,
10709 preventMark: false,
10710 isFormField : true,
10713 labelAlign : false,
10716 formatedValue : false,
10717 forceFeedback : false,
10719 indicatorpos : 'left',
10729 parentLabelAlign : function()
10732 while (parent.parent()) {
10733 parent = parent.parent();
10734 if (typeof(parent.labelAlign) !='undefined') {
10735 return parent.labelAlign;
10742 getAutoCreate : function()
10744 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10750 if(this.inputType != 'hidden'){
10751 cfg.cls = 'form-group' //input-group
10757 type : this.inputType,
10758 value : this.value,
10759 cls : 'form-control',
10760 placeholder : this.placeholder || '',
10761 autocomplete : this.autocomplete || 'new-password'
10763 if (this.inputType == 'file') {
10764 input.style = 'overflow:hidden'; // why not in CSS?
10767 if(this.capture.length){
10768 input.capture = this.capture;
10771 if(this.accept.length){
10772 input.accept = this.accept + "/*";
10776 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10779 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10780 input.maxLength = this.maxLength;
10783 if (this.disabled) {
10784 input.disabled=true;
10787 if (this.readOnly) {
10788 input.readonly=true;
10792 input.name = this.name;
10796 input.cls += ' input-' + this.size;
10800 ['xs','sm','md','lg'].map(function(size){
10801 if (settings[size]) {
10802 cfg.cls += ' col-' + size + '-' + settings[size];
10806 var inputblock = input;
10810 cls: 'glyphicon form-control-feedback'
10813 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10816 cls : 'has-feedback',
10824 if (this.before || this.after) {
10827 cls : 'input-group',
10831 if (this.before && typeof(this.before) == 'string') {
10833 inputblock.cn.push({
10835 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10839 if (this.before && typeof(this.before) == 'object') {
10840 this.before = Roo.factory(this.before);
10842 inputblock.cn.push({
10844 cls : 'roo-input-before input-group-prepend input-group-' +
10845 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10849 inputblock.cn.push(input);
10851 if (this.after && typeof(this.after) == 'string') {
10852 inputblock.cn.push({
10854 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10858 if (this.after && typeof(this.after) == 'object') {
10859 this.after = Roo.factory(this.after);
10861 inputblock.cn.push({
10863 cls : 'roo-input-after input-group-append input-group-' +
10864 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10868 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10869 inputblock.cls += ' has-feedback';
10870 inputblock.cn.push(feedback);
10875 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10876 tooltip : 'This field is required'
10878 if (this.allowBlank ) {
10879 indicator.style = this.allowBlank ? ' display:none' : '';
10881 if (align ==='left' && this.fieldLabel.length) {
10883 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10890 cls : 'control-label col-form-label',
10891 html : this.fieldLabel
10902 var labelCfg = cfg.cn[1];
10903 var contentCfg = cfg.cn[2];
10905 if(this.indicatorpos == 'right'){
10910 cls : 'control-label col-form-label',
10914 html : this.fieldLabel
10928 labelCfg = cfg.cn[0];
10929 contentCfg = cfg.cn[1];
10933 if(this.labelWidth > 12){
10934 labelCfg.style = "width: " + this.labelWidth + 'px';
10937 if(this.labelWidth < 13 && this.labelmd == 0){
10938 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
10941 if(this.labellg > 0){
10942 labelCfg.cls += ' col-lg-' + this.labellg;
10943 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10946 if(this.labelmd > 0){
10947 labelCfg.cls += ' col-md-' + this.labelmd;
10948 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10951 if(this.labelsm > 0){
10952 labelCfg.cls += ' col-sm-' + this.labelsm;
10953 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10956 if(this.labelxs > 0){
10957 labelCfg.cls += ' col-xs-' + this.labelxs;
10958 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10962 } else if ( this.fieldLabel.length) {
10969 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10970 tooltip : 'This field is required',
10971 style : this.allowBlank ? ' display:none' : ''
10975 //cls : 'input-group-addon',
10976 html : this.fieldLabel
10984 if(this.indicatorpos == 'right'){
10989 //cls : 'input-group-addon',
10990 html : this.fieldLabel
10995 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10996 tooltip : 'This field is required',
10997 style : this.allowBlank ? ' display:none' : ''
11017 if (this.parentType === 'Navbar' && this.parent().bar) {
11018 cfg.cls += ' navbar-form';
11021 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11022 // on BS4 we do this only if not form
11023 cfg.cls += ' navbar-form';
11031 * return the real input element.
11033 inputEl: function ()
11035 return this.el.select('input.form-control',true).first();
11038 tooltipEl : function()
11040 return this.inputEl();
11043 indicatorEl : function()
11045 if (Roo.bootstrap.version == 4) {
11046 return false; // not enabled in v4 yet.
11049 var indicator = this.el.select('i.roo-required-indicator',true).first();
11059 setDisabled : function(v)
11061 var i = this.inputEl().dom;
11063 i.removeAttribute('disabled');
11067 i.setAttribute('disabled','true');
11069 initEvents : function()
11072 this.inputEl().on("keydown" , this.fireKey, this);
11073 this.inputEl().on("focus", this.onFocus, this);
11074 this.inputEl().on("blur", this.onBlur, this);
11076 this.inputEl().relayEvent('keyup', this);
11077 this.inputEl().relayEvent('paste', this);
11079 this.indicator = this.indicatorEl();
11081 if(this.indicator){
11082 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11085 // reference to original value for reset
11086 this.originalValue = this.getValue();
11087 //Roo.form.TextField.superclass.initEvents.call(this);
11088 if(this.validationEvent == 'keyup'){
11089 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11090 this.inputEl().on('keyup', this.filterValidation, this);
11092 else if(this.validationEvent !== false){
11093 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11096 if(this.selectOnFocus){
11097 this.on("focus", this.preFocus, this);
11100 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11101 this.inputEl().on("keypress", this.filterKeys, this);
11103 this.inputEl().relayEvent('keypress', this);
11106 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11107 this.el.on("click", this.autoSize, this);
11110 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11111 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11114 if (typeof(this.before) == 'object') {
11115 this.before.render(this.el.select('.roo-input-before',true).first());
11117 if (typeof(this.after) == 'object') {
11118 this.after.render(this.el.select('.roo-input-after',true).first());
11121 this.inputEl().on('change', this.onChange, this);
11124 filterValidation : function(e){
11125 if(!e.isNavKeyPress()){
11126 this.validationTask.delay(this.validationDelay);
11130 * Validates the field value
11131 * @return {Boolean} True if the value is valid, else false
11133 validate : function(){
11134 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11135 if(this.disabled || this.validateValue(this.getRawValue())){
11140 this.markInvalid();
11146 * Validates a value according to the field's validation rules and marks the field as invalid
11147 * if the validation fails
11148 * @param {Mixed} value The value to validate
11149 * @return {Boolean} True if the value is valid, else false
11151 validateValue : function(value)
11153 if(this.getVisibilityEl().hasClass('hidden')){
11157 if(value.length < 1) { // if it's blank
11158 if(this.allowBlank){
11164 if(value.length < this.minLength){
11167 if(value.length > this.maxLength){
11171 var vt = Roo.form.VTypes;
11172 if(!vt[this.vtype](value, this)){
11176 if(typeof this.validator == "function"){
11177 var msg = this.validator(value);
11181 if (typeof(msg) == 'string') {
11182 this.invalidText = msg;
11186 if(this.regex && !this.regex.test(value)){
11194 fireKey : function(e){
11195 //Roo.log('field ' + e.getKey());
11196 if(e.isNavKeyPress()){
11197 this.fireEvent("specialkey", this, e);
11200 focus : function (selectText){
11202 this.inputEl().focus();
11203 if(selectText === true){
11204 this.inputEl().dom.select();
11210 onFocus : function(){
11211 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11212 // this.el.addClass(this.focusClass);
11214 if(!this.hasFocus){
11215 this.hasFocus = true;
11216 this.startValue = this.getValue();
11217 this.fireEvent("focus", this);
11221 beforeBlur : Roo.emptyFn,
11225 onBlur : function(){
11227 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11228 //this.el.removeClass(this.focusClass);
11230 this.hasFocus = false;
11231 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11234 var v = this.getValue();
11235 if(String(v) !== String(this.startValue)){
11236 this.fireEvent('change', this, v, this.startValue);
11238 this.fireEvent("blur", this);
11241 onChange : function(e)
11243 var v = this.getValue();
11244 if(String(v) !== String(this.startValue)){
11245 this.fireEvent('change', this, v, this.startValue);
11251 * Resets the current field value to the originally loaded value and clears any validation messages
11253 reset : function(){
11254 this.setValue(this.originalValue);
11258 * Returns the name of the field
11259 * @return {Mixed} name The name field
11261 getName: function(){
11265 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11266 * @return {Mixed} value The field value
11268 getValue : function(){
11270 var v = this.inputEl().getValue();
11275 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11276 * @return {Mixed} value The field value
11278 getRawValue : function(){
11279 var v = this.inputEl().getValue();
11285 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11286 * @param {Mixed} value The value to set
11288 setRawValue : function(v){
11289 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11292 selectText : function(start, end){
11293 var v = this.getRawValue();
11295 start = start === undefined ? 0 : start;
11296 end = end === undefined ? v.length : end;
11297 var d = this.inputEl().dom;
11298 if(d.setSelectionRange){
11299 d.setSelectionRange(start, end);
11300 }else if(d.createTextRange){
11301 var range = d.createTextRange();
11302 range.moveStart("character", start);
11303 range.moveEnd("character", v.length-end);
11310 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11311 * @param {Mixed} value The value to set
11313 setValue : function(v){
11316 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11322 processValue : function(value){
11323 if(this.stripCharsRe){
11324 var newValue = value.replace(this.stripCharsRe, '');
11325 if(newValue !== value){
11326 this.setRawValue(newValue);
11333 preFocus : function(){
11335 if(this.selectOnFocus){
11336 this.inputEl().dom.select();
11339 filterKeys : function(e){
11340 var k = e.getKey();
11341 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11344 var c = e.getCharCode(), cc = String.fromCharCode(c);
11345 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11348 if(!this.maskRe.test(cc)){
11353 * Clear any invalid styles/messages for this field
11355 clearInvalid : function(){
11357 if(!this.el || this.preventMark){ // not rendered
11362 this.el.removeClass([this.invalidClass, 'is-invalid']);
11364 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11366 var feedback = this.el.select('.form-control-feedback', true).first();
11369 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11374 if(this.indicator){
11375 this.indicator.removeClass('visible');
11376 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11379 this.fireEvent('valid', this);
11383 * Mark this field as valid
11385 markValid : function()
11387 if(!this.el || this.preventMark){ // not rendered...
11391 this.el.removeClass([this.invalidClass, this.validClass]);
11392 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11394 var feedback = this.el.select('.form-control-feedback', true).first();
11397 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11400 if(this.indicator){
11401 this.indicator.removeClass('visible');
11402 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11410 if(this.allowBlank && !this.getRawValue().length){
11413 if (Roo.bootstrap.version == 3) {
11414 this.el.addClass(this.validClass);
11416 this.inputEl().addClass('is-valid');
11419 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11421 var feedback = this.el.select('.form-control-feedback', true).first();
11424 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11425 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11430 this.fireEvent('valid', this);
11434 * Mark this field as invalid
11435 * @param {String} msg The validation message
11437 markInvalid : function(msg)
11439 if(!this.el || this.preventMark){ // not rendered
11443 this.el.removeClass([this.invalidClass, this.validClass]);
11444 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11446 var feedback = this.el.select('.form-control-feedback', true).first();
11449 this.el.select('.form-control-feedback', true).first().removeClass(
11450 [this.invalidFeedbackClass, this.validFeedbackClass]);
11457 if(this.allowBlank && !this.getRawValue().length){
11461 if(this.indicator){
11462 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11463 this.indicator.addClass('visible');
11465 if (Roo.bootstrap.version == 3) {
11466 this.el.addClass(this.invalidClass);
11468 this.inputEl().addClass('is-invalid');
11473 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11475 var feedback = this.el.select('.form-control-feedback', true).first();
11478 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11480 if(this.getValue().length || this.forceFeedback){
11481 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11488 this.fireEvent('invalid', this, msg);
11491 SafariOnKeyDown : function(event)
11493 // this is a workaround for a password hang bug on chrome/ webkit.
11494 if (this.inputEl().dom.type != 'password') {
11498 var isSelectAll = false;
11500 if(this.inputEl().dom.selectionEnd > 0){
11501 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11503 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11504 event.preventDefault();
11509 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11511 event.preventDefault();
11512 // this is very hacky as keydown always get's upper case.
11514 var cc = String.fromCharCode(event.getCharCode());
11515 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11519 adjustWidth : function(tag, w){
11520 tag = tag.toLowerCase();
11521 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11522 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11523 if(tag == 'input'){
11526 if(tag == 'textarea'){
11529 }else if(Roo.isOpera){
11530 if(tag == 'input'){
11533 if(tag == 'textarea'){
11541 setFieldLabel : function(v)
11543 if(!this.rendered){
11547 if(this.indicatorEl()){
11548 var ar = this.el.select('label > span',true);
11550 if (ar.elements.length) {
11551 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11552 this.fieldLabel = v;
11556 var br = this.el.select('label',true);
11558 if(br.elements.length) {
11559 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11560 this.fieldLabel = v;
11564 Roo.log('Cannot Found any of label > span || label in input');
11568 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11569 this.fieldLabel = v;
11584 * @class Roo.bootstrap.TextArea
11585 * @extends Roo.bootstrap.Input
11586 * Bootstrap TextArea class
11587 * @cfg {Number} cols Specifies the visible width of a text area
11588 * @cfg {Number} rows Specifies the visible number of lines in a text area
11589 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11590 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11591 * @cfg {string} html text
11594 * Create a new TextArea
11595 * @param {Object} config The config object
11598 Roo.bootstrap.TextArea = function(config){
11599 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11603 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11613 getAutoCreate : function(){
11615 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11621 if(this.inputType != 'hidden'){
11622 cfg.cls = 'form-group' //input-group
11630 value : this.value || '',
11631 html: this.html || '',
11632 cls : 'form-control',
11633 placeholder : this.placeholder || ''
11637 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11638 input.maxLength = this.maxLength;
11642 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11646 input.cols = this.cols;
11649 if (this.readOnly) {
11650 input.readonly = true;
11654 input.name = this.name;
11658 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11662 ['xs','sm','md','lg'].map(function(size){
11663 if (settings[size]) {
11664 cfg.cls += ' col-' + size + '-' + settings[size];
11668 var inputblock = input;
11670 if(this.hasFeedback && !this.allowBlank){
11674 cls: 'glyphicon form-control-feedback'
11678 cls : 'has-feedback',
11687 if (this.before || this.after) {
11690 cls : 'input-group',
11694 inputblock.cn.push({
11696 cls : 'input-group-addon',
11701 inputblock.cn.push(input);
11703 if(this.hasFeedback && !this.allowBlank){
11704 inputblock.cls += ' has-feedback';
11705 inputblock.cn.push(feedback);
11709 inputblock.cn.push({
11711 cls : 'input-group-addon',
11718 if (align ==='left' && this.fieldLabel.length) {
11723 cls : 'control-label',
11724 html : this.fieldLabel
11735 if(this.labelWidth > 12){
11736 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11739 if(this.labelWidth < 13 && this.labelmd == 0){
11740 this.labelmd = this.labelWidth;
11743 if(this.labellg > 0){
11744 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11745 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11748 if(this.labelmd > 0){
11749 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11750 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11753 if(this.labelsm > 0){
11754 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11755 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11758 if(this.labelxs > 0){
11759 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11760 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11763 } else if ( this.fieldLabel.length) {
11768 //cls : 'input-group-addon',
11769 html : this.fieldLabel
11787 if (this.disabled) {
11788 input.disabled=true;
11795 * return the real textarea element.
11797 inputEl: function ()
11799 return this.el.select('textarea.form-control',true).first();
11803 * Clear any invalid styles/messages for this field
11805 clearInvalid : function()
11808 if(!this.el || this.preventMark){ // not rendered
11812 var label = this.el.select('label', true).first();
11813 var icon = this.el.select('i.fa-star', true).first();
11818 this.el.removeClass( this.validClass);
11819 this.inputEl().removeClass('is-invalid');
11821 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11823 var feedback = this.el.select('.form-control-feedback', true).first();
11826 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11831 this.fireEvent('valid', this);
11835 * Mark this field as valid
11837 markValid : function()
11839 if(!this.el || this.preventMark){ // not rendered
11843 this.el.removeClass([this.invalidClass, this.validClass]);
11844 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11846 var feedback = this.el.select('.form-control-feedback', true).first();
11849 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11852 if(this.disabled || this.allowBlank){
11856 var label = this.el.select('label', true).first();
11857 var icon = this.el.select('i.fa-star', true).first();
11862 if (Roo.bootstrap.version == 3) {
11863 this.el.addClass(this.validClass);
11865 this.inputEl().addClass('is-valid');
11869 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11871 var feedback = this.el.select('.form-control-feedback', true).first();
11874 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11875 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11880 this.fireEvent('valid', this);
11884 * Mark this field as invalid
11885 * @param {String} msg The validation message
11887 markInvalid : function(msg)
11889 if(!this.el || this.preventMark){ // not rendered
11893 this.el.removeClass([this.invalidClass, this.validClass]);
11894 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11896 var feedback = this.el.select('.form-control-feedback', true).first();
11899 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11902 if(this.disabled || this.allowBlank){
11906 var label = this.el.select('label', true).first();
11907 var icon = this.el.select('i.fa-star', true).first();
11909 if(!this.getValue().length && label && !icon){
11910 this.el.createChild({
11912 cls : 'text-danger fa fa-lg fa-star',
11913 tooltip : 'This field is required',
11914 style : 'margin-right:5px;'
11918 if (Roo.bootstrap.version == 3) {
11919 this.el.addClass(this.invalidClass);
11921 this.inputEl().addClass('is-invalid');
11924 // fixme ... this may be depricated need to test..
11925 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11927 var feedback = this.el.select('.form-control-feedback', true).first();
11930 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11932 if(this.getValue().length || this.forceFeedback){
11933 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11940 this.fireEvent('invalid', this, msg);
11948 * trigger field - base class for combo..
11953 * @class Roo.bootstrap.TriggerField
11954 * @extends Roo.bootstrap.Input
11955 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11956 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11957 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11958 * for which you can provide a custom implementation. For example:
11960 var trigger = new Roo.bootstrap.TriggerField();
11961 trigger.onTriggerClick = myTriggerFn;
11962 trigger.applyTo('my-field');
11965 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11966 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11967 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11968 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11969 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11972 * Create a new TriggerField.
11973 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11974 * to the base TextField)
11976 Roo.bootstrap.TriggerField = function(config){
11977 this.mimicing = false;
11978 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11981 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11983 * @cfg {String} triggerClass A CSS class to apply to the trigger
11986 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11991 * @cfg {Boolean} removable (true|false) special filter default false
11995 /** @cfg {Boolean} grow @hide */
11996 /** @cfg {Number} growMin @hide */
11997 /** @cfg {Number} growMax @hide */
12003 autoSize: Roo.emptyFn,
12007 deferHeight : true,
12010 actionMode : 'wrap',
12015 getAutoCreate : function(){
12017 var align = this.labelAlign || this.parentLabelAlign();
12022 cls: 'form-group' //input-group
12029 type : this.inputType,
12030 cls : 'form-control',
12031 autocomplete: 'new-password',
12032 placeholder : this.placeholder || ''
12036 input.name = this.name;
12039 input.cls += ' input-' + this.size;
12042 if (this.disabled) {
12043 input.disabled=true;
12046 var inputblock = input;
12048 if(this.hasFeedback && !this.allowBlank){
12052 cls: 'glyphicon form-control-feedback'
12055 if(this.removable && !this.editable ){
12057 cls : 'has-feedback',
12063 cls : 'roo-combo-removable-btn close'
12070 cls : 'has-feedback',
12079 if(this.removable && !this.editable ){
12081 cls : 'roo-removable',
12087 cls : 'roo-combo-removable-btn close'
12094 if (this.before || this.after) {
12097 cls : 'input-group',
12101 inputblock.cn.push({
12103 cls : 'input-group-addon input-group-prepend input-group-text',
12108 inputblock.cn.push(input);
12110 if(this.hasFeedback && !this.allowBlank){
12111 inputblock.cls += ' has-feedback';
12112 inputblock.cn.push(feedback);
12116 inputblock.cn.push({
12118 cls : 'input-group-addon input-group-append input-group-text',
12127 var ibwrap = inputblock;
12132 cls: 'roo-select2-choices',
12136 cls: 'roo-select2-search-field',
12148 cls: 'roo-select2-container input-group',
12153 cls: 'form-hidden-field'
12159 if(!this.multiple && this.showToggleBtn){
12165 if (this.caret != false) {
12168 cls: 'fa fa-' + this.caret
12175 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12177 Roo.bootstrap.version == 3 ? caret : '',
12180 cls: 'combobox-clear',
12194 combobox.cls += ' roo-select2-container-multi';
12198 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12199 tooltip : 'This field is required'
12201 if (Roo.bootstrap.version == 4) {
12204 style : 'display:none'
12209 if (align ==='left' && this.fieldLabel.length) {
12211 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12218 cls : 'control-label',
12219 html : this.fieldLabel
12231 var labelCfg = cfg.cn[1];
12232 var contentCfg = cfg.cn[2];
12234 if(this.indicatorpos == 'right'){
12239 cls : 'control-label',
12243 html : this.fieldLabel
12257 labelCfg = cfg.cn[0];
12258 contentCfg = cfg.cn[1];
12261 if(this.labelWidth > 12){
12262 labelCfg.style = "width: " + this.labelWidth + 'px';
12265 if(this.labelWidth < 13 && this.labelmd == 0){
12266 this.labelmd = this.labelWidth;
12269 if(this.labellg > 0){
12270 labelCfg.cls += ' col-lg-' + this.labellg;
12271 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12274 if(this.labelmd > 0){
12275 labelCfg.cls += ' col-md-' + this.labelmd;
12276 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12279 if(this.labelsm > 0){
12280 labelCfg.cls += ' col-sm-' + this.labelsm;
12281 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12284 if(this.labelxs > 0){
12285 labelCfg.cls += ' col-xs-' + this.labelxs;
12286 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12289 } else if ( this.fieldLabel.length) {
12290 // Roo.log(" label");
12295 //cls : 'input-group-addon',
12296 html : this.fieldLabel
12304 if(this.indicatorpos == 'right'){
12312 html : this.fieldLabel
12326 // Roo.log(" no label && no align");
12333 ['xs','sm','md','lg'].map(function(size){
12334 if (settings[size]) {
12335 cfg.cls += ' col-' + size + '-' + settings[size];
12346 onResize : function(w, h){
12347 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12348 // if(typeof w == 'number'){
12349 // var x = w - this.trigger.getWidth();
12350 // this.inputEl().setWidth(this.adjustWidth('input', x));
12351 // this.trigger.setStyle('left', x+'px');
12356 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12359 getResizeEl : function(){
12360 return this.inputEl();
12364 getPositionEl : function(){
12365 return this.inputEl();
12369 alignErrorIcon : function(){
12370 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12374 initEvents : function(){
12378 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12379 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12380 if(!this.multiple && this.showToggleBtn){
12381 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12382 if(this.hideTrigger){
12383 this.trigger.setDisplayed(false);
12385 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12389 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12392 if(this.removable && !this.editable && !this.tickable){
12393 var close = this.closeTriggerEl();
12396 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12397 close.on('click', this.removeBtnClick, this, close);
12401 //this.trigger.addClassOnOver('x-form-trigger-over');
12402 //this.trigger.addClassOnClick('x-form-trigger-click');
12405 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12409 closeTriggerEl : function()
12411 var close = this.el.select('.roo-combo-removable-btn', true).first();
12412 return close ? close : false;
12415 removeBtnClick : function(e, h, el)
12417 e.preventDefault();
12419 if(this.fireEvent("remove", this) !== false){
12421 this.fireEvent("afterremove", this)
12425 createList : function()
12427 this.list = Roo.get(document.body).createChild({
12428 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12429 cls: 'typeahead typeahead-long dropdown-menu shadow',
12430 style: 'display:none'
12433 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12438 initTrigger : function(){
12443 onDestroy : function(){
12445 this.trigger.removeAllListeners();
12446 // this.trigger.remove();
12449 // this.wrap.remove();
12451 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12455 onFocus : function(){
12456 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12458 if(!this.mimicing){
12459 this.wrap.addClass('x-trigger-wrap-focus');
12460 this.mimicing = true;
12461 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12462 if(this.monitorTab){
12463 this.el.on("keydown", this.checkTab, this);
12470 checkTab : function(e){
12471 if(e.getKey() == e.TAB){
12472 this.triggerBlur();
12477 onBlur : function(){
12482 mimicBlur : function(e, t){
12484 if(!this.wrap.contains(t) && this.validateBlur()){
12485 this.triggerBlur();
12491 triggerBlur : function(){
12492 this.mimicing = false;
12493 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12494 if(this.monitorTab){
12495 this.el.un("keydown", this.checkTab, this);
12497 //this.wrap.removeClass('x-trigger-wrap-focus');
12498 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12502 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12503 validateBlur : function(e, t){
12508 onDisable : function(){
12509 this.inputEl().dom.disabled = true;
12510 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12512 // this.wrap.addClass('x-item-disabled');
12517 onEnable : function(){
12518 this.inputEl().dom.disabled = false;
12519 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12521 // this.el.removeClass('x-item-disabled');
12526 onShow : function(){
12527 var ae = this.getActionEl();
12530 ae.dom.style.display = '';
12531 ae.dom.style.visibility = 'visible';
12537 onHide : function(){
12538 var ae = this.getActionEl();
12539 ae.dom.style.display = 'none';
12543 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12544 * by an implementing function.
12546 * @param {EventObject} e
12548 onTriggerClick : Roo.emptyFn
12556 * @class Roo.bootstrap.CardUploader
12557 * @extends Roo.bootstrap.Button
12558 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12559 * @cfg {Number} errorTimeout default 3000
12560 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12561 * @cfg {Array} html The button text.
12565 * Create a new CardUploader
12566 * @param {Object} config The config object
12569 Roo.bootstrap.CardUploader = function(config){
12573 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12576 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12584 * When a image is clicked on - and needs to display a slideshow or similar..
12585 * @param {Roo.bootstrap.Card} this
12586 * @param {Object} The image information data
12592 * When a the download link is clicked
12593 * @param {Roo.bootstrap.Card} this
12594 * @param {Object} The image information data contains
12601 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12604 errorTimeout : 3000,
12608 fileCollection : false,
12611 getAutoCreate : function()
12615 cls :'form-group' ,
12620 //cls : 'input-group-addon',
12621 html : this.fieldLabel
12629 value : this.value,
12630 cls : 'd-none form-control'
12635 multiple : 'multiple',
12637 cls : 'd-none roo-card-upload-selector'
12641 cls : 'roo-card-uploader-button-container w-100 mb-2'
12644 cls : 'card-columns roo-card-uploader-container'
12654 getChildContainer : function() /// what children are added to.
12656 return this.containerEl;
12659 getButtonContainer : function() /// what children are added to.
12661 return this.el.select(".roo-card-uploader-button-container").first();
12664 initEvents : function()
12667 Roo.bootstrap.Input.prototype.initEvents.call(this);
12671 xns: Roo.bootstrap,
12674 container_method : 'getButtonContainer' ,
12675 html : this.html, // fix changable?
12678 'click' : function(btn, e) {
12687 this.urlAPI = (window.createObjectURL && window) ||
12688 (window.URL && URL.revokeObjectURL && URL) ||
12689 (window.webkitURL && webkitURL);
12694 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12696 this.selectorEl.on('change', this.onFileSelected, this);
12699 this.images.forEach(function(img) {
12702 this.images = false;
12704 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12710 onClick : function(e)
12712 e.preventDefault();
12714 this.selectorEl.dom.click();
12718 onFileSelected : function(e)
12720 e.preventDefault();
12722 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12726 Roo.each(this.selectorEl.dom.files, function(file){
12727 this.addFile(file);
12736 addFile : function(file)
12739 if(typeof(file) === 'string'){
12740 throw "Add file by name?"; // should not happen
12744 if(!file || !this.urlAPI){
12754 var url = _this.urlAPI.createObjectURL( file);
12757 id : Roo.bootstrap.CardUploader.ID--,
12758 is_uploaded : false,
12762 mimetype : file.type,
12770 * addCard - add an Attachment to the uploader
12771 * @param data - the data about the image to upload
12775 title : "Title of file",
12776 is_uploaded : false,
12777 src : "http://.....",
12778 srcfile : { the File upload object },
12779 mimetype : file.type,
12782 .. any other data...
12788 addCard : function (data)
12790 // hidden input element?
12791 // if the file is not an image...
12792 //then we need to use something other that and header_image
12797 xns : Roo.bootstrap,
12798 xtype : 'CardFooter',
12801 xns : Roo.bootstrap,
12807 xns : Roo.bootstrap,
12809 html : String.format("<small>{0}</small>", data.title),
12810 cls : 'col-10 text-left',
12815 click : function() {
12817 t.fireEvent( "download", t, data );
12823 xns : Roo.bootstrap,
12825 style: 'max-height: 28px; ',
12831 click : function() {
12832 t.removeCard(data.id)
12844 var cn = this.addxtype(
12847 xns : Roo.bootstrap,
12850 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12851 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12852 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12857 initEvents : function() {
12858 Roo.bootstrap.Card.prototype.initEvents.call(this);
12860 this.imgEl = this.el.select('.card-img-top').first();
12862 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12863 this.imgEl.set({ 'pointer' : 'cursor' });
12866 this.getCardFooter().addClass('p-1');
12873 // dont' really need ot update items.
12874 // this.items.push(cn);
12875 this.fileCollection.add(cn);
12877 if (!data.srcfile) {
12878 this.updateInput();
12883 var reader = new FileReader();
12884 reader.addEventListener("load", function() {
12885 data.srcdata = reader.result;
12888 reader.readAsDataURL(data.srcfile);
12893 removeCard : function(id)
12896 var card = this.fileCollection.get(id);
12897 card.data.is_deleted = 1;
12898 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12899 //this.fileCollection.remove(card);
12900 //this.items = this.items.filter(function(e) { return e != card });
12901 // dont' really need ot update items.
12902 card.el.dom.parentNode.removeChild(card.el.dom);
12903 this.updateInput();
12909 this.fileCollection.each(function(card) {
12910 if (card.el.dom && card.el.dom.parentNode) {
12911 card.el.dom.parentNode.removeChild(card.el.dom);
12914 this.fileCollection.clear();
12915 this.updateInput();
12918 updateInput : function()
12921 this.fileCollection.each(function(e) {
12925 this.inputEl().dom.value = JSON.stringify(data);
12935 Roo.bootstrap.CardUploader.ID = -1;/*
12937 * Ext JS Library 1.1.1
12938 * Copyright(c) 2006-2007, Ext JS, LLC.
12940 * Originally Released Under LGPL - original licence link has changed is not relivant.
12943 * <script type="text/javascript">
12948 * @class Roo.data.SortTypes
12950 * Defines the default sorting (casting?) comparison functions used when sorting data.
12952 Roo.data.SortTypes = {
12954 * Default sort that does nothing
12955 * @param {Mixed} s The value being converted
12956 * @return {Mixed} The comparison value
12958 none : function(s){
12963 * The regular expression used to strip tags
12967 stripTagsRE : /<\/?[^>]+>/gi,
12970 * Strips all HTML tags to sort on text only
12971 * @param {Mixed} s The value being converted
12972 * @return {String} The comparison value
12974 asText : function(s){
12975 return String(s).replace(this.stripTagsRE, "");
12979 * Strips all HTML tags to sort on text only - Case insensitive
12980 * @param {Mixed} s The value being converted
12981 * @return {String} The comparison value
12983 asUCText : function(s){
12984 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12988 * Case insensitive string
12989 * @param {Mixed} s The value being converted
12990 * @return {String} The comparison value
12992 asUCString : function(s) {
12993 return String(s).toUpperCase();
12998 * @param {Mixed} s The value being converted
12999 * @return {Number} The comparison value
13001 asDate : function(s) {
13005 if(s instanceof Date){
13006 return s.getTime();
13008 return Date.parse(String(s));
13013 * @param {Mixed} s The value being converted
13014 * @return {Float} The comparison value
13016 asFloat : function(s) {
13017 var val = parseFloat(String(s).replace(/,/g, ""));
13026 * @param {Mixed} s The value being converted
13027 * @return {Number} The comparison value
13029 asInt : function(s) {
13030 var val = parseInt(String(s).replace(/,/g, ""));
13038 * Ext JS Library 1.1.1
13039 * Copyright(c) 2006-2007, Ext JS, LLC.
13041 * Originally Released Under LGPL - original licence link has changed is not relivant.
13044 * <script type="text/javascript">
13048 * @class Roo.data.Record
13049 * Instances of this class encapsulate both record <em>definition</em> information, and record
13050 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13051 * to access Records cached in an {@link Roo.data.Store} object.<br>
13053 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13054 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13057 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13059 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13060 * {@link #create}. The parameters are the same.
13061 * @param {Array} data An associative Array of data values keyed by the field name.
13062 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13063 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13064 * not specified an integer id is generated.
13066 Roo.data.Record = function(data, id){
13067 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13072 * Generate a constructor for a specific record layout.
13073 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13074 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13075 * Each field definition object may contain the following properties: <ul>
13076 * <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,
13077 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13078 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13079 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13080 * is being used, then this is a string containing the javascript expression to reference the data relative to
13081 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13082 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13083 * this may be omitted.</p></li>
13084 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13085 * <ul><li>auto (Default, implies no conversion)</li>
13090 * <li>date</li></ul></p></li>
13091 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13092 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13093 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13094 * by the Reader into an object that will be stored in the Record. It is passed the
13095 * following parameters:<ul>
13096 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13098 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13100 * <br>usage:<br><pre><code>
13101 var TopicRecord = Roo.data.Record.create(
13102 {name: 'title', mapping: 'topic_title'},
13103 {name: 'author', mapping: 'username'},
13104 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13105 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13106 {name: 'lastPoster', mapping: 'user2'},
13107 {name: 'excerpt', mapping: 'post_text'}
13110 var myNewRecord = new TopicRecord({
13111 title: 'Do my job please',
13114 lastPost: new Date(),
13115 lastPoster: 'Animal',
13116 excerpt: 'No way dude!'
13118 myStore.add(myNewRecord);
13123 Roo.data.Record.create = function(o){
13124 var f = function(){
13125 f.superclass.constructor.apply(this, arguments);
13127 Roo.extend(f, Roo.data.Record);
13128 var p = f.prototype;
13129 p.fields = new Roo.util.MixedCollection(false, function(field){
13132 for(var i = 0, len = o.length; i < len; i++){
13133 p.fields.add(new Roo.data.Field(o[i]));
13135 f.getField = function(name){
13136 return p.fields.get(name);
13141 Roo.data.Record.AUTO_ID = 1000;
13142 Roo.data.Record.EDIT = 'edit';
13143 Roo.data.Record.REJECT = 'reject';
13144 Roo.data.Record.COMMIT = 'commit';
13146 Roo.data.Record.prototype = {
13148 * Readonly flag - true if this record has been modified.
13157 join : function(store){
13158 this.store = store;
13162 * Set the named field to the specified value.
13163 * @param {String} name The name of the field to set.
13164 * @param {Object} value The value to set the field to.
13166 set : function(name, value){
13167 if(this.data[name] == value){
13171 if(!this.modified){
13172 this.modified = {};
13174 if(typeof this.modified[name] == 'undefined'){
13175 this.modified[name] = this.data[name];
13177 this.data[name] = value;
13178 if(!this.editing && this.store){
13179 this.store.afterEdit(this);
13184 * Get the value of the named field.
13185 * @param {String} name The name of the field to get the value of.
13186 * @return {Object} The value of the field.
13188 get : function(name){
13189 return this.data[name];
13193 beginEdit : function(){
13194 this.editing = true;
13195 this.modified = {};
13199 cancelEdit : function(){
13200 this.editing = false;
13201 delete this.modified;
13205 endEdit : function(){
13206 this.editing = false;
13207 if(this.dirty && this.store){
13208 this.store.afterEdit(this);
13213 * Usually called by the {@link Roo.data.Store} which owns the Record.
13214 * Rejects all changes made to the Record since either creation, or the last commit operation.
13215 * Modified fields are reverted to their original values.
13217 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13218 * of reject operations.
13220 reject : function(){
13221 var m = this.modified;
13223 if(typeof m[n] != "function"){
13224 this.data[n] = m[n];
13227 this.dirty = false;
13228 delete this.modified;
13229 this.editing = false;
13231 this.store.afterReject(this);
13236 * Usually called by the {@link Roo.data.Store} which owns the Record.
13237 * Commits all changes made to the Record since either creation, or the last commit operation.
13239 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13240 * of commit operations.
13242 commit : function(){
13243 this.dirty = false;
13244 delete this.modified;
13245 this.editing = false;
13247 this.store.afterCommit(this);
13252 hasError : function(){
13253 return this.error != null;
13257 clearError : function(){
13262 * Creates a copy of this record.
13263 * @param {String} id (optional) A new record id if you don't want to use this record's id
13266 copy : function(newId) {
13267 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13271 * Ext JS Library 1.1.1
13272 * Copyright(c) 2006-2007, Ext JS, LLC.
13274 * Originally Released Under LGPL - original licence link has changed is not relivant.
13277 * <script type="text/javascript">
13283 * @class Roo.data.Store
13284 * @extends Roo.util.Observable
13285 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13286 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13288 * 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
13289 * has no knowledge of the format of the data returned by the Proxy.<br>
13291 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13292 * instances from the data object. These records are cached and made available through accessor functions.
13294 * Creates a new Store.
13295 * @param {Object} config A config object containing the objects needed for the Store to access data,
13296 * and read the data into Records.
13298 Roo.data.Store = function(config){
13299 this.data = new Roo.util.MixedCollection(false);
13300 this.data.getKey = function(o){
13303 this.baseParams = {};
13305 this.paramNames = {
13310 "multisort" : "_multisort"
13313 if(config && config.data){
13314 this.inlineData = config.data;
13315 delete config.data;
13318 Roo.apply(this, config);
13320 if(this.reader){ // reader passed
13321 this.reader = Roo.factory(this.reader, Roo.data);
13322 this.reader.xmodule = this.xmodule || false;
13323 if(!this.recordType){
13324 this.recordType = this.reader.recordType;
13326 if(this.reader.onMetaChange){
13327 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13331 if(this.recordType){
13332 this.fields = this.recordType.prototype.fields;
13334 this.modified = [];
13338 * @event datachanged
13339 * Fires when the data cache has changed, and a widget which is using this Store
13340 * as a Record cache should refresh its view.
13341 * @param {Store} this
13343 datachanged : true,
13345 * @event metachange
13346 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13347 * @param {Store} this
13348 * @param {Object} meta The JSON metadata
13353 * Fires when Records have been added to the Store
13354 * @param {Store} this
13355 * @param {Roo.data.Record[]} records The array of Records added
13356 * @param {Number} index The index at which the record(s) were added
13361 * Fires when a Record has been removed from the Store
13362 * @param {Store} this
13363 * @param {Roo.data.Record} record The Record that was removed
13364 * @param {Number} index The index at which the record was removed
13369 * Fires when a Record has been updated
13370 * @param {Store} this
13371 * @param {Roo.data.Record} record The Record that was updated
13372 * @param {String} operation The update operation being performed. Value may be one of:
13374 Roo.data.Record.EDIT
13375 Roo.data.Record.REJECT
13376 Roo.data.Record.COMMIT
13382 * Fires when the data cache has been cleared.
13383 * @param {Store} this
13387 * @event beforeload
13388 * Fires before a request is made for a new data object. If the beforeload handler returns false
13389 * the load action will be canceled.
13390 * @param {Store} this
13391 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13395 * @event beforeloadadd
13396 * Fires after a new set of Records has been loaded.
13397 * @param {Store} this
13398 * @param {Roo.data.Record[]} records The Records that were loaded
13399 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13401 beforeloadadd : true,
13404 * Fires after a new set of Records has been loaded, before they are added to the store.
13405 * @param {Store} this
13406 * @param {Roo.data.Record[]} records The Records that were loaded
13407 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13408 * @params {Object} return from reader
13412 * @event loadexception
13413 * Fires if an exception occurs in the Proxy during loading.
13414 * Called with the signature of the Proxy's "loadexception" event.
13415 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13418 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13419 * @param {Object} load options
13420 * @param {Object} jsonData from your request (normally this contains the Exception)
13422 loadexception : true
13426 this.proxy = Roo.factory(this.proxy, Roo.data);
13427 this.proxy.xmodule = this.xmodule || false;
13428 this.relayEvents(this.proxy, ["loadexception"]);
13430 this.sortToggle = {};
13431 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13433 Roo.data.Store.superclass.constructor.call(this);
13435 if(this.inlineData){
13436 this.loadData(this.inlineData);
13437 delete this.inlineData;
13441 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13443 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13444 * without a remote query - used by combo/forms at present.
13448 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13451 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13454 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13455 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13458 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13459 * on any HTTP request
13462 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13465 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13469 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13470 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13472 remoteSort : false,
13475 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13476 * loaded or when a record is removed. (defaults to false).
13478 pruneModifiedRecords : false,
13481 lastOptions : null,
13484 * Add Records to the Store and fires the add event.
13485 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13487 add : function(records){
13488 records = [].concat(records);
13489 for(var i = 0, len = records.length; i < len; i++){
13490 records[i].join(this);
13492 var index = this.data.length;
13493 this.data.addAll(records);
13494 this.fireEvent("add", this, records, index);
13498 * Remove a Record from the Store and fires the remove event.
13499 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13501 remove : function(record){
13502 var index = this.data.indexOf(record);
13503 this.data.removeAt(index);
13505 if(this.pruneModifiedRecords){
13506 this.modified.remove(record);
13508 this.fireEvent("remove", this, record, index);
13512 * Remove all Records from the Store and fires the clear event.
13514 removeAll : function(){
13516 if(this.pruneModifiedRecords){
13517 this.modified = [];
13519 this.fireEvent("clear", this);
13523 * Inserts Records to the Store at the given index and fires the add event.
13524 * @param {Number} index The start index at which to insert the passed Records.
13525 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13527 insert : function(index, records){
13528 records = [].concat(records);
13529 for(var i = 0, len = records.length; i < len; i++){
13530 this.data.insert(index, records[i]);
13531 records[i].join(this);
13533 this.fireEvent("add", this, records, index);
13537 * Get the index within the cache of the passed Record.
13538 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13539 * @return {Number} The index of the passed Record. Returns -1 if not found.
13541 indexOf : function(record){
13542 return this.data.indexOf(record);
13546 * Get the index within the cache of the Record with the passed id.
13547 * @param {String} id The id of the Record to find.
13548 * @return {Number} The index of the Record. Returns -1 if not found.
13550 indexOfId : function(id){
13551 return this.data.indexOfKey(id);
13555 * Get the Record with the specified id.
13556 * @param {String} id The id of the Record to find.
13557 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13559 getById : function(id){
13560 return this.data.key(id);
13564 * Get the Record at the specified index.
13565 * @param {Number} index The index of the Record to find.
13566 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13568 getAt : function(index){
13569 return this.data.itemAt(index);
13573 * Returns a range of Records between specified indices.
13574 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13575 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13576 * @return {Roo.data.Record[]} An array of Records
13578 getRange : function(start, end){
13579 return this.data.getRange(start, end);
13583 storeOptions : function(o){
13584 o = Roo.apply({}, o);
13587 this.lastOptions = o;
13591 * Loads the Record cache from the configured Proxy using the configured Reader.
13593 * If using remote paging, then the first load call must specify the <em>start</em>
13594 * and <em>limit</em> properties in the options.params property to establish the initial
13595 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13597 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13598 * and this call will return before the new data has been loaded. Perform any post-processing
13599 * in a callback function, or in a "load" event handler.</strong>
13601 * @param {Object} options An object containing properties which control loading options:<ul>
13602 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13603 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13604 * passed the following arguments:<ul>
13605 * <li>r : Roo.data.Record[]</li>
13606 * <li>options: Options object from the load call</li>
13607 * <li>success: Boolean success indicator</li></ul></li>
13608 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13609 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13612 load : function(options){
13613 options = options || {};
13614 if(this.fireEvent("beforeload", this, options) !== false){
13615 this.storeOptions(options);
13616 var p = Roo.apply(options.params || {}, this.baseParams);
13617 // if meta was not loaded from remote source.. try requesting it.
13618 if (!this.reader.metaFromRemote) {
13619 p._requestMeta = 1;
13621 if(this.sortInfo && this.remoteSort){
13622 var pn = this.paramNames;
13623 p[pn["sort"]] = this.sortInfo.field;
13624 p[pn["dir"]] = this.sortInfo.direction;
13626 if (this.multiSort) {
13627 var pn = this.paramNames;
13628 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13631 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13636 * Reloads the Record cache from the configured Proxy using the configured Reader and
13637 * the options from the last load operation performed.
13638 * @param {Object} options (optional) An object containing properties which may override the options
13639 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13640 * the most recently used options are reused).
13642 reload : function(options){
13643 this.load(Roo.applyIf(options||{}, this.lastOptions));
13647 // Called as a callback by the Reader during a load operation.
13648 loadRecords : function(o, options, success){
13649 if(!o || success === false){
13650 if(success !== false){
13651 this.fireEvent("load", this, [], options, o);
13653 if(options.callback){
13654 options.callback.call(options.scope || this, [], options, false);
13658 // if data returned failure - throw an exception.
13659 if (o.success === false) {
13660 // show a message if no listener is registered.
13661 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13662 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13664 // loadmask wil be hooked into this..
13665 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13668 var r = o.records, t = o.totalRecords || r.length;
13670 this.fireEvent("beforeloadadd", this, r, options, o);
13672 if(!options || options.add !== true){
13673 if(this.pruneModifiedRecords){
13674 this.modified = [];
13676 for(var i = 0, len = r.length; i < len; i++){
13680 this.data = this.snapshot;
13681 delete this.snapshot;
13684 this.data.addAll(r);
13685 this.totalLength = t;
13687 this.fireEvent("datachanged", this);
13689 this.totalLength = Math.max(t, this.data.length+r.length);
13693 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13695 var e = new Roo.data.Record({});
13697 e.set(this.parent.displayField, this.parent.emptyTitle);
13698 e.set(this.parent.valueField, '');
13703 this.fireEvent("load", this, r, options, o);
13704 if(options.callback){
13705 options.callback.call(options.scope || this, r, options, true);
13711 * Loads data from a passed data block. A Reader which understands the format of the data
13712 * must have been configured in the constructor.
13713 * @param {Object} data The data block from which to read the Records. The format of the data expected
13714 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13715 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13717 loadData : function(o, append){
13718 var r = this.reader.readRecords(o);
13719 this.loadRecords(r, {add: append}, true);
13723 * using 'cn' the nested child reader read the child array into it's child stores.
13724 * @param {Object} rec The record with a 'children array
13726 loadDataFromChildren : function(rec)
13728 this.loadData(this.reader.toLoadData(rec));
13733 * Gets the number of cached records.
13735 * <em>If using paging, this may not be the total size of the dataset. If the data object
13736 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13737 * the data set size</em>
13739 getCount : function(){
13740 return this.data.length || 0;
13744 * Gets the total number of records in the dataset as returned by the server.
13746 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13747 * the dataset size</em>
13749 getTotalCount : function(){
13750 return this.totalLength || 0;
13754 * Returns the sort state of the Store as an object with two properties:
13756 field {String} The name of the field by which the Records are sorted
13757 direction {String} The sort order, "ASC" or "DESC"
13760 getSortState : function(){
13761 return this.sortInfo;
13765 applySort : function(){
13766 if(this.sortInfo && !this.remoteSort){
13767 var s = this.sortInfo, f = s.field;
13768 var st = this.fields.get(f).sortType;
13769 var fn = function(r1, r2){
13770 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13771 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13773 this.data.sort(s.direction, fn);
13774 if(this.snapshot && this.snapshot != this.data){
13775 this.snapshot.sort(s.direction, fn);
13781 * Sets the default sort column and order to be used by the next load operation.
13782 * @param {String} fieldName The name of the field to sort by.
13783 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13785 setDefaultSort : function(field, dir){
13786 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13790 * Sort the Records.
13791 * If remote sorting is used, the sort is performed on the server, and the cache is
13792 * reloaded. If local sorting is used, the cache is sorted internally.
13793 * @param {String} fieldName The name of the field to sort by.
13794 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13796 sort : function(fieldName, dir){
13797 var f = this.fields.get(fieldName);
13799 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13801 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13802 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13807 this.sortToggle[f.name] = dir;
13808 this.sortInfo = {field: f.name, direction: dir};
13809 if(!this.remoteSort){
13811 this.fireEvent("datachanged", this);
13813 this.load(this.lastOptions);
13818 * Calls the specified function for each of the Records in the cache.
13819 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13820 * Returning <em>false</em> aborts and exits the iteration.
13821 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13823 each : function(fn, scope){
13824 this.data.each(fn, scope);
13828 * Gets all records modified since the last commit. Modified records are persisted across load operations
13829 * (e.g., during paging).
13830 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13832 getModifiedRecords : function(){
13833 return this.modified;
13837 createFilterFn : function(property, value, anyMatch){
13838 if(!value.exec){ // not a regex
13839 value = String(value);
13840 if(value.length == 0){
13843 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13845 return function(r){
13846 return value.test(r.data[property]);
13851 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13852 * @param {String} property A field on your records
13853 * @param {Number} start The record index to start at (defaults to 0)
13854 * @param {Number} end The last record index to include (defaults to length - 1)
13855 * @return {Number} The sum
13857 sum : function(property, start, end){
13858 var rs = this.data.items, v = 0;
13859 start = start || 0;
13860 end = (end || end === 0) ? end : rs.length-1;
13862 for(var i = start; i <= end; i++){
13863 v += (rs[i].data[property] || 0);
13869 * Filter the records by a specified property.
13870 * @param {String} field A field on your records
13871 * @param {String/RegExp} value Either a string that the field
13872 * should start with or a RegExp to test against the field
13873 * @param {Boolean} anyMatch True to match any part not just the beginning
13875 filter : function(property, value, anyMatch){
13876 var fn = this.createFilterFn(property, value, anyMatch);
13877 return fn ? this.filterBy(fn) : this.clearFilter();
13881 * Filter by a function. The specified function will be called with each
13882 * record in this data source. If the function returns true the record is included,
13883 * otherwise it is filtered.
13884 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13885 * @param {Object} scope (optional) The scope of the function (defaults to this)
13887 filterBy : function(fn, scope){
13888 this.snapshot = this.snapshot || this.data;
13889 this.data = this.queryBy(fn, scope||this);
13890 this.fireEvent("datachanged", this);
13894 * Query the records by a specified property.
13895 * @param {String} field A field on your records
13896 * @param {String/RegExp} value Either a string that the field
13897 * should start with or a RegExp to test against the field
13898 * @param {Boolean} anyMatch True to match any part not just the beginning
13899 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13901 query : function(property, value, anyMatch){
13902 var fn = this.createFilterFn(property, value, anyMatch);
13903 return fn ? this.queryBy(fn) : this.data.clone();
13907 * Query by a function. The specified function will be called with each
13908 * record in this data source. If the function returns true the record is included
13910 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13911 * @param {Object} scope (optional) The scope of the function (defaults to this)
13912 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13914 queryBy : function(fn, scope){
13915 var data = this.snapshot || this.data;
13916 return data.filterBy(fn, scope||this);
13920 * Collects unique values for a particular dataIndex from this store.
13921 * @param {String} dataIndex The property to collect
13922 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13923 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13924 * @return {Array} An array of the unique values
13926 collect : function(dataIndex, allowNull, bypassFilter){
13927 var d = (bypassFilter === true && this.snapshot) ?
13928 this.snapshot.items : this.data.items;
13929 var v, sv, r = [], l = {};
13930 for(var i = 0, len = d.length; i < len; i++){
13931 v = d[i].data[dataIndex];
13933 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13942 * Revert to a view of the Record cache with no filtering applied.
13943 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13945 clearFilter : function(suppressEvent){
13946 if(this.snapshot && this.snapshot != this.data){
13947 this.data = this.snapshot;
13948 delete this.snapshot;
13949 if(suppressEvent !== true){
13950 this.fireEvent("datachanged", this);
13956 afterEdit : function(record){
13957 if(this.modified.indexOf(record) == -1){
13958 this.modified.push(record);
13960 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13964 afterReject : function(record){
13965 this.modified.remove(record);
13966 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13970 afterCommit : function(record){
13971 this.modified.remove(record);
13972 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13976 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13977 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13979 commitChanges : function(){
13980 var m = this.modified.slice(0);
13981 this.modified = [];
13982 for(var i = 0, len = m.length; i < len; i++){
13988 * Cancel outstanding changes on all changed records.
13990 rejectChanges : function(){
13991 var m = this.modified.slice(0);
13992 this.modified = [];
13993 for(var i = 0, len = m.length; i < len; i++){
13998 onMetaChange : function(meta, rtype, o){
13999 this.recordType = rtype;
14000 this.fields = rtype.prototype.fields;
14001 delete this.snapshot;
14002 this.sortInfo = meta.sortInfo || this.sortInfo;
14003 this.modified = [];
14004 this.fireEvent('metachange', this, this.reader.meta);
14007 moveIndex : function(data, type)
14009 var index = this.indexOf(data);
14011 var newIndex = index + type;
14015 this.insert(newIndex, data);
14020 * Ext JS Library 1.1.1
14021 * Copyright(c) 2006-2007, Ext JS, LLC.
14023 * Originally Released Under LGPL - original licence link has changed is not relivant.
14026 * <script type="text/javascript">
14030 * @class Roo.data.SimpleStore
14031 * @extends Roo.data.Store
14032 * Small helper class to make creating Stores from Array data easier.
14033 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14034 * @cfg {Array} fields An array of field definition objects, or field name strings.
14035 * @cfg {Object} an existing reader (eg. copied from another store)
14036 * @cfg {Array} data The multi-dimensional array of data
14038 * @param {Object} config
14040 Roo.data.SimpleStore = function(config)
14042 Roo.data.SimpleStore.superclass.constructor.call(this, {
14044 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14047 Roo.data.Record.create(config.fields)
14049 proxy : new Roo.data.MemoryProxy(config.data)
14053 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14055 * Ext JS Library 1.1.1
14056 * Copyright(c) 2006-2007, Ext JS, LLC.
14058 * Originally Released Under LGPL - original licence link has changed is not relivant.
14061 * <script type="text/javascript">
14066 * @extends Roo.data.Store
14067 * @class Roo.data.JsonStore
14068 * Small helper class to make creating Stores for JSON data easier. <br/>
14070 var store = new Roo.data.JsonStore({
14071 url: 'get-images.php',
14073 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14076 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14077 * JsonReader and HttpProxy (unless inline data is provided).</b>
14078 * @cfg {Array} fields An array of field definition objects, or field name strings.
14080 * @param {Object} config
14082 Roo.data.JsonStore = function(c){
14083 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14084 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14085 reader: new Roo.data.JsonReader(c, c.fields)
14088 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14090 * Ext JS Library 1.1.1
14091 * Copyright(c) 2006-2007, Ext JS, LLC.
14093 * Originally Released Under LGPL - original licence link has changed is not relivant.
14096 * <script type="text/javascript">
14100 Roo.data.Field = function(config){
14101 if(typeof config == "string"){
14102 config = {name: config};
14104 Roo.apply(this, config);
14107 this.type = "auto";
14110 var st = Roo.data.SortTypes;
14111 // named sortTypes are supported, here we look them up
14112 if(typeof this.sortType == "string"){
14113 this.sortType = st[this.sortType];
14116 // set default sortType for strings and dates
14117 if(!this.sortType){
14120 this.sortType = st.asUCString;
14123 this.sortType = st.asDate;
14126 this.sortType = st.none;
14131 var stripRe = /[\$,%]/g;
14133 // prebuilt conversion function for this field, instead of
14134 // switching every time we're reading a value
14136 var cv, dateFormat = this.dateFormat;
14141 cv = function(v){ return v; };
14144 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14148 return v !== undefined && v !== null && v !== '' ?
14149 parseInt(String(v).replace(stripRe, ""), 10) : '';
14154 return v !== undefined && v !== null && v !== '' ?
14155 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14160 cv = function(v){ return v === true || v === "true" || v == 1; };
14167 if(v instanceof Date){
14171 if(dateFormat == "timestamp"){
14172 return new Date(v*1000);
14174 return Date.parseDate(v, dateFormat);
14176 var parsed = Date.parse(v);
14177 return parsed ? new Date(parsed) : null;
14186 Roo.data.Field.prototype = {
14194 * Ext JS Library 1.1.1
14195 * Copyright(c) 2006-2007, Ext JS, LLC.
14197 * Originally Released Under LGPL - original licence link has changed is not relivant.
14200 * <script type="text/javascript">
14203 // Base class for reading structured data from a data source. This class is intended to be
14204 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14207 * @class Roo.data.DataReader
14208 * Base class for reading structured data from a data source. This class is intended to be
14209 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14212 Roo.data.DataReader = function(meta, recordType){
14216 this.recordType = recordType instanceof Array ?
14217 Roo.data.Record.create(recordType) : recordType;
14220 Roo.data.DataReader.prototype = {
14223 readerType : 'Data',
14225 * Create an empty record
14226 * @param {Object} data (optional) - overlay some values
14227 * @return {Roo.data.Record} record created.
14229 newRow : function(d) {
14231 this.recordType.prototype.fields.each(function(c) {
14233 case 'int' : da[c.name] = 0; break;
14234 case 'date' : da[c.name] = new Date(); break;
14235 case 'float' : da[c.name] = 0.0; break;
14236 case 'boolean' : da[c.name] = false; break;
14237 default : da[c.name] = ""; break;
14241 return new this.recordType(Roo.apply(da, d));
14247 * Ext JS Library 1.1.1
14248 * Copyright(c) 2006-2007, Ext JS, LLC.
14250 * Originally Released Under LGPL - original licence link has changed is not relivant.
14253 * <script type="text/javascript">
14257 * @class Roo.data.DataProxy
14258 * @extends Roo.data.Observable
14259 * This class is an abstract base class for implementations which provide retrieval of
14260 * unformatted data objects.<br>
14262 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14263 * (of the appropriate type which knows how to parse the data object) to provide a block of
14264 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14266 * Custom implementations must implement the load method as described in
14267 * {@link Roo.data.HttpProxy#load}.
14269 Roo.data.DataProxy = function(){
14272 * @event beforeload
14273 * Fires before a network request is made to retrieve a data object.
14274 * @param {Object} This DataProxy object.
14275 * @param {Object} params The params parameter to the load function.
14280 * Fires before the load method's callback is called.
14281 * @param {Object} This DataProxy object.
14282 * @param {Object} o The data object.
14283 * @param {Object} arg The callback argument object passed to the load function.
14287 * @event loadexception
14288 * Fires if an Exception occurs during data retrieval.
14289 * @param {Object} This DataProxy object.
14290 * @param {Object} o The data object.
14291 * @param {Object} arg The callback argument object passed to the load function.
14292 * @param {Object} e The Exception.
14294 loadexception : true
14296 Roo.data.DataProxy.superclass.constructor.call(this);
14299 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14302 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14306 * Ext JS Library 1.1.1
14307 * Copyright(c) 2006-2007, Ext JS, LLC.
14309 * Originally Released Under LGPL - original licence link has changed is not relivant.
14312 * <script type="text/javascript">
14315 * @class Roo.data.MemoryProxy
14316 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14317 * to the Reader when its load method is called.
14319 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14321 Roo.data.MemoryProxy = function(data){
14325 Roo.data.MemoryProxy.superclass.constructor.call(this);
14329 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14332 * Load data from the requested source (in this case an in-memory
14333 * data object passed to the constructor), read the data object into
14334 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14335 * process that block using the passed callback.
14336 * @param {Object} params This parameter is not used by the MemoryProxy class.
14337 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14338 * object into a block of Roo.data.Records.
14339 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14340 * The function must be passed <ul>
14341 * <li>The Record block object</li>
14342 * <li>The "arg" argument from the load function</li>
14343 * <li>A boolean success indicator</li>
14345 * @param {Object} scope The scope in which to call the callback
14346 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14348 load : function(params, reader, callback, scope, arg){
14349 params = params || {};
14352 result = reader.readRecords(params.data ? params.data :this.data);
14354 this.fireEvent("loadexception", this, arg, null, e);
14355 callback.call(scope, null, arg, false);
14358 callback.call(scope, result, arg, true);
14362 update : function(params, records){
14367 * Ext JS Library 1.1.1
14368 * Copyright(c) 2006-2007, Ext JS, LLC.
14370 * Originally Released Under LGPL - original licence link has changed is not relivant.
14373 * <script type="text/javascript">
14376 * @class Roo.data.HttpProxy
14377 * @extends Roo.data.DataProxy
14378 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14379 * configured to reference a certain URL.<br><br>
14381 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14382 * from which the running page was served.<br><br>
14384 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14386 * Be aware that to enable the browser to parse an XML document, the server must set
14387 * the Content-Type header in the HTTP response to "text/xml".
14389 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14390 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14391 * will be used to make the request.
14393 Roo.data.HttpProxy = function(conn){
14394 Roo.data.HttpProxy.superclass.constructor.call(this);
14395 // is conn a conn config or a real conn?
14397 this.useAjax = !conn || !conn.events;
14401 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14402 // thse are take from connection...
14405 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14408 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14409 * extra parameters to each request made by this object. (defaults to undefined)
14412 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14413 * to each request made by this object. (defaults to undefined)
14416 * @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)
14419 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14422 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14428 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14432 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14433 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14434 * a finer-grained basis than the DataProxy events.
14436 getConnection : function(){
14437 return this.useAjax ? Roo.Ajax : this.conn;
14441 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14442 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14443 * process that block using the passed callback.
14444 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14445 * for the request to the remote server.
14446 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14447 * object into a block of Roo.data.Records.
14448 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14449 * The function must be passed <ul>
14450 * <li>The Record block object</li>
14451 * <li>The "arg" argument from the load function</li>
14452 * <li>A boolean success indicator</li>
14454 * @param {Object} scope The scope in which to call the callback
14455 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14457 load : function(params, reader, callback, scope, arg){
14458 if(this.fireEvent("beforeload", this, params) !== false){
14460 params : params || {},
14462 callback : callback,
14467 callback : this.loadResponse,
14471 Roo.applyIf(o, this.conn);
14472 if(this.activeRequest){
14473 Roo.Ajax.abort(this.activeRequest);
14475 this.activeRequest = Roo.Ajax.request(o);
14477 this.conn.request(o);
14480 callback.call(scope||this, null, arg, false);
14485 loadResponse : function(o, success, response){
14486 delete this.activeRequest;
14488 this.fireEvent("loadexception", this, o, response);
14489 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14494 result = o.reader.read(response);
14496 this.fireEvent("loadexception", this, o, response, e);
14497 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14501 this.fireEvent("load", this, o, o.request.arg);
14502 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14506 update : function(dataSet){
14511 updateResponse : function(dataSet){
14516 * Ext JS Library 1.1.1
14517 * Copyright(c) 2006-2007, Ext JS, LLC.
14519 * Originally Released Under LGPL - original licence link has changed is not relivant.
14522 * <script type="text/javascript">
14526 * @class Roo.data.ScriptTagProxy
14527 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14528 * other than the originating domain of the running page.<br><br>
14530 * <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
14531 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14533 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14534 * source code that is used as the source inside a <script> tag.<br><br>
14536 * In order for the browser to process the returned data, the server must wrap the data object
14537 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14538 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14539 * depending on whether the callback name was passed:
14542 boolean scriptTag = false;
14543 String cb = request.getParameter("callback");
14546 response.setContentType("text/javascript");
14548 response.setContentType("application/x-json");
14550 Writer out = response.getWriter();
14552 out.write(cb + "(");
14554 out.print(dataBlock.toJsonString());
14561 * @param {Object} config A configuration object.
14563 Roo.data.ScriptTagProxy = function(config){
14564 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14565 Roo.apply(this, config);
14566 this.head = document.getElementsByTagName("head")[0];
14569 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14571 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14573 * @cfg {String} url The URL from which to request the data object.
14576 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14580 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14581 * the server the name of the callback function set up by the load call to process the returned data object.
14582 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14583 * javascript output which calls this named function passing the data object as its only parameter.
14585 callbackParam : "callback",
14587 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14588 * name to the request.
14593 * Load data from the configured URL, read the data object into
14594 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14595 * process that block using the passed callback.
14596 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14597 * for the request to the remote server.
14598 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14599 * object into a block of Roo.data.Records.
14600 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14601 * The function must be passed <ul>
14602 * <li>The Record block object</li>
14603 * <li>The "arg" argument from the load function</li>
14604 * <li>A boolean success indicator</li>
14606 * @param {Object} scope The scope in which to call the callback
14607 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14609 load : function(params, reader, callback, scope, arg){
14610 if(this.fireEvent("beforeload", this, params) !== false){
14612 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14614 var url = this.url;
14615 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14617 url += "&_dc=" + (new Date().getTime());
14619 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14622 cb : "stcCallback"+transId,
14623 scriptId : "stcScript"+transId,
14627 callback : callback,
14633 window[trans.cb] = function(o){
14634 conn.handleResponse(o, trans);
14637 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14639 if(this.autoAbort !== false){
14643 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14645 var script = document.createElement("script");
14646 script.setAttribute("src", url);
14647 script.setAttribute("type", "text/javascript");
14648 script.setAttribute("id", trans.scriptId);
14649 this.head.appendChild(script);
14651 this.trans = trans;
14653 callback.call(scope||this, null, arg, false);
14658 isLoading : function(){
14659 return this.trans ? true : false;
14663 * Abort the current server request.
14665 abort : function(){
14666 if(this.isLoading()){
14667 this.destroyTrans(this.trans);
14672 destroyTrans : function(trans, isLoaded){
14673 this.head.removeChild(document.getElementById(trans.scriptId));
14674 clearTimeout(trans.timeoutId);
14676 window[trans.cb] = undefined;
14678 delete window[trans.cb];
14681 // if hasn't been loaded, wait for load to remove it to prevent script error
14682 window[trans.cb] = function(){
14683 window[trans.cb] = undefined;
14685 delete window[trans.cb];
14692 handleResponse : function(o, trans){
14693 this.trans = false;
14694 this.destroyTrans(trans, true);
14697 result = trans.reader.readRecords(o);
14699 this.fireEvent("loadexception", this, o, trans.arg, e);
14700 trans.callback.call(trans.scope||window, null, trans.arg, false);
14703 this.fireEvent("load", this, o, trans.arg);
14704 trans.callback.call(trans.scope||window, result, trans.arg, true);
14708 handleFailure : function(trans){
14709 this.trans = false;
14710 this.destroyTrans(trans, false);
14711 this.fireEvent("loadexception", this, null, trans.arg);
14712 trans.callback.call(trans.scope||window, null, trans.arg, false);
14716 * Ext JS Library 1.1.1
14717 * Copyright(c) 2006-2007, Ext JS, LLC.
14719 * Originally Released Under LGPL - original licence link has changed is not relivant.
14722 * <script type="text/javascript">
14726 * @class Roo.data.JsonReader
14727 * @extends Roo.data.DataReader
14728 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14729 * based on mappings in a provided Roo.data.Record constructor.
14731 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14732 * in the reply previously.
14737 var RecordDef = Roo.data.Record.create([
14738 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14739 {name: 'occupation'} // This field will use "occupation" as the mapping.
14741 var myReader = new Roo.data.JsonReader({
14742 totalProperty: "results", // The property which contains the total dataset size (optional)
14743 root: "rows", // The property which contains an Array of row objects
14744 id: "id" // The property within each row object that provides an ID for the record (optional)
14748 * This would consume a JSON file like this:
14750 { 'results': 2, 'rows': [
14751 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14752 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14755 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14756 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14757 * paged from the remote server.
14758 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14759 * @cfg {String} root name of the property which contains the Array of row objects.
14760 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14761 * @cfg {Array} fields Array of field definition objects
14763 * Create a new JsonReader
14764 * @param {Object} meta Metadata configuration options
14765 * @param {Object} recordType Either an Array of field definition objects,
14766 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14768 Roo.data.JsonReader = function(meta, recordType){
14771 // set some defaults:
14772 Roo.applyIf(meta, {
14773 totalProperty: 'total',
14774 successProperty : 'success',
14779 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14781 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14783 readerType : 'Json',
14786 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14787 * Used by Store query builder to append _requestMeta to params.
14790 metaFromRemote : false,
14792 * This method is only used by a DataProxy which has retrieved data from a remote server.
14793 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14794 * @return {Object} data A data block which is used by an Roo.data.Store object as
14795 * a cache of Roo.data.Records.
14797 read : function(response){
14798 var json = response.responseText;
14800 var o = /* eval:var:o */ eval("("+json+")");
14802 throw {message: "JsonReader.read: Json object not found"};
14808 this.metaFromRemote = true;
14809 this.meta = o.metaData;
14810 this.recordType = Roo.data.Record.create(o.metaData.fields);
14811 this.onMetaChange(this.meta, this.recordType, o);
14813 return this.readRecords(o);
14816 // private function a store will implement
14817 onMetaChange : function(meta, recordType, o){
14824 simpleAccess: function(obj, subsc) {
14831 getJsonAccessor: function(){
14833 return function(expr) {
14835 return(re.test(expr))
14836 ? new Function("obj", "return obj." + expr)
14841 return Roo.emptyFn;
14846 * Create a data block containing Roo.data.Records from an XML document.
14847 * @param {Object} o An object which contains an Array of row objects in the property specified
14848 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14849 * which contains the total size of the dataset.
14850 * @return {Object} data A data block which is used by an Roo.data.Store object as
14851 * a cache of Roo.data.Records.
14853 readRecords : function(o){
14855 * After any data loads, the raw JSON data is available for further custom processing.
14859 var s = this.meta, Record = this.recordType,
14860 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14862 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14864 if(s.totalProperty) {
14865 this.getTotal = this.getJsonAccessor(s.totalProperty);
14867 if(s.successProperty) {
14868 this.getSuccess = this.getJsonAccessor(s.successProperty);
14870 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14872 var g = this.getJsonAccessor(s.id);
14873 this.getId = function(rec) {
14875 return (r === undefined || r === "") ? null : r;
14878 this.getId = function(){return null;};
14881 for(var jj = 0; jj < fl; jj++){
14883 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14884 this.ef[jj] = this.getJsonAccessor(map);
14888 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14889 if(s.totalProperty){
14890 var vt = parseInt(this.getTotal(o), 10);
14895 if(s.successProperty){
14896 var vs = this.getSuccess(o);
14897 if(vs === false || vs === 'false'){
14902 for(var i = 0; i < c; i++){
14905 var id = this.getId(n);
14906 for(var j = 0; j < fl; j++){
14908 var v = this.ef[j](n);
14910 Roo.log('missing convert for ' + f.name);
14914 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14916 var record = new Record(values, id);
14918 records[i] = record;
14924 totalRecords : totalRecords
14927 // used when loading children.. @see loadDataFromChildren
14928 toLoadData: function(rec)
14930 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14931 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14932 return { data : data, total : data.length };
14937 * Ext JS Library 1.1.1
14938 * Copyright(c) 2006-2007, Ext JS, LLC.
14940 * Originally Released Under LGPL - original licence link has changed is not relivant.
14943 * <script type="text/javascript">
14947 * @class Roo.data.ArrayReader
14948 * @extends Roo.data.DataReader
14949 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14950 * Each element of that Array represents a row of data fields. The
14951 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14952 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14956 var RecordDef = Roo.data.Record.create([
14957 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14958 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14960 var myReader = new Roo.data.ArrayReader({
14961 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14965 * This would consume an Array like this:
14967 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14971 * Create a new JsonReader
14972 * @param {Object} meta Metadata configuration options.
14973 * @param {Object|Array} recordType Either an Array of field definition objects
14975 * @cfg {Array} fields Array of field definition objects
14976 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14977 * as specified to {@link Roo.data.Record#create},
14978 * or an {@link Roo.data.Record} object
14981 * created using {@link Roo.data.Record#create}.
14983 Roo.data.ArrayReader = function(meta, recordType)
14985 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14988 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14991 * Create a data block containing Roo.data.Records from an XML document.
14992 * @param {Object} o An Array of row objects which represents the dataset.
14993 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14994 * a cache of Roo.data.Records.
14996 readRecords : function(o)
14998 var sid = this.meta ? this.meta.id : null;
14999 var recordType = this.recordType, fields = recordType.prototype.fields;
15002 for(var i = 0; i < root.length; i++){
15005 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15006 for(var j = 0, jlen = fields.length; j < jlen; j++){
15007 var f = fields.items[j];
15008 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15009 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15011 values[f.name] = v;
15013 var record = new recordType(values, id);
15015 records[records.length] = record;
15019 totalRecords : records.length
15022 // used when loading children.. @see loadDataFromChildren
15023 toLoadData: function(rec)
15025 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15026 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15037 * @class Roo.bootstrap.ComboBox
15038 * @extends Roo.bootstrap.TriggerField
15039 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15040 * @cfg {Boolean} append (true|false) default false
15041 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15042 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15043 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15044 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15045 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15046 * @cfg {Boolean} animate default true
15047 * @cfg {Boolean} emptyResultText only for touch device
15048 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15049 * @cfg {String} emptyTitle default ''
15050 * @cfg {Number} width fixed with? experimental
15052 * Create a new ComboBox.
15053 * @param {Object} config Configuration options
15055 Roo.bootstrap.ComboBox = function(config){
15056 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15060 * Fires when the dropdown list is expanded
15061 * @param {Roo.bootstrap.ComboBox} combo This combo box
15066 * Fires when the dropdown list is collapsed
15067 * @param {Roo.bootstrap.ComboBox} combo This combo box
15071 * @event beforeselect
15072 * Fires before a list item is selected. Return false to cancel the selection.
15073 * @param {Roo.bootstrap.ComboBox} combo This combo box
15074 * @param {Roo.data.Record} record The data record returned from the underlying store
15075 * @param {Number} index The index of the selected item in the dropdown list
15077 'beforeselect' : true,
15080 * Fires when a list item is selected
15081 * @param {Roo.bootstrap.ComboBox} combo This combo box
15082 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15083 * @param {Number} index The index of the selected item in the dropdown list
15087 * @event beforequery
15088 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15089 * The event object passed has these properties:
15090 * @param {Roo.bootstrap.ComboBox} combo This combo box
15091 * @param {String} query The query
15092 * @param {Boolean} forceAll true to force "all" query
15093 * @param {Boolean} cancel true to cancel the query
15094 * @param {Object} e The query event object
15096 'beforequery': true,
15099 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15100 * @param {Roo.bootstrap.ComboBox} combo This combo box
15105 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15106 * @param {Roo.bootstrap.ComboBox} combo This combo box
15107 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15112 * Fires when the remove value from the combobox array
15113 * @param {Roo.bootstrap.ComboBox} combo This combo box
15117 * @event afterremove
15118 * Fires when the remove value from the combobox array
15119 * @param {Roo.bootstrap.ComboBox} combo This combo box
15121 'afterremove' : true,
15123 * @event specialfilter
15124 * Fires when specialfilter
15125 * @param {Roo.bootstrap.ComboBox} combo This combo box
15127 'specialfilter' : true,
15130 * Fires when tick the element
15131 * @param {Roo.bootstrap.ComboBox} combo This combo box
15135 * @event touchviewdisplay
15136 * Fires when touch view require special display (default is using displayField)
15137 * @param {Roo.bootstrap.ComboBox} combo This combo box
15138 * @param {Object} cfg set html .
15140 'touchviewdisplay' : true
15145 this.tickItems = [];
15147 this.selectedIndex = -1;
15148 if(this.mode == 'local'){
15149 if(config.queryDelay === undefined){
15150 this.queryDelay = 10;
15152 if(config.minChars === undefined){
15158 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15161 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15162 * rendering into an Roo.Editor, defaults to false)
15165 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15166 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15169 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15172 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15173 * the dropdown list (defaults to undefined, with no header element)
15177 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15181 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15183 listWidth: undefined,
15185 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15186 * mode = 'remote' or 'text' if mode = 'local')
15188 displayField: undefined,
15191 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15192 * mode = 'remote' or 'value' if mode = 'local').
15193 * Note: use of a valueField requires the user make a selection
15194 * in order for a value to be mapped.
15196 valueField: undefined,
15198 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15203 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15204 * field's data value (defaults to the underlying DOM element's name)
15206 hiddenName: undefined,
15208 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15212 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15214 selectedClass: 'active',
15217 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15221 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15222 * anchor positions (defaults to 'tl-bl')
15224 listAlign: 'tl-bl?',
15226 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15230 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15231 * query specified by the allQuery config option (defaults to 'query')
15233 triggerAction: 'query',
15235 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15236 * (defaults to 4, does not apply if editable = false)
15240 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15241 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15245 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15246 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15250 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15251 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15255 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15256 * when editable = true (defaults to false)
15258 selectOnFocus:false,
15260 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15262 queryParam: 'query',
15264 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15265 * when mode = 'remote' (defaults to 'Loading...')
15267 loadingText: 'Loading...',
15269 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15273 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15277 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15278 * traditional select (defaults to true)
15282 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15286 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15290 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15291 * listWidth has a higher value)
15295 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15296 * allow the user to set arbitrary text into the field (defaults to false)
15298 forceSelection:false,
15300 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15301 * if typeAhead = true (defaults to 250)
15303 typeAheadDelay : 250,
15305 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15306 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15308 valueNotFoundText : undefined,
15310 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15312 blockFocus : false,
15315 * @cfg {Boolean} disableClear Disable showing of clear button.
15317 disableClear : false,
15319 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15321 alwaysQuery : false,
15324 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15329 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15331 invalidClass : "has-warning",
15334 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15336 validClass : "has-success",
15339 * @cfg {Boolean} specialFilter (true|false) special filter default false
15341 specialFilter : false,
15344 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15346 mobileTouchView : true,
15349 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15351 useNativeIOS : false,
15354 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15356 mobile_restrict_height : false,
15358 ios_options : false,
15370 btnPosition : 'right',
15371 triggerList : true,
15372 showToggleBtn : true,
15374 emptyResultText: 'Empty',
15375 triggerText : 'Select',
15379 // element that contains real text value.. (when hidden is used..)
15381 getAutoCreate : function()
15386 * Render classic select for iso
15389 if(Roo.isIOS && this.useNativeIOS){
15390 cfg = this.getAutoCreateNativeIOS();
15398 if(Roo.isTouch && this.mobileTouchView){
15399 cfg = this.getAutoCreateTouchView();
15406 if(!this.tickable){
15407 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15412 * ComboBox with tickable selections
15415 var align = this.labelAlign || this.parentLabelAlign();
15418 cls : 'form-group roo-combobox-tickable' //input-group
15421 var btn_text_select = '';
15422 var btn_text_done = '';
15423 var btn_text_cancel = '';
15425 if (this.btn_text_show) {
15426 btn_text_select = 'Select';
15427 btn_text_done = 'Done';
15428 btn_text_cancel = 'Cancel';
15433 cls : 'tickable-buttons',
15438 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15439 //html : this.triggerText
15440 html: btn_text_select
15446 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15448 html: btn_text_done
15454 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15456 html: btn_text_cancel
15462 buttons.cn.unshift({
15464 cls: 'roo-select2-search-field-input'
15470 Roo.each(buttons.cn, function(c){
15472 c.cls += ' btn-' + _this.size;
15475 if (_this.disabled) {
15482 style : 'display: contents',
15487 cls: 'form-hidden-field'
15491 cls: 'roo-select2-choices',
15495 cls: 'roo-select2-search-field',
15506 cls: 'roo-select2-container input-group roo-select2-container-multi',
15512 // cls: 'typeahead typeahead-long dropdown-menu',
15513 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15518 if(this.hasFeedback && !this.allowBlank){
15522 cls: 'glyphicon form-control-feedback'
15525 combobox.cn.push(feedback);
15532 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15533 tooltip : 'This field is required'
15535 if (Roo.bootstrap.version == 4) {
15538 style : 'display:none'
15541 if (align ==='left' && this.fieldLabel.length) {
15543 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15550 cls : 'control-label col-form-label',
15551 html : this.fieldLabel
15563 var labelCfg = cfg.cn[1];
15564 var contentCfg = cfg.cn[2];
15567 if(this.indicatorpos == 'right'){
15573 cls : 'control-label col-form-label',
15577 html : this.fieldLabel
15593 labelCfg = cfg.cn[0];
15594 contentCfg = cfg.cn[1];
15598 if(this.labelWidth > 12){
15599 labelCfg.style = "width: " + this.labelWidth + 'px';
15601 if(this.width * 1 > 0){
15602 contentCfg.style = "width: " + this.width + 'px';
15604 if(this.labelWidth < 13 && this.labelmd == 0){
15605 this.labelmd = this.labelWidth;
15608 if(this.labellg > 0){
15609 labelCfg.cls += ' col-lg-' + this.labellg;
15610 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15613 if(this.labelmd > 0){
15614 labelCfg.cls += ' col-md-' + this.labelmd;
15615 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15618 if(this.labelsm > 0){
15619 labelCfg.cls += ' col-sm-' + this.labelsm;
15620 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15623 if(this.labelxs > 0){
15624 labelCfg.cls += ' col-xs-' + this.labelxs;
15625 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15629 } else if ( this.fieldLabel.length) {
15630 // Roo.log(" label");
15635 //cls : 'input-group-addon',
15636 html : this.fieldLabel
15641 if(this.indicatorpos == 'right'){
15645 //cls : 'input-group-addon',
15646 html : this.fieldLabel
15656 // Roo.log(" no label && no align");
15663 ['xs','sm','md','lg'].map(function(size){
15664 if (settings[size]) {
15665 cfg.cls += ' col-' + size + '-' + settings[size];
15673 _initEventsCalled : false,
15676 initEvents: function()
15678 if (this._initEventsCalled) { // as we call render... prevent looping...
15681 this._initEventsCalled = true;
15684 throw "can not find store for combo";
15687 this.indicator = this.indicatorEl();
15689 this.store = Roo.factory(this.store, Roo.data);
15690 this.store.parent = this;
15692 // if we are building from html. then this element is so complex, that we can not really
15693 // use the rendered HTML.
15694 // so we have to trash and replace the previous code.
15695 if (Roo.XComponent.build_from_html) {
15696 // remove this element....
15697 var e = this.el.dom, k=0;
15698 while (e ) { e = e.previousSibling; ++k;}
15703 this.rendered = false;
15705 this.render(this.parent().getChildContainer(true), k);
15708 if(Roo.isIOS && this.useNativeIOS){
15709 this.initIOSView();
15717 if(Roo.isTouch && this.mobileTouchView){
15718 this.initTouchView();
15723 this.initTickableEvents();
15727 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15729 if(this.hiddenName){
15731 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15733 this.hiddenField.dom.value =
15734 this.hiddenValue !== undefined ? this.hiddenValue :
15735 this.value !== undefined ? this.value : '';
15737 // prevent input submission
15738 this.el.dom.removeAttribute('name');
15739 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15744 // this.el.dom.setAttribute('autocomplete', 'off');
15747 var cls = 'x-combo-list';
15749 //this.list = new Roo.Layer({
15750 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15756 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15757 _this.list.setWidth(lw);
15760 this.list.on('mouseover', this.onViewOver, this);
15761 this.list.on('mousemove', this.onViewMove, this);
15762 this.list.on('scroll', this.onViewScroll, this);
15765 this.list.swallowEvent('mousewheel');
15766 this.assetHeight = 0;
15769 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15770 this.assetHeight += this.header.getHeight();
15773 this.innerList = this.list.createChild({cls:cls+'-inner'});
15774 this.innerList.on('mouseover', this.onViewOver, this);
15775 this.innerList.on('mousemove', this.onViewMove, this);
15776 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15778 if(this.allowBlank && !this.pageSize && !this.disableClear){
15779 this.footer = this.list.createChild({cls:cls+'-ft'});
15780 this.pageTb = new Roo.Toolbar(this.footer);
15784 this.footer = this.list.createChild({cls:cls+'-ft'});
15785 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15786 {pageSize: this.pageSize});
15790 if (this.pageTb && this.allowBlank && !this.disableClear) {
15792 this.pageTb.add(new Roo.Toolbar.Fill(), {
15793 cls: 'x-btn-icon x-btn-clear',
15795 handler: function()
15798 _this.clearValue();
15799 _this.onSelect(false, -1);
15804 this.assetHeight += this.footer.getHeight();
15809 this.tpl = Roo.bootstrap.version == 4 ?
15810 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15811 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15814 this.view = new Roo.View(this.list, this.tpl, {
15815 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15817 //this.view.wrapEl.setDisplayed(false);
15818 this.view.on('click', this.onViewClick, this);
15821 this.store.on('beforeload', this.onBeforeLoad, this);
15822 this.store.on('load', this.onLoad, this);
15823 this.store.on('loadexception', this.onLoadException, this);
15825 if(this.resizable){
15826 this.resizer = new Roo.Resizable(this.list, {
15827 pinned:true, handles:'se'
15829 this.resizer.on('resize', function(r, w, h){
15830 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15831 this.listWidth = w;
15832 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15833 this.restrictHeight();
15835 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15838 if(!this.editable){
15839 this.editable = true;
15840 this.setEditable(false);
15845 if (typeof(this.events.add.listeners) != 'undefined') {
15847 this.addicon = this.wrap.createChild(
15848 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15850 this.addicon.on('click', function(e) {
15851 this.fireEvent('add', this);
15854 if (typeof(this.events.edit.listeners) != 'undefined') {
15856 this.editicon = this.wrap.createChild(
15857 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15858 if (this.addicon) {
15859 this.editicon.setStyle('margin-left', '40px');
15861 this.editicon.on('click', function(e) {
15863 // we fire even if inothing is selected..
15864 this.fireEvent('edit', this, this.lastData );
15870 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15871 "up" : function(e){
15872 this.inKeyMode = true;
15876 "down" : function(e){
15877 if(!this.isExpanded()){
15878 this.onTriggerClick();
15880 this.inKeyMode = true;
15885 "enter" : function(e){
15886 // this.onViewClick();
15890 if(this.fireEvent("specialkey", this, e)){
15891 this.onViewClick(false);
15897 "esc" : function(e){
15901 "tab" : function(e){
15904 if(this.fireEvent("specialkey", this, e)){
15905 this.onViewClick(false);
15913 doRelay : function(foo, bar, hname){
15914 if(hname == 'down' || this.scope.isExpanded()){
15915 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15924 this.queryDelay = Math.max(this.queryDelay || 10,
15925 this.mode == 'local' ? 10 : 250);
15928 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15930 if(this.typeAhead){
15931 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15933 if(this.editable !== false){
15934 this.inputEl().on("keyup", this.onKeyUp, this);
15936 if(this.forceSelection){
15937 this.inputEl().on('blur', this.doForce, this);
15941 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15942 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15946 initTickableEvents: function()
15950 if(this.hiddenName){
15952 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15954 this.hiddenField.dom.value =
15955 this.hiddenValue !== undefined ? this.hiddenValue :
15956 this.value !== undefined ? this.value : '';
15958 // prevent input submission
15959 this.el.dom.removeAttribute('name');
15960 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15965 // this.list = this.el.select('ul.dropdown-menu',true).first();
15967 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15968 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15969 if(this.triggerList){
15970 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15973 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15974 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15976 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15977 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15979 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15980 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15982 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15983 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15984 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15987 this.cancelBtn.hide();
15992 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15993 _this.list.setWidth(lw);
15996 this.list.on('mouseover', this.onViewOver, this);
15997 this.list.on('mousemove', this.onViewMove, this);
15999 this.list.on('scroll', this.onViewScroll, this);
16002 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16003 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16006 this.view = new Roo.View(this.list, this.tpl, {
16011 selectedClass: this.selectedClass
16014 //this.view.wrapEl.setDisplayed(false);
16015 this.view.on('click', this.onViewClick, this);
16019 this.store.on('beforeload', this.onBeforeLoad, this);
16020 this.store.on('load', this.onLoad, this);
16021 this.store.on('loadexception', this.onLoadException, this);
16024 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16025 "up" : function(e){
16026 this.inKeyMode = true;
16030 "down" : function(e){
16031 this.inKeyMode = true;
16035 "enter" : function(e){
16036 if(this.fireEvent("specialkey", this, e)){
16037 this.onViewClick(false);
16043 "esc" : function(e){
16044 this.onTickableFooterButtonClick(e, false, false);
16047 "tab" : function(e){
16048 this.fireEvent("specialkey", this, e);
16050 this.onTickableFooterButtonClick(e, false, false);
16057 doRelay : function(e, fn, key){
16058 if(this.scope.isExpanded()){
16059 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16068 this.queryDelay = Math.max(this.queryDelay || 10,
16069 this.mode == 'local' ? 10 : 250);
16072 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16074 if(this.typeAhead){
16075 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16078 if(this.editable !== false){
16079 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16082 this.indicator = this.indicatorEl();
16084 if(this.indicator){
16085 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16086 this.indicator.hide();
16091 onDestroy : function(){
16093 this.view.setStore(null);
16094 this.view.el.removeAllListeners();
16095 this.view.el.remove();
16096 this.view.purgeListeners();
16099 this.list.dom.innerHTML = '';
16103 this.store.un('beforeload', this.onBeforeLoad, this);
16104 this.store.un('load', this.onLoad, this);
16105 this.store.un('loadexception', this.onLoadException, this);
16107 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16111 fireKey : function(e){
16112 if(e.isNavKeyPress() && !this.list.isVisible()){
16113 this.fireEvent("specialkey", this, e);
16118 onResize: function(w, h)
16122 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16124 // if(typeof w != 'number'){
16125 // // we do not handle it!?!?
16128 // var tw = this.trigger.getWidth();
16129 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16130 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16132 // this.inputEl().setWidth( this.adjustWidth('input', x));
16134 // //this.trigger.setStyle('left', x+'px');
16136 // if(this.list && this.listWidth === undefined){
16137 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16138 // this.list.setWidth(lw);
16139 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16147 * Allow or prevent the user from directly editing the field text. If false is passed,
16148 * the user will only be able to select from the items defined in the dropdown list. This method
16149 * is the runtime equivalent of setting the 'editable' config option at config time.
16150 * @param {Boolean} value True to allow the user to directly edit the field text
16152 setEditable : function(value){
16153 if(value == this.editable){
16156 this.editable = value;
16158 this.inputEl().dom.setAttribute('readOnly', true);
16159 this.inputEl().on('mousedown', this.onTriggerClick, this);
16160 this.inputEl().addClass('x-combo-noedit');
16162 this.inputEl().dom.setAttribute('readOnly', false);
16163 this.inputEl().un('mousedown', this.onTriggerClick, this);
16164 this.inputEl().removeClass('x-combo-noedit');
16170 onBeforeLoad : function(combo,opts){
16171 if(!this.hasFocus){
16175 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16177 this.restrictHeight();
16178 this.selectedIndex = -1;
16182 onLoad : function(){
16184 this.hasQuery = false;
16186 if(!this.hasFocus){
16190 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16191 this.loading.hide();
16194 if(this.store.getCount() > 0){
16197 this.restrictHeight();
16198 if(this.lastQuery == this.allQuery){
16199 if(this.editable && !this.tickable){
16200 this.inputEl().dom.select();
16204 !this.selectByValue(this.value, true) &&
16207 !this.store.lastOptions ||
16208 typeof(this.store.lastOptions.add) == 'undefined' ||
16209 this.store.lastOptions.add != true
16212 this.select(0, true);
16215 if(this.autoFocus){
16218 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16219 this.taTask.delay(this.typeAheadDelay);
16223 this.onEmptyResults();
16229 onLoadException : function()
16231 this.hasQuery = false;
16233 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16234 this.loading.hide();
16237 if(this.tickable && this.editable){
16242 // only causes errors at present
16243 //Roo.log(this.store.reader.jsonData);
16244 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16246 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16252 onTypeAhead : function(){
16253 if(this.store.getCount() > 0){
16254 var r = this.store.getAt(0);
16255 var newValue = r.data[this.displayField];
16256 var len = newValue.length;
16257 var selStart = this.getRawValue().length;
16259 if(selStart != len){
16260 this.setRawValue(newValue);
16261 this.selectText(selStart, newValue.length);
16267 onSelect : function(record, index){
16269 if(this.fireEvent('beforeselect', this, record, index) !== false){
16271 this.setFromData(index > -1 ? record.data : false);
16274 this.fireEvent('select', this, record, index);
16279 * Returns the currently selected field value or empty string if no value is set.
16280 * @return {String} value The selected value
16282 getValue : function()
16284 if(Roo.isIOS && this.useNativeIOS){
16285 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16289 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16292 if(this.valueField){
16293 return typeof this.value != 'undefined' ? this.value : '';
16295 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16299 getRawValue : function()
16301 if(Roo.isIOS && this.useNativeIOS){
16302 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16305 var v = this.inputEl().getValue();
16311 * Clears any text/value currently set in the field
16313 clearValue : function(){
16315 if(this.hiddenField){
16316 this.hiddenField.dom.value = '';
16319 this.setRawValue('');
16320 this.lastSelectionText = '';
16321 this.lastData = false;
16323 var close = this.closeTriggerEl();
16334 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16335 * will be displayed in the field. If the value does not match the data value of an existing item,
16336 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16337 * Otherwise the field will be blank (although the value will still be set).
16338 * @param {String} value The value to match
16340 setValue : function(v)
16342 if(Roo.isIOS && this.useNativeIOS){
16343 this.setIOSValue(v);
16353 if(this.valueField){
16354 var r = this.findRecord(this.valueField, v);
16356 text = r.data[this.displayField];
16357 }else if(this.valueNotFoundText !== undefined){
16358 text = this.valueNotFoundText;
16361 this.lastSelectionText = text;
16362 if(this.hiddenField){
16363 this.hiddenField.dom.value = v;
16365 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16368 var close = this.closeTriggerEl();
16371 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16377 * @property {Object} the last set data for the element
16382 * Sets the value of the field based on a object which is related to the record format for the store.
16383 * @param {Object} value the value to set as. or false on reset?
16385 setFromData : function(o){
16392 var dv = ''; // display value
16393 var vv = ''; // value value..
16395 if (this.displayField) {
16396 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16398 // this is an error condition!!!
16399 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16402 if(this.valueField){
16403 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16406 var close = this.closeTriggerEl();
16409 if(dv.length || vv * 1 > 0){
16411 this.blockFocus=true;
16417 if(this.hiddenField){
16418 this.hiddenField.dom.value = vv;
16420 this.lastSelectionText = dv;
16421 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16425 // no hidden field.. - we store the value in 'value', but still display
16426 // display field!!!!
16427 this.lastSelectionText = dv;
16428 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16435 reset : function(){
16436 // overridden so that last data is reset..
16443 this.setValue(this.originalValue);
16444 //this.clearInvalid();
16445 this.lastData = false;
16447 this.view.clearSelections();
16453 findRecord : function(prop, value){
16455 if(this.store.getCount() > 0){
16456 this.store.each(function(r){
16457 if(r.data[prop] == value){
16467 getName: function()
16469 // returns hidden if it's set..
16470 if (!this.rendered) {return ''};
16471 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16475 onViewMove : function(e, t){
16476 this.inKeyMode = false;
16480 onViewOver : function(e, t){
16481 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16484 var item = this.view.findItemFromChild(t);
16487 var index = this.view.indexOf(item);
16488 this.select(index, false);
16493 onViewClick : function(view, doFocus, el, e)
16495 var index = this.view.getSelectedIndexes()[0];
16497 var r = this.store.getAt(index);
16501 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16508 Roo.each(this.tickItems, function(v,k){
16510 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16512 _this.tickItems.splice(k, 1);
16514 if(typeof(e) == 'undefined' && view == false){
16515 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16527 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16528 this.tickItems.push(r.data);
16531 if(typeof(e) == 'undefined' && view == false){
16532 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16539 this.onSelect(r, index);
16541 if(doFocus !== false && !this.blockFocus){
16542 this.inputEl().focus();
16547 restrictHeight : function(){
16548 //this.innerList.dom.style.height = '';
16549 //var inner = this.innerList.dom;
16550 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16551 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16552 //this.list.beginUpdate();
16553 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16554 this.list.alignTo(this.inputEl(), this.listAlign);
16555 this.list.alignTo(this.inputEl(), this.listAlign);
16556 //this.list.endUpdate();
16560 onEmptyResults : function(){
16562 if(this.tickable && this.editable){
16563 this.hasFocus = false;
16564 this.restrictHeight();
16572 * Returns true if the dropdown list is expanded, else false.
16574 isExpanded : function(){
16575 return this.list.isVisible();
16579 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16580 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16581 * @param {String} value The data value of the item to select
16582 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16583 * selected item if it is not currently in view (defaults to true)
16584 * @return {Boolean} True if the value matched an item in the list, else false
16586 selectByValue : function(v, scrollIntoView){
16587 if(v !== undefined && v !== null){
16588 var r = this.findRecord(this.valueField || this.displayField, v);
16590 this.select(this.store.indexOf(r), scrollIntoView);
16598 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16599 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16600 * @param {Number} index The zero-based index of the list item to select
16601 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16602 * selected item if it is not currently in view (defaults to true)
16604 select : function(index, scrollIntoView){
16605 this.selectedIndex = index;
16606 this.view.select(index);
16607 if(scrollIntoView !== false){
16608 var el = this.view.getNode(index);
16610 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16613 this.list.scrollChildIntoView(el, false);
16619 selectNext : function(){
16620 var ct = this.store.getCount();
16622 if(this.selectedIndex == -1){
16624 }else if(this.selectedIndex < ct-1){
16625 this.select(this.selectedIndex+1);
16631 selectPrev : function(){
16632 var ct = this.store.getCount();
16634 if(this.selectedIndex == -1){
16636 }else if(this.selectedIndex != 0){
16637 this.select(this.selectedIndex-1);
16643 onKeyUp : function(e){
16644 if(this.editable !== false && !e.isSpecialKey()){
16645 this.lastKey = e.getKey();
16646 this.dqTask.delay(this.queryDelay);
16651 validateBlur : function(){
16652 return !this.list || !this.list.isVisible();
16656 initQuery : function(){
16658 var v = this.getRawValue();
16660 if(this.tickable && this.editable){
16661 v = this.tickableInputEl().getValue();
16668 doForce : function(){
16669 if(this.inputEl().dom.value.length > 0){
16670 this.inputEl().dom.value =
16671 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16677 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16678 * query allowing the query action to be canceled if needed.
16679 * @param {String} query The SQL query to execute
16680 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16681 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16682 * saved in the current store (defaults to false)
16684 doQuery : function(q, forceAll){
16686 if(q === undefined || q === null){
16691 forceAll: forceAll,
16695 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16700 forceAll = qe.forceAll;
16701 if(forceAll === true || (q.length >= this.minChars)){
16703 this.hasQuery = true;
16705 if(this.lastQuery != q || this.alwaysQuery){
16706 this.lastQuery = q;
16707 if(this.mode == 'local'){
16708 this.selectedIndex = -1;
16710 this.store.clearFilter();
16713 if(this.specialFilter){
16714 this.fireEvent('specialfilter', this);
16719 this.store.filter(this.displayField, q);
16722 this.store.fireEvent("datachanged", this.store);
16729 this.store.baseParams[this.queryParam] = q;
16731 var options = {params : this.getParams(q)};
16734 options.add = true;
16735 options.params.start = this.page * this.pageSize;
16738 this.store.load(options);
16741 * this code will make the page width larger, at the beginning, the list not align correctly,
16742 * we should expand the list on onLoad
16743 * so command out it
16748 this.selectedIndex = -1;
16753 this.loadNext = false;
16757 getParams : function(q){
16759 //p[this.queryParam] = q;
16763 p.limit = this.pageSize;
16769 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16771 collapse : function(){
16772 if(!this.isExpanded()){
16778 this.hasFocus = false;
16782 this.cancelBtn.hide();
16783 this.trigger.show();
16786 this.tickableInputEl().dom.value = '';
16787 this.tickableInputEl().blur();
16792 Roo.get(document).un('mousedown', this.collapseIf, this);
16793 Roo.get(document).un('mousewheel', this.collapseIf, this);
16794 if (!this.editable) {
16795 Roo.get(document).un('keydown', this.listKeyPress, this);
16797 this.fireEvent('collapse', this);
16803 collapseIf : function(e){
16804 var in_combo = e.within(this.el);
16805 var in_list = e.within(this.list);
16806 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16808 if (in_combo || in_list || is_list) {
16809 //e.stopPropagation();
16814 this.onTickableFooterButtonClick(e, false, false);
16822 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16824 expand : function(){
16826 if(this.isExpanded() || !this.hasFocus){
16830 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16831 this.list.setWidth(lw);
16837 this.restrictHeight();
16841 this.tickItems = Roo.apply([], this.item);
16844 this.cancelBtn.show();
16845 this.trigger.hide();
16848 this.tickableInputEl().focus();
16853 Roo.get(document).on('mousedown', this.collapseIf, this);
16854 Roo.get(document).on('mousewheel', this.collapseIf, this);
16855 if (!this.editable) {
16856 Roo.get(document).on('keydown', this.listKeyPress, this);
16859 this.fireEvent('expand', this);
16863 // Implements the default empty TriggerField.onTriggerClick function
16864 onTriggerClick : function(e)
16866 Roo.log('trigger click');
16868 if(this.disabled || !this.triggerList){
16873 this.loadNext = false;
16875 if(this.isExpanded()){
16877 if (!this.blockFocus) {
16878 this.inputEl().focus();
16882 this.hasFocus = true;
16883 if(this.triggerAction == 'all') {
16884 this.doQuery(this.allQuery, true);
16886 this.doQuery(this.getRawValue());
16888 if (!this.blockFocus) {
16889 this.inputEl().focus();
16894 onTickableTriggerClick : function(e)
16901 this.loadNext = false;
16902 this.hasFocus = true;
16904 if(this.triggerAction == 'all') {
16905 this.doQuery(this.allQuery, true);
16907 this.doQuery(this.getRawValue());
16911 onSearchFieldClick : function(e)
16913 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16914 this.onTickableFooterButtonClick(e, false, false);
16918 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16923 this.loadNext = false;
16924 this.hasFocus = true;
16926 if(this.triggerAction == 'all') {
16927 this.doQuery(this.allQuery, true);
16929 this.doQuery(this.getRawValue());
16933 listKeyPress : function(e)
16935 //Roo.log('listkeypress');
16936 // scroll to first matching element based on key pres..
16937 if (e.isSpecialKey()) {
16940 var k = String.fromCharCode(e.getKey()).toUpperCase();
16943 var csel = this.view.getSelectedNodes();
16944 var cselitem = false;
16946 var ix = this.view.indexOf(csel[0]);
16947 cselitem = this.store.getAt(ix);
16948 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16954 this.store.each(function(v) {
16956 // start at existing selection.
16957 if (cselitem.id == v.id) {
16963 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16964 match = this.store.indexOf(v);
16970 if (match === false) {
16971 return true; // no more action?
16974 this.view.select(match);
16975 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16976 sn.scrollIntoView(sn.dom.parentNode, false);
16979 onViewScroll : function(e, t){
16981 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){
16985 this.hasQuery = true;
16987 this.loading = this.list.select('.loading', true).first();
16989 if(this.loading === null){
16990 this.list.createChild({
16992 cls: 'loading roo-select2-more-results roo-select2-active',
16993 html: 'Loading more results...'
16996 this.loading = this.list.select('.loading', true).first();
16998 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17000 this.loading.hide();
17003 this.loading.show();
17008 this.loadNext = true;
17010 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17015 addItem : function(o)
17017 var dv = ''; // display value
17019 if (this.displayField) {
17020 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17022 // this is an error condition!!!
17023 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17030 var choice = this.choices.createChild({
17032 cls: 'roo-select2-search-choice',
17041 cls: 'roo-select2-search-choice-close fa fa-times',
17046 }, this.searchField);
17048 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17050 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17058 this.inputEl().dom.value = '';
17063 onRemoveItem : function(e, _self, o)
17065 e.preventDefault();
17067 this.lastItem = Roo.apply([], this.item);
17069 var index = this.item.indexOf(o.data) * 1;
17072 Roo.log('not this item?!');
17076 this.item.splice(index, 1);
17081 this.fireEvent('remove', this, e);
17087 syncValue : function()
17089 if(!this.item.length){
17096 Roo.each(this.item, function(i){
17097 if(_this.valueField){
17098 value.push(i[_this.valueField]);
17105 this.value = value.join(',');
17107 if(this.hiddenField){
17108 this.hiddenField.dom.value = this.value;
17111 this.store.fireEvent("datachanged", this.store);
17116 clearItem : function()
17118 if(!this.multiple){
17124 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17132 if(this.tickable && !Roo.isTouch){
17133 this.view.refresh();
17137 inputEl: function ()
17139 if(Roo.isIOS && this.useNativeIOS){
17140 return this.el.select('select.roo-ios-select', true).first();
17143 if(Roo.isTouch && this.mobileTouchView){
17144 return this.el.select('input.form-control',true).first();
17148 return this.searchField;
17151 return this.el.select('input.form-control',true).first();
17154 onTickableFooterButtonClick : function(e, btn, el)
17156 e.preventDefault();
17158 this.lastItem = Roo.apply([], this.item);
17160 if(btn && btn.name == 'cancel'){
17161 this.tickItems = Roo.apply([], this.item);
17170 Roo.each(this.tickItems, function(o){
17178 validate : function()
17180 if(this.getVisibilityEl().hasClass('hidden')){
17184 var v = this.getRawValue();
17187 v = this.getValue();
17190 if(this.disabled || this.allowBlank || v.length){
17195 this.markInvalid();
17199 tickableInputEl : function()
17201 if(!this.tickable || !this.editable){
17202 return this.inputEl();
17205 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17209 getAutoCreateTouchView : function()
17214 cls: 'form-group' //input-group
17220 type : this.inputType,
17221 cls : 'form-control x-combo-noedit',
17222 autocomplete: 'new-password',
17223 placeholder : this.placeholder || '',
17228 input.name = this.name;
17232 input.cls += ' input-' + this.size;
17235 if (this.disabled) {
17236 input.disabled = true;
17240 cls : 'roo-combobox-wrap',
17247 inputblock.cls += ' input-group';
17249 inputblock.cn.unshift({
17251 cls : 'input-group-addon input-group-prepend input-group-text',
17256 if(this.removable && !this.multiple){
17257 inputblock.cls += ' roo-removable';
17259 inputblock.cn.push({
17262 cls : 'roo-combo-removable-btn close'
17266 if(this.hasFeedback && !this.allowBlank){
17268 inputblock.cls += ' has-feedback';
17270 inputblock.cn.push({
17272 cls: 'glyphicon form-control-feedback'
17279 inputblock.cls += (this.before) ? '' : ' input-group';
17281 inputblock.cn.push({
17283 cls : 'input-group-addon input-group-append input-group-text',
17289 var ibwrap = inputblock;
17294 cls: 'roo-select2-choices',
17298 cls: 'roo-select2-search-field',
17311 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17316 cls: 'form-hidden-field'
17322 if(!this.multiple && this.showToggleBtn){
17328 if (this.caret != false) {
17331 cls: 'fa fa-' + this.caret
17338 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17340 Roo.bootstrap.version == 3 ? caret : '',
17343 cls: 'combobox-clear',
17357 combobox.cls += ' roo-select2-container-multi';
17360 var align = this.labelAlign || this.parentLabelAlign();
17362 if (align ==='left' && this.fieldLabel.length) {
17367 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17368 tooltip : 'This field is required'
17372 cls : 'control-label col-form-label',
17373 html : this.fieldLabel
17377 cls : 'roo-combobox-wrap ',
17384 var labelCfg = cfg.cn[1];
17385 var contentCfg = cfg.cn[2];
17388 if(this.indicatorpos == 'right'){
17393 cls : 'control-label col-form-label',
17397 html : this.fieldLabel
17401 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17402 tooltip : 'This field is required'
17407 cls : "roo-combobox-wrap ",
17415 labelCfg = cfg.cn[0];
17416 contentCfg = cfg.cn[1];
17421 if(this.labelWidth > 12){
17422 labelCfg.style = "width: " + this.labelWidth + 'px';
17425 if(this.labelWidth < 13 && this.labelmd == 0){
17426 this.labelmd = this.labelWidth;
17429 if(this.labellg > 0){
17430 labelCfg.cls += ' col-lg-' + this.labellg;
17431 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17434 if(this.labelmd > 0){
17435 labelCfg.cls += ' col-md-' + this.labelmd;
17436 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17439 if(this.labelsm > 0){
17440 labelCfg.cls += ' col-sm-' + this.labelsm;
17441 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17444 if(this.labelxs > 0){
17445 labelCfg.cls += ' col-xs-' + this.labelxs;
17446 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17450 } else if ( this.fieldLabel.length) {
17454 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17455 tooltip : 'This field is required'
17459 cls : 'control-label',
17460 html : this.fieldLabel
17471 if(this.indicatorpos == 'right'){
17475 cls : 'control-label',
17476 html : this.fieldLabel,
17480 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17481 tooltip : 'This field is required'
17498 var settings = this;
17500 ['xs','sm','md','lg'].map(function(size){
17501 if (settings[size]) {
17502 cfg.cls += ' col-' + size + '-' + settings[size];
17509 initTouchView : function()
17511 this.renderTouchView();
17513 this.touchViewEl.on('scroll', function(){
17514 this.el.dom.scrollTop = 0;
17517 this.originalValue = this.getValue();
17519 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17521 this.inputEl().on("click", this.showTouchView, this);
17522 if (this.triggerEl) {
17523 this.triggerEl.on("click", this.showTouchView, this);
17527 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17528 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17530 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17532 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17533 this.store.on('load', this.onTouchViewLoad, this);
17534 this.store.on('loadexception', this.onTouchViewLoadException, this);
17536 if(this.hiddenName){
17538 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17540 this.hiddenField.dom.value =
17541 this.hiddenValue !== undefined ? this.hiddenValue :
17542 this.value !== undefined ? this.value : '';
17544 this.el.dom.removeAttribute('name');
17545 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17549 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17550 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17553 if(this.removable && !this.multiple){
17554 var close = this.closeTriggerEl();
17556 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17557 close.on('click', this.removeBtnClick, this, close);
17561 * fix the bug in Safari iOS8
17563 this.inputEl().on("focus", function(e){
17564 document.activeElement.blur();
17567 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17574 renderTouchView : function()
17576 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17577 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17579 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17580 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17582 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17583 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17584 this.touchViewBodyEl.setStyle('overflow', 'auto');
17586 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17587 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17589 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17590 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17594 showTouchView : function()
17600 this.touchViewHeaderEl.hide();
17602 if(this.modalTitle.length){
17603 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17604 this.touchViewHeaderEl.show();
17607 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17608 this.touchViewEl.show();
17610 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17612 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17613 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17615 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17617 if(this.modalTitle.length){
17618 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17621 this.touchViewBodyEl.setHeight(bodyHeight);
17625 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17627 this.touchViewEl.addClass(['in','show']);
17630 if(this._touchViewMask){
17631 Roo.get(document.body).addClass("x-body-masked");
17632 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17633 this._touchViewMask.setStyle('z-index', 10000);
17634 this._touchViewMask.addClass('show');
17637 this.doTouchViewQuery();
17641 hideTouchView : function()
17643 this.touchViewEl.removeClass(['in','show']);
17647 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17649 this.touchViewEl.setStyle('display', 'none');
17652 if(this._touchViewMask){
17653 this._touchViewMask.removeClass('show');
17654 Roo.get(document.body).removeClass("x-body-masked");
17658 setTouchViewValue : function()
17665 Roo.each(this.tickItems, function(o){
17670 this.hideTouchView();
17673 doTouchViewQuery : function()
17682 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17686 if(!this.alwaysQuery || this.mode == 'local'){
17687 this.onTouchViewLoad();
17694 onTouchViewBeforeLoad : function(combo,opts)
17700 onTouchViewLoad : function()
17702 if(this.store.getCount() < 1){
17703 this.onTouchViewEmptyResults();
17707 this.clearTouchView();
17709 var rawValue = this.getRawValue();
17711 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17713 this.tickItems = [];
17715 this.store.data.each(function(d, rowIndex){
17716 var row = this.touchViewListGroup.createChild(template);
17718 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17719 row.addClass(d.data.cls);
17722 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17725 html : d.data[this.displayField]
17728 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17729 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17732 row.removeClass('selected');
17733 if(!this.multiple && this.valueField &&
17734 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17737 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17738 row.addClass('selected');
17741 if(this.multiple && this.valueField &&
17742 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17746 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17747 this.tickItems.push(d.data);
17750 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17754 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17756 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17758 if(this.modalTitle.length){
17759 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17762 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17764 if(this.mobile_restrict_height && listHeight < bodyHeight){
17765 this.touchViewBodyEl.setHeight(listHeight);
17770 if(firstChecked && listHeight > bodyHeight){
17771 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17776 onTouchViewLoadException : function()
17778 this.hideTouchView();
17781 onTouchViewEmptyResults : function()
17783 this.clearTouchView();
17785 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17787 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17791 clearTouchView : function()
17793 this.touchViewListGroup.dom.innerHTML = '';
17796 onTouchViewClick : function(e, el, o)
17798 e.preventDefault();
17801 var rowIndex = o.rowIndex;
17803 var r = this.store.getAt(rowIndex);
17805 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17807 if(!this.multiple){
17808 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17809 c.dom.removeAttribute('checked');
17812 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17814 this.setFromData(r.data);
17816 var close = this.closeTriggerEl();
17822 this.hideTouchView();
17824 this.fireEvent('select', this, r, rowIndex);
17829 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17830 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17831 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17835 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17836 this.addItem(r.data);
17837 this.tickItems.push(r.data);
17841 getAutoCreateNativeIOS : function()
17844 cls: 'form-group' //input-group,
17849 cls : 'roo-ios-select'
17853 combobox.name = this.name;
17856 if (this.disabled) {
17857 combobox.disabled = true;
17860 var settings = this;
17862 ['xs','sm','md','lg'].map(function(size){
17863 if (settings[size]) {
17864 cfg.cls += ' col-' + size + '-' + settings[size];
17874 initIOSView : function()
17876 this.store.on('load', this.onIOSViewLoad, this);
17881 onIOSViewLoad : function()
17883 if(this.store.getCount() < 1){
17887 this.clearIOSView();
17889 if(this.allowBlank) {
17891 var default_text = '-- SELECT --';
17893 if(this.placeholder.length){
17894 default_text = this.placeholder;
17897 if(this.emptyTitle.length){
17898 default_text += ' - ' + this.emptyTitle + ' -';
17901 var opt = this.inputEl().createChild({
17904 html : default_text
17908 o[this.valueField] = 0;
17909 o[this.displayField] = default_text;
17911 this.ios_options.push({
17918 this.store.data.each(function(d, rowIndex){
17922 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17923 html = d.data[this.displayField];
17928 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17929 value = d.data[this.valueField];
17938 if(this.value == d.data[this.valueField]){
17939 option['selected'] = true;
17942 var opt = this.inputEl().createChild(option);
17944 this.ios_options.push({
17951 this.inputEl().on('change', function(){
17952 this.fireEvent('select', this);
17957 clearIOSView: function()
17959 this.inputEl().dom.innerHTML = '';
17961 this.ios_options = [];
17964 setIOSValue: function(v)
17968 if(!this.ios_options){
17972 Roo.each(this.ios_options, function(opts){
17974 opts.el.dom.removeAttribute('selected');
17976 if(opts.data[this.valueField] != v){
17980 opts.el.dom.setAttribute('selected', true);
17986 * @cfg {Boolean} grow
17990 * @cfg {Number} growMin
17994 * @cfg {Number} growMax
18003 Roo.apply(Roo.bootstrap.ComboBox, {
18007 cls: 'modal-header',
18029 cls: 'list-group-item',
18033 cls: 'roo-combobox-list-group-item-value'
18037 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18051 listItemCheckbox : {
18053 cls: 'list-group-item',
18057 cls: 'roo-combobox-list-group-item-value'
18061 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18077 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18082 cls: 'modal-footer',
18090 cls: 'col-xs-6 text-left',
18093 cls: 'btn btn-danger roo-touch-view-cancel',
18099 cls: 'col-xs-6 text-right',
18102 cls: 'btn btn-success roo-touch-view-ok',
18113 Roo.apply(Roo.bootstrap.ComboBox, {
18115 touchViewTemplate : {
18117 cls: 'modal fade roo-combobox-touch-view',
18121 cls: 'modal-dialog',
18122 style : 'position:fixed', // we have to fix position....
18126 cls: 'modal-content',
18128 Roo.bootstrap.ComboBox.header,
18129 Roo.bootstrap.ComboBox.body,
18130 Roo.bootstrap.ComboBox.footer
18139 * Ext JS Library 1.1.1
18140 * Copyright(c) 2006-2007, Ext JS, LLC.
18142 * Originally Released Under LGPL - original licence link has changed is not relivant.
18145 * <script type="text/javascript">
18150 * @extends Roo.util.Observable
18151 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18152 * This class also supports single and multi selection modes. <br>
18153 * Create a data model bound view:
18155 var store = new Roo.data.Store(...);
18157 var view = new Roo.View({
18159 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18161 singleSelect: true,
18162 selectedClass: "ydataview-selected",
18166 // listen for node click?
18167 view.on("click", function(vw, index, node, e){
18168 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18172 dataModel.load("foobar.xml");
18174 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18176 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18177 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18179 * Note: old style constructor is still suported (container, template, config)
18182 * Create a new View
18183 * @param {Object} config The config object
18186 Roo.View = function(config, depreciated_tpl, depreciated_config){
18188 this.parent = false;
18190 if (typeof(depreciated_tpl) == 'undefined') {
18191 // new way.. - universal constructor.
18192 Roo.apply(this, config);
18193 this.el = Roo.get(this.el);
18196 this.el = Roo.get(config);
18197 this.tpl = depreciated_tpl;
18198 Roo.apply(this, depreciated_config);
18200 this.wrapEl = this.el.wrap().wrap();
18201 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18204 if(typeof(this.tpl) == "string"){
18205 this.tpl = new Roo.Template(this.tpl);
18207 // support xtype ctors..
18208 this.tpl = new Roo.factory(this.tpl, Roo);
18212 this.tpl.compile();
18217 * @event beforeclick
18218 * Fires before a click is processed. Returns false to cancel the default action.
18219 * @param {Roo.View} this
18220 * @param {Number} index The index of the target node
18221 * @param {HTMLElement} node The target node
18222 * @param {Roo.EventObject} e The raw event object
18224 "beforeclick" : true,
18227 * Fires when a template node is clicked.
18228 * @param {Roo.View} this
18229 * @param {Number} index The index of the target node
18230 * @param {HTMLElement} node The target node
18231 * @param {Roo.EventObject} e The raw event object
18236 * Fires when a template node is double clicked.
18237 * @param {Roo.View} this
18238 * @param {Number} index The index of the target node
18239 * @param {HTMLElement} node The target node
18240 * @param {Roo.EventObject} e The raw event object
18244 * @event contextmenu
18245 * Fires when a template node is right clicked.
18246 * @param {Roo.View} this
18247 * @param {Number} index The index of the target node
18248 * @param {HTMLElement} node The target node
18249 * @param {Roo.EventObject} e The raw event object
18251 "contextmenu" : true,
18253 * @event selectionchange
18254 * Fires when the selected nodes change.
18255 * @param {Roo.View} this
18256 * @param {Array} selections Array of the selected nodes
18258 "selectionchange" : true,
18261 * @event beforeselect
18262 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18263 * @param {Roo.View} this
18264 * @param {HTMLElement} node The node to be selected
18265 * @param {Array} selections Array of currently selected nodes
18267 "beforeselect" : true,
18269 * @event preparedata
18270 * Fires on every row to render, to allow you to change the data.
18271 * @param {Roo.View} this
18272 * @param {Object} data to be rendered (change this)
18274 "preparedata" : true
18282 "click": this.onClick,
18283 "dblclick": this.onDblClick,
18284 "contextmenu": this.onContextMenu,
18288 this.selections = [];
18290 this.cmp = new Roo.CompositeElementLite([]);
18292 this.store = Roo.factory(this.store, Roo.data);
18293 this.setStore(this.store, true);
18296 if ( this.footer && this.footer.xtype) {
18298 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18300 this.footer.dataSource = this.store;
18301 this.footer.container = fctr;
18302 this.footer = Roo.factory(this.footer, Roo);
18303 fctr.insertFirst(this.el);
18305 // this is a bit insane - as the paging toolbar seems to detach the el..
18306 // dom.parentNode.parentNode.parentNode
18307 // they get detached?
18311 Roo.View.superclass.constructor.call(this);
18316 Roo.extend(Roo.View, Roo.util.Observable, {
18319 * @cfg {Roo.data.Store} store Data store to load data from.
18324 * @cfg {String|Roo.Element} el The container element.
18329 * @cfg {String|Roo.Template} tpl The template used by this View
18333 * @cfg {String} dataName the named area of the template to use as the data area
18334 * Works with domtemplates roo-name="name"
18338 * @cfg {String} selectedClass The css class to add to selected nodes
18340 selectedClass : "x-view-selected",
18342 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18347 * @cfg {String} text to display on mask (default Loading)
18351 * @cfg {Boolean} multiSelect Allow multiple selection
18353 multiSelect : false,
18355 * @cfg {Boolean} singleSelect Allow single selection
18357 singleSelect: false,
18360 * @cfg {Boolean} toggleSelect - selecting
18362 toggleSelect : false,
18365 * @cfg {Boolean} tickable - selecting
18370 * Returns the element this view is bound to.
18371 * @return {Roo.Element}
18373 getEl : function(){
18374 return this.wrapEl;
18380 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18382 refresh : function(){
18383 //Roo.log('refresh');
18386 // if we are using something like 'domtemplate', then
18387 // the what gets used is:
18388 // t.applySubtemplate(NAME, data, wrapping data..)
18389 // the outer template then get' applied with
18390 // the store 'extra data'
18391 // and the body get's added to the
18392 // roo-name="data" node?
18393 // <span class='roo-tpl-{name}'></span> ?????
18397 this.clearSelections();
18398 this.el.update("");
18400 var records = this.store.getRange();
18401 if(records.length < 1) {
18403 // is this valid?? = should it render a template??
18405 this.el.update(this.emptyText);
18409 if (this.dataName) {
18410 this.el.update(t.apply(this.store.meta)); //????
18411 el = this.el.child('.roo-tpl-' + this.dataName);
18414 for(var i = 0, len = records.length; i < len; i++){
18415 var data = this.prepareData(records[i].data, i, records[i]);
18416 this.fireEvent("preparedata", this, data, i, records[i]);
18418 var d = Roo.apply({}, data);
18421 Roo.apply(d, {'roo-id' : Roo.id()});
18425 Roo.each(this.parent.item, function(item){
18426 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18429 Roo.apply(d, {'roo-data-checked' : 'checked'});
18433 html[html.length] = Roo.util.Format.trim(
18435 t.applySubtemplate(this.dataName, d, this.store.meta) :
18442 el.update(html.join(""));
18443 this.nodes = el.dom.childNodes;
18444 this.updateIndexes(0);
18449 * Function to override to reformat the data that is sent to
18450 * the template for each node.
18451 * DEPRICATED - use the preparedata event handler.
18452 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18453 * a JSON object for an UpdateManager bound view).
18455 prepareData : function(data, index, record)
18457 this.fireEvent("preparedata", this, data, index, record);
18461 onUpdate : function(ds, record){
18462 // Roo.log('on update');
18463 this.clearSelections();
18464 var index = this.store.indexOf(record);
18465 var n = this.nodes[index];
18466 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18467 n.parentNode.removeChild(n);
18468 this.updateIndexes(index, index);
18474 onAdd : function(ds, records, index)
18476 //Roo.log(['on Add', ds, records, index] );
18477 this.clearSelections();
18478 if(this.nodes.length == 0){
18482 var n = this.nodes[index];
18483 for(var i = 0, len = records.length; i < len; i++){
18484 var d = this.prepareData(records[i].data, i, records[i]);
18486 this.tpl.insertBefore(n, d);
18489 this.tpl.append(this.el, d);
18492 this.updateIndexes(index);
18495 onRemove : function(ds, record, index){
18496 // Roo.log('onRemove');
18497 this.clearSelections();
18498 var el = this.dataName ?
18499 this.el.child('.roo-tpl-' + this.dataName) :
18502 el.dom.removeChild(this.nodes[index]);
18503 this.updateIndexes(index);
18507 * Refresh an individual node.
18508 * @param {Number} index
18510 refreshNode : function(index){
18511 this.onUpdate(this.store, this.store.getAt(index));
18514 updateIndexes : function(startIndex, endIndex){
18515 var ns = this.nodes;
18516 startIndex = startIndex || 0;
18517 endIndex = endIndex || ns.length - 1;
18518 for(var i = startIndex; i <= endIndex; i++){
18519 ns[i].nodeIndex = i;
18524 * Changes the data store this view uses and refresh the view.
18525 * @param {Store} store
18527 setStore : function(store, initial){
18528 if(!initial && this.store){
18529 this.store.un("datachanged", this.refresh);
18530 this.store.un("add", this.onAdd);
18531 this.store.un("remove", this.onRemove);
18532 this.store.un("update", this.onUpdate);
18533 this.store.un("clear", this.refresh);
18534 this.store.un("beforeload", this.onBeforeLoad);
18535 this.store.un("load", this.onLoad);
18536 this.store.un("loadexception", this.onLoad);
18540 store.on("datachanged", this.refresh, this);
18541 store.on("add", this.onAdd, this);
18542 store.on("remove", this.onRemove, this);
18543 store.on("update", this.onUpdate, this);
18544 store.on("clear", this.refresh, this);
18545 store.on("beforeload", this.onBeforeLoad, this);
18546 store.on("load", this.onLoad, this);
18547 store.on("loadexception", this.onLoad, this);
18555 * onbeforeLoad - masks the loading area.
18558 onBeforeLoad : function(store,opts)
18560 //Roo.log('onBeforeLoad');
18562 this.el.update("");
18564 this.el.mask(this.mask ? this.mask : "Loading" );
18566 onLoad : function ()
18573 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18574 * @param {HTMLElement} node
18575 * @return {HTMLElement} The template node
18577 findItemFromChild : function(node){
18578 var el = this.dataName ?
18579 this.el.child('.roo-tpl-' + this.dataName,true) :
18582 if(!node || node.parentNode == el){
18585 var p = node.parentNode;
18586 while(p && p != el){
18587 if(p.parentNode == el){
18596 onClick : function(e){
18597 var item = this.findItemFromChild(e.getTarget());
18599 var index = this.indexOf(item);
18600 if(this.onItemClick(item, index, e) !== false){
18601 this.fireEvent("click", this, index, item, e);
18604 this.clearSelections();
18609 onContextMenu : function(e){
18610 var item = this.findItemFromChild(e.getTarget());
18612 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18617 onDblClick : function(e){
18618 var item = this.findItemFromChild(e.getTarget());
18620 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18624 onItemClick : function(item, index, e)
18626 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18629 if (this.toggleSelect) {
18630 var m = this.isSelected(item) ? 'unselect' : 'select';
18633 _t[m](item, true, false);
18636 if(this.multiSelect || this.singleSelect){
18637 if(this.multiSelect && e.shiftKey && this.lastSelection){
18638 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18640 this.select(item, this.multiSelect && e.ctrlKey);
18641 this.lastSelection = item;
18644 if(!this.tickable){
18645 e.preventDefault();
18653 * Get the number of selected nodes.
18656 getSelectionCount : function(){
18657 return this.selections.length;
18661 * Get the currently selected nodes.
18662 * @return {Array} An array of HTMLElements
18664 getSelectedNodes : function(){
18665 return this.selections;
18669 * Get the indexes of the selected nodes.
18672 getSelectedIndexes : function(){
18673 var indexes = [], s = this.selections;
18674 for(var i = 0, len = s.length; i < len; i++){
18675 indexes.push(s[i].nodeIndex);
18681 * Clear all selections
18682 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18684 clearSelections : function(suppressEvent){
18685 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18686 this.cmp.elements = this.selections;
18687 this.cmp.removeClass(this.selectedClass);
18688 this.selections = [];
18689 if(!suppressEvent){
18690 this.fireEvent("selectionchange", this, this.selections);
18696 * Returns true if the passed node is selected
18697 * @param {HTMLElement/Number} node The node or node index
18698 * @return {Boolean}
18700 isSelected : function(node){
18701 var s = this.selections;
18705 node = this.getNode(node);
18706 return s.indexOf(node) !== -1;
18711 * @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
18712 * @param {Boolean} keepExisting (optional) true to keep existing selections
18713 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18715 select : function(nodeInfo, keepExisting, suppressEvent){
18716 if(nodeInfo instanceof Array){
18718 this.clearSelections(true);
18720 for(var i = 0, len = nodeInfo.length; i < len; i++){
18721 this.select(nodeInfo[i], true, true);
18725 var node = this.getNode(nodeInfo);
18726 if(!node || this.isSelected(node)){
18727 return; // already selected.
18730 this.clearSelections(true);
18733 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18734 Roo.fly(node).addClass(this.selectedClass);
18735 this.selections.push(node);
18736 if(!suppressEvent){
18737 this.fireEvent("selectionchange", this, this.selections);
18745 * @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
18746 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18747 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18749 unselect : function(nodeInfo, keepExisting, suppressEvent)
18751 if(nodeInfo instanceof Array){
18752 Roo.each(this.selections, function(s) {
18753 this.unselect(s, nodeInfo);
18757 var node = this.getNode(nodeInfo);
18758 if(!node || !this.isSelected(node)){
18759 //Roo.log("not selected");
18760 return; // not selected.
18764 Roo.each(this.selections, function(s) {
18766 Roo.fly(node).removeClass(this.selectedClass);
18773 this.selections= ns;
18774 this.fireEvent("selectionchange", this, this.selections);
18778 * Gets a template node.
18779 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18780 * @return {HTMLElement} The node or null if it wasn't found
18782 getNode : function(nodeInfo){
18783 if(typeof nodeInfo == "string"){
18784 return document.getElementById(nodeInfo);
18785 }else if(typeof nodeInfo == "number"){
18786 return this.nodes[nodeInfo];
18792 * Gets a range template nodes.
18793 * @param {Number} startIndex
18794 * @param {Number} endIndex
18795 * @return {Array} An array of nodes
18797 getNodes : function(start, end){
18798 var ns = this.nodes;
18799 start = start || 0;
18800 end = typeof end == "undefined" ? ns.length - 1 : end;
18803 for(var i = start; i <= end; i++){
18807 for(var i = start; i >= end; i--){
18815 * Finds the index of the passed node
18816 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18817 * @return {Number} The index of the node or -1
18819 indexOf : function(node){
18820 node = this.getNode(node);
18821 if(typeof node.nodeIndex == "number"){
18822 return node.nodeIndex;
18824 var ns = this.nodes;
18825 for(var i = 0, len = ns.length; i < len; i++){
18836 * based on jquery fullcalendar
18840 Roo.bootstrap = Roo.bootstrap || {};
18842 * @class Roo.bootstrap.Calendar
18843 * @extends Roo.bootstrap.Component
18844 * Bootstrap Calendar class
18845 * @cfg {Boolean} loadMask (true|false) default false
18846 * @cfg {Object} header generate the user specific header of the calendar, default false
18849 * Create a new Container
18850 * @param {Object} config The config object
18855 Roo.bootstrap.Calendar = function(config){
18856 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18860 * Fires when a date is selected
18861 * @param {DatePicker} this
18862 * @param {Date} date The selected date
18866 * @event monthchange
18867 * Fires when the displayed month changes
18868 * @param {DatePicker} this
18869 * @param {Date} date The selected month
18871 'monthchange': true,
18873 * @event evententer
18874 * Fires when mouse over an event
18875 * @param {Calendar} this
18876 * @param {event} Event
18878 'evententer': true,
18880 * @event eventleave
18881 * Fires when the mouse leaves an
18882 * @param {Calendar} this
18885 'eventleave': true,
18887 * @event eventclick
18888 * Fires when the mouse click an
18889 * @param {Calendar} this
18898 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18901 * @cfg {Number} startDay
18902 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18910 getAutoCreate : function(){
18913 var fc_button = function(name, corner, style, content ) {
18914 return Roo.apply({},{
18916 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18918 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18921 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18932 style : 'width:100%',
18939 cls : 'fc-header-left',
18941 fc_button('prev', 'left', 'arrow', '‹' ),
18942 fc_button('next', 'right', 'arrow', '›' ),
18943 { tag: 'span', cls: 'fc-header-space' },
18944 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18952 cls : 'fc-header-center',
18956 cls: 'fc-header-title',
18959 html : 'month / year'
18967 cls : 'fc-header-right',
18969 /* fc_button('month', 'left', '', 'month' ),
18970 fc_button('week', '', '', 'week' ),
18971 fc_button('day', 'right', '', 'day' )
18983 header = this.header;
18986 var cal_heads = function() {
18988 // fixme - handle this.
18990 for (var i =0; i < Date.dayNames.length; i++) {
18991 var d = Date.dayNames[i];
18994 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18995 html : d.substring(0,3)
18999 ret[0].cls += ' fc-first';
19000 ret[6].cls += ' fc-last';
19003 var cal_cell = function(n) {
19006 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19011 cls: 'fc-day-number',
19015 cls: 'fc-day-content',
19019 style: 'position: relative;' // height: 17px;
19031 var cal_rows = function() {
19034 for (var r = 0; r < 6; r++) {
19041 for (var i =0; i < Date.dayNames.length; i++) {
19042 var d = Date.dayNames[i];
19043 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19046 row.cn[0].cls+=' fc-first';
19047 row.cn[0].cn[0].style = 'min-height:90px';
19048 row.cn[6].cls+=' fc-last';
19052 ret[0].cls += ' fc-first';
19053 ret[4].cls += ' fc-prev-last';
19054 ret[5].cls += ' fc-last';
19061 cls: 'fc-border-separate',
19062 style : 'width:100%',
19070 cls : 'fc-first fc-last',
19088 cls : 'fc-content',
19089 style : "position: relative;",
19092 cls : 'fc-view fc-view-month fc-grid',
19093 style : 'position: relative',
19094 unselectable : 'on',
19097 cls : 'fc-event-container',
19098 style : 'position:absolute;z-index:8;top:0;left:0;'
19116 initEvents : function()
19119 throw "can not find store for calendar";
19125 style: "text-align:center",
19129 style: "background-color:white;width:50%;margin:250 auto",
19133 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19144 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19146 var size = this.el.select('.fc-content', true).first().getSize();
19147 this.maskEl.setSize(size.width, size.height);
19148 this.maskEl.enableDisplayMode("block");
19149 if(!this.loadMask){
19150 this.maskEl.hide();
19153 this.store = Roo.factory(this.store, Roo.data);
19154 this.store.on('load', this.onLoad, this);
19155 this.store.on('beforeload', this.onBeforeLoad, this);
19159 this.cells = this.el.select('.fc-day',true);
19160 //Roo.log(this.cells);
19161 this.textNodes = this.el.query('.fc-day-number');
19162 this.cells.addClassOnOver('fc-state-hover');
19164 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19165 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19166 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19167 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19169 this.on('monthchange', this.onMonthChange, this);
19171 this.update(new Date().clearTime());
19174 resize : function() {
19175 var sz = this.el.getSize();
19177 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19178 this.el.select('.fc-day-content div',true).setHeight(34);
19183 showPrevMonth : function(e){
19184 this.update(this.activeDate.add("mo", -1));
19186 showToday : function(e){
19187 this.update(new Date().clearTime());
19190 showNextMonth : function(e){
19191 this.update(this.activeDate.add("mo", 1));
19195 showPrevYear : function(){
19196 this.update(this.activeDate.add("y", -1));
19200 showNextYear : function(){
19201 this.update(this.activeDate.add("y", 1));
19206 update : function(date)
19208 var vd = this.activeDate;
19209 this.activeDate = date;
19210 // if(vd && this.el){
19211 // var t = date.getTime();
19212 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19213 // Roo.log('using add remove');
19215 // this.fireEvent('monthchange', this, date);
19217 // this.cells.removeClass("fc-state-highlight");
19218 // this.cells.each(function(c){
19219 // if(c.dateValue == t){
19220 // c.addClass("fc-state-highlight");
19221 // setTimeout(function(){
19222 // try{c.dom.firstChild.focus();}catch(e){}
19232 var days = date.getDaysInMonth();
19234 var firstOfMonth = date.getFirstDateOfMonth();
19235 var startingPos = firstOfMonth.getDay()-this.startDay;
19237 if(startingPos < this.startDay){
19241 var pm = date.add(Date.MONTH, -1);
19242 var prevStart = pm.getDaysInMonth()-startingPos;
19244 this.cells = this.el.select('.fc-day',true);
19245 this.textNodes = this.el.query('.fc-day-number');
19246 this.cells.addClassOnOver('fc-state-hover');
19248 var cells = this.cells.elements;
19249 var textEls = this.textNodes;
19251 Roo.each(cells, function(cell){
19252 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19255 days += startingPos;
19257 // convert everything to numbers so it's fast
19258 var day = 86400000;
19259 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19262 //Roo.log(prevStart);
19264 var today = new Date().clearTime().getTime();
19265 var sel = date.clearTime().getTime();
19266 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19267 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19268 var ddMatch = this.disabledDatesRE;
19269 var ddText = this.disabledDatesText;
19270 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19271 var ddaysText = this.disabledDaysText;
19272 var format = this.format;
19274 var setCellClass = function(cal, cell){
19278 //Roo.log('set Cell Class');
19280 var t = d.getTime();
19284 cell.dateValue = t;
19286 cell.className += " fc-today";
19287 cell.className += " fc-state-highlight";
19288 cell.title = cal.todayText;
19291 // disable highlight in other month..
19292 //cell.className += " fc-state-highlight";
19297 cell.className = " fc-state-disabled";
19298 cell.title = cal.minText;
19302 cell.className = " fc-state-disabled";
19303 cell.title = cal.maxText;
19307 if(ddays.indexOf(d.getDay()) != -1){
19308 cell.title = ddaysText;
19309 cell.className = " fc-state-disabled";
19312 if(ddMatch && format){
19313 var fvalue = d.dateFormat(format);
19314 if(ddMatch.test(fvalue)){
19315 cell.title = ddText.replace("%0", fvalue);
19316 cell.className = " fc-state-disabled";
19320 if (!cell.initialClassName) {
19321 cell.initialClassName = cell.dom.className;
19324 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19329 for(; i < startingPos; i++) {
19330 textEls[i].innerHTML = (++prevStart);
19331 d.setDate(d.getDate()+1);
19333 cells[i].className = "fc-past fc-other-month";
19334 setCellClass(this, cells[i]);
19339 for(; i < days; i++){
19340 intDay = i - startingPos + 1;
19341 textEls[i].innerHTML = (intDay);
19342 d.setDate(d.getDate()+1);
19344 cells[i].className = ''; // "x-date-active";
19345 setCellClass(this, cells[i]);
19349 for(; i < 42; i++) {
19350 textEls[i].innerHTML = (++extraDays);
19351 d.setDate(d.getDate()+1);
19353 cells[i].className = "fc-future fc-other-month";
19354 setCellClass(this, cells[i]);
19357 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19359 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19361 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19362 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19364 if(totalRows != 6){
19365 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19366 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19369 this.fireEvent('monthchange', this, date);
19373 if(!this.internalRender){
19374 var main = this.el.dom.firstChild;
19375 var w = main.offsetWidth;
19376 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19377 Roo.fly(main).setWidth(w);
19378 this.internalRender = true;
19379 // opera does not respect the auto grow header center column
19380 // then, after it gets a width opera refuses to recalculate
19381 // without a second pass
19382 if(Roo.isOpera && !this.secondPass){
19383 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19384 this.secondPass = true;
19385 this.update.defer(10, this, [date]);
19392 findCell : function(dt) {
19393 dt = dt.clearTime().getTime();
19395 this.cells.each(function(c){
19396 //Roo.log("check " +c.dateValue + '?=' + dt);
19397 if(c.dateValue == dt){
19407 findCells : function(ev) {
19408 var s = ev.start.clone().clearTime().getTime();
19410 var e= ev.end.clone().clearTime().getTime();
19413 this.cells.each(function(c){
19414 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19416 if(c.dateValue > e){
19419 if(c.dateValue < s){
19428 // findBestRow: function(cells)
19432 // for (var i =0 ; i < cells.length;i++) {
19433 // ret = Math.max(cells[i].rows || 0,ret);
19440 addItem : function(ev)
19442 // look for vertical location slot in
19443 var cells = this.findCells(ev);
19445 // ev.row = this.findBestRow(cells);
19447 // work out the location.
19451 for(var i =0; i < cells.length; i++) {
19453 cells[i].row = cells[0].row;
19456 cells[i].row = cells[i].row + 1;
19466 if (crow.start.getY() == cells[i].getY()) {
19468 crow.end = cells[i];
19485 cells[0].events.push(ev);
19487 this.calevents.push(ev);
19490 clearEvents: function() {
19492 if(!this.calevents){
19496 Roo.each(this.cells.elements, function(c){
19502 Roo.each(this.calevents, function(e) {
19503 Roo.each(e.els, function(el) {
19504 el.un('mouseenter' ,this.onEventEnter, this);
19505 el.un('mouseleave' ,this.onEventLeave, this);
19510 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19516 renderEvents: function()
19520 this.cells.each(function(c) {
19529 if(c.row != c.events.length){
19530 r = 4 - (4 - (c.row - c.events.length));
19533 c.events = ev.slice(0, r);
19534 c.more = ev.slice(r);
19536 if(c.more.length && c.more.length == 1){
19537 c.events.push(c.more.pop());
19540 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19544 this.cells.each(function(c) {
19546 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19549 for (var e = 0; e < c.events.length; e++){
19550 var ev = c.events[e];
19551 var rows = ev.rows;
19553 for(var i = 0; i < rows.length; i++) {
19555 // how many rows should it span..
19558 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19559 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19561 unselectable : "on",
19564 cls: 'fc-event-inner',
19568 // cls: 'fc-event-time',
19569 // html : cells.length > 1 ? '' : ev.time
19573 cls: 'fc-event-title',
19574 html : String.format('{0}', ev.title)
19581 cls: 'ui-resizable-handle ui-resizable-e',
19582 html : '  '
19589 cfg.cls += ' fc-event-start';
19591 if ((i+1) == rows.length) {
19592 cfg.cls += ' fc-event-end';
19595 var ctr = _this.el.select('.fc-event-container',true).first();
19596 var cg = ctr.createChild(cfg);
19598 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19599 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19601 var r = (c.more.length) ? 1 : 0;
19602 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19603 cg.setWidth(ebox.right - sbox.x -2);
19605 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19606 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19607 cg.on('click', _this.onEventClick, _this, ev);
19618 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19619 style : 'position: absolute',
19620 unselectable : "on",
19623 cls: 'fc-event-inner',
19627 cls: 'fc-event-title',
19635 cls: 'ui-resizable-handle ui-resizable-e',
19636 html : '  '
19642 var ctr = _this.el.select('.fc-event-container',true).first();
19643 var cg = ctr.createChild(cfg);
19645 var sbox = c.select('.fc-day-content',true).first().getBox();
19646 var ebox = c.select('.fc-day-content',true).first().getBox();
19648 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19649 cg.setWidth(ebox.right - sbox.x -2);
19651 cg.on('click', _this.onMoreEventClick, _this, c.more);
19661 onEventEnter: function (e, el,event,d) {
19662 this.fireEvent('evententer', this, el, event);
19665 onEventLeave: function (e, el,event,d) {
19666 this.fireEvent('eventleave', this, el, event);
19669 onEventClick: function (e, el,event,d) {
19670 this.fireEvent('eventclick', this, el, event);
19673 onMonthChange: function () {
19677 onMoreEventClick: function(e, el, more)
19681 this.calpopover.placement = 'right';
19682 this.calpopover.setTitle('More');
19684 this.calpopover.setContent('');
19686 var ctr = this.calpopover.el.select('.popover-content', true).first();
19688 Roo.each(more, function(m){
19690 cls : 'fc-event-hori fc-event-draggable',
19693 var cg = ctr.createChild(cfg);
19695 cg.on('click', _this.onEventClick, _this, m);
19698 this.calpopover.show(el);
19703 onLoad: function ()
19705 this.calevents = [];
19708 if(this.store.getCount() > 0){
19709 this.store.data.each(function(d){
19712 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19713 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19714 time : d.data.start_time,
19715 title : d.data.title,
19716 description : d.data.description,
19717 venue : d.data.venue
19722 this.renderEvents();
19724 if(this.calevents.length && this.loadMask){
19725 this.maskEl.hide();
19729 onBeforeLoad: function()
19731 this.clearEvents();
19733 this.maskEl.show();
19747 * @class Roo.bootstrap.Popover
19748 * @extends Roo.bootstrap.Component
19749 * Bootstrap Popover class
19750 * @cfg {String} html contents of the popover (or false to use children..)
19751 * @cfg {String} title of popover (or false to hide)
19752 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19753 * @cfg {String} trigger click || hover (or false to trigger manually)
19754 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19755 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19756 * - if false and it has a 'parent' then it will be automatically added to that element
19757 * - if string - Roo.get will be called
19758 * @cfg {Number} delay - delay before showing
19761 * Create a new Popover
19762 * @param {Object} config The config object
19765 Roo.bootstrap.Popover = function(config){
19766 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19772 * After the popover show
19774 * @param {Roo.bootstrap.Popover} this
19779 * After the popover hide
19781 * @param {Roo.bootstrap.Popover} this
19787 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19792 placement : 'right',
19793 trigger : 'hover', // hover
19799 can_build_overlaid : false,
19801 maskEl : false, // the mask element
19804 alignEl : false, // when show is called with an element - this get's stored.
19806 getChildContainer : function()
19808 return this.contentEl;
19811 getPopoverHeader : function()
19813 this.title = true; // flag not to hide it..
19814 this.headerEl.addClass('p-0');
19815 return this.headerEl
19819 getAutoCreate : function(){
19822 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19823 style: 'display:block',
19829 cls : 'popover-inner ',
19833 cls: 'popover-title popover-header',
19834 html : this.title === false ? '' : this.title
19837 cls : 'popover-content popover-body ' + (this.cls || ''),
19838 html : this.html || ''
19849 * @param {string} the title
19851 setTitle: function(str)
19855 this.headerEl.dom.innerHTML = str;
19860 * @param {string} the body content
19862 setContent: function(str)
19865 if (this.contentEl) {
19866 this.contentEl.dom.innerHTML = str;
19870 // as it get's added to the bottom of the page.
19871 onRender : function(ct, position)
19873 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19878 var cfg = Roo.apply({}, this.getAutoCreate());
19882 cfg.cls += ' ' + this.cls;
19885 cfg.style = this.style;
19887 //Roo.log("adding to ");
19888 this.el = Roo.get(document.body).createChild(cfg, position);
19889 // Roo.log(this.el);
19892 this.contentEl = this.el.select('.popover-content',true).first();
19893 this.headerEl = this.el.select('.popover-title',true).first();
19896 if(typeof(this.items) != 'undefined'){
19897 var items = this.items;
19900 for(var i =0;i < items.length;i++) {
19901 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19905 this.items = nitems;
19907 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19908 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19915 resizeMask : function()
19917 this.maskEl.setSize(
19918 Roo.lib.Dom.getViewWidth(true),
19919 Roo.lib.Dom.getViewHeight(true)
19923 initEvents : function()
19927 Roo.bootstrap.Popover.register(this);
19930 this.arrowEl = this.el.select('.arrow',true).first();
19931 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19932 this.el.enableDisplayMode('block');
19936 if (this.over === false && !this.parent()) {
19939 if (this.triggers === false) {
19944 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19945 var triggers = this.trigger ? this.trigger.split(' ') : [];
19946 Roo.each(triggers, function(trigger) {
19948 if (trigger == 'click') {
19949 on_el.on('click', this.toggle, this);
19950 } else if (trigger != 'manual') {
19951 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19952 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19954 on_el.on(eventIn ,this.enter, this);
19955 on_el.on(eventOut, this.leave, this);
19965 toggle : function () {
19966 this.hoverState == 'in' ? this.leave() : this.enter();
19969 enter : function () {
19971 clearTimeout(this.timeout);
19973 this.hoverState = 'in';
19975 if (!this.delay || !this.delay.show) {
19980 this.timeout = setTimeout(function () {
19981 if (_t.hoverState == 'in') {
19984 }, this.delay.show)
19987 leave : function() {
19988 clearTimeout(this.timeout);
19990 this.hoverState = 'out';
19992 if (!this.delay || !this.delay.hide) {
19997 this.timeout = setTimeout(function () {
19998 if (_t.hoverState == 'out') {
20001 }, this.delay.hide)
20005 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20006 * @param {string} (left|right|top|bottom) position
20008 show : function (on_el, placement)
20010 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20011 on_el = on_el || false; // default to false
20014 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20015 on_el = this.parent().el;
20016 } else if (this.over) {
20017 Roo.get(this.over);
20022 this.alignEl = Roo.get( on_el );
20025 this.render(document.body);
20031 if (this.title === false) {
20032 this.headerEl.hide();
20037 this.el.dom.style.display = 'block';
20040 if (this.alignEl) {
20041 this.updatePosition(this.placement, true);
20044 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20045 var es = this.el.getSize();
20046 var x = Roo.lib.Dom.getViewWidth()/2;
20047 var y = Roo.lib.Dom.getViewHeight()/2;
20048 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20053 //var arrow = this.el.select('.arrow',true).first();
20054 //arrow.set(align[2],
20056 this.el.addClass('in');
20060 this.hoverState = 'in';
20063 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20064 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20065 this.maskEl.dom.style.display = 'block';
20066 this.maskEl.addClass('show');
20068 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20070 this.fireEvent('show', this);
20074 * fire this manually after loading a grid in the table for example
20075 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20076 * @param {Boolean} try and move it if we cant get right position.
20078 updatePosition : function(placement, try_move)
20080 // allow for calling with no parameters
20081 placement = placement ? placement : this.placement;
20082 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20084 this.el.removeClass([
20085 'fade','top','bottom', 'left', 'right','in',
20086 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20088 this.el.addClass(placement + ' bs-popover-' + placement);
20090 if (!this.alignEl ) {
20094 switch (placement) {
20096 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20097 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20098 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20099 //normal display... or moved up/down.
20100 this.el.setXY(offset);
20101 var xy = this.alignEl.getAnchorXY('tr', false);
20103 this.arrowEl.setXY(xy);
20106 // continue through...
20107 return this.updatePosition('left', false);
20111 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20112 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20113 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20114 //normal display... or moved up/down.
20115 this.el.setXY(offset);
20116 var xy = this.alignEl.getAnchorXY('tl', false);
20117 xy[0]-=10;xy[1]+=5; // << fix me
20118 this.arrowEl.setXY(xy);
20122 return this.updatePosition('right', false);
20125 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20126 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20127 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20128 //normal display... or moved up/down.
20129 this.el.setXY(offset);
20130 var xy = this.alignEl.getAnchorXY('t', false);
20131 xy[1]-=10; // << fix me
20132 this.arrowEl.setXY(xy);
20136 return this.updatePosition('bottom', false);
20139 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20140 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20141 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20142 //normal display... or moved up/down.
20143 this.el.setXY(offset);
20144 var xy = this.alignEl.getAnchorXY('b', false);
20145 xy[1]+=2; // << fix me
20146 this.arrowEl.setXY(xy);
20150 return this.updatePosition('top', false);
20161 this.el.setXY([0,0]);
20162 this.el.removeClass('in');
20164 this.hoverState = null;
20165 this.maskEl.hide(); // always..
20166 this.fireEvent('hide', this);
20172 Roo.apply(Roo.bootstrap.Popover, {
20175 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20176 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20177 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20178 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20183 clickHander : false,
20186 onMouseDown : function(e)
20188 if (!e.getTarget(".roo-popover")) {
20196 register : function(popup)
20198 if (!Roo.bootstrap.Popover.clickHandler) {
20199 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20201 // hide other popups.
20203 this.popups.push(popup);
20205 hideAll : function()
20207 this.popups.forEach(function(p) {
20215 * Card header - holder for the card header elements.
20220 * @class Roo.bootstrap.PopoverNav
20221 * @extends Roo.bootstrap.NavGroup
20222 * Bootstrap Popover header navigation class
20224 * Create a new Popover Header Navigation
20225 * @param {Object} config The config object
20228 Roo.bootstrap.PopoverNav = function(config){
20229 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20232 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20235 container_method : 'getPopoverHeader'
20253 * @class Roo.bootstrap.Progress
20254 * @extends Roo.bootstrap.Component
20255 * Bootstrap Progress class
20256 * @cfg {Boolean} striped striped of the progress bar
20257 * @cfg {Boolean} active animated of the progress bar
20261 * Create a new Progress
20262 * @param {Object} config The config object
20265 Roo.bootstrap.Progress = function(config){
20266 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20269 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20274 getAutoCreate : function(){
20282 cfg.cls += ' progress-striped';
20286 cfg.cls += ' active';
20305 * @class Roo.bootstrap.ProgressBar
20306 * @extends Roo.bootstrap.Component
20307 * Bootstrap ProgressBar class
20308 * @cfg {Number} aria_valuenow aria-value now
20309 * @cfg {Number} aria_valuemin aria-value min
20310 * @cfg {Number} aria_valuemax aria-value max
20311 * @cfg {String} label label for the progress bar
20312 * @cfg {String} panel (success | info | warning | danger )
20313 * @cfg {String} role role of the progress bar
20314 * @cfg {String} sr_only text
20318 * Create a new ProgressBar
20319 * @param {Object} config The config object
20322 Roo.bootstrap.ProgressBar = function(config){
20323 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20326 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20330 aria_valuemax : 100,
20336 getAutoCreate : function()
20341 cls: 'progress-bar',
20342 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20354 cfg.role = this.role;
20357 if(this.aria_valuenow){
20358 cfg['aria-valuenow'] = this.aria_valuenow;
20361 if(this.aria_valuemin){
20362 cfg['aria-valuemin'] = this.aria_valuemin;
20365 if(this.aria_valuemax){
20366 cfg['aria-valuemax'] = this.aria_valuemax;
20369 if(this.label && !this.sr_only){
20370 cfg.html = this.label;
20374 cfg.cls += ' progress-bar-' + this.panel;
20380 update : function(aria_valuenow)
20382 this.aria_valuenow = aria_valuenow;
20384 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20399 * @class Roo.bootstrap.TabGroup
20400 * @extends Roo.bootstrap.Column
20401 * Bootstrap Column class
20402 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20403 * @cfg {Boolean} carousel true to make the group behave like a carousel
20404 * @cfg {Boolean} bullets show bullets for the panels
20405 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20406 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20407 * @cfg {Boolean} showarrow (true|false) show arrow default true
20410 * Create a new TabGroup
20411 * @param {Object} config The config object
20414 Roo.bootstrap.TabGroup = function(config){
20415 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20417 this.navId = Roo.id();
20420 Roo.bootstrap.TabGroup.register(this);
20424 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20427 transition : false,
20432 slideOnTouch : false,
20435 getAutoCreate : function()
20437 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20439 cfg.cls += ' tab-content';
20441 if (this.carousel) {
20442 cfg.cls += ' carousel slide';
20445 cls : 'carousel-inner',
20449 if(this.bullets && !Roo.isTouch){
20452 cls : 'carousel-bullets',
20456 if(this.bullets_cls){
20457 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20464 cfg.cn[0].cn.push(bullets);
20467 if(this.showarrow){
20468 cfg.cn[0].cn.push({
20470 class : 'carousel-arrow',
20474 class : 'carousel-prev',
20478 class : 'fa fa-chevron-left'
20484 class : 'carousel-next',
20488 class : 'fa fa-chevron-right'
20501 initEvents: function()
20503 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20504 // this.el.on("touchstart", this.onTouchStart, this);
20507 if(this.autoslide){
20510 this.slideFn = window.setInterval(function() {
20511 _this.showPanelNext();
20515 if(this.showarrow){
20516 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20517 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20523 // onTouchStart : function(e, el, o)
20525 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20529 // this.showPanelNext();
20533 getChildContainer : function()
20535 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20539 * register a Navigation item
20540 * @param {Roo.bootstrap.NavItem} the navitem to add
20542 register : function(item)
20544 this.tabs.push( item);
20545 item.navId = this.navId; // not really needed..
20550 getActivePanel : function()
20553 Roo.each(this.tabs, function(t) {
20563 getPanelByName : function(n)
20566 Roo.each(this.tabs, function(t) {
20567 if (t.tabId == n) {
20575 indexOfPanel : function(p)
20578 Roo.each(this.tabs, function(t,i) {
20579 if (t.tabId == p.tabId) {
20588 * show a specific panel
20589 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20590 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20592 showPanel : function (pan)
20594 if(this.transition || typeof(pan) == 'undefined'){
20595 Roo.log("waiting for the transitionend");
20599 if (typeof(pan) == 'number') {
20600 pan = this.tabs[pan];
20603 if (typeof(pan) == 'string') {
20604 pan = this.getPanelByName(pan);
20607 var cur = this.getActivePanel();
20610 Roo.log('pan or acitve pan is undefined');
20614 if (pan.tabId == this.getActivePanel().tabId) {
20618 if (false === cur.fireEvent('beforedeactivate')) {
20622 if(this.bullets > 0 && !Roo.isTouch){
20623 this.setActiveBullet(this.indexOfPanel(pan));
20626 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20628 //class="carousel-item carousel-item-next carousel-item-left"
20630 this.transition = true;
20631 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20632 var lr = dir == 'next' ? 'left' : 'right';
20633 pan.el.addClass(dir); // or prev
20634 pan.el.addClass('carousel-item-' + dir); // or prev
20635 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20636 cur.el.addClass(lr); // or right
20637 pan.el.addClass(lr);
20638 cur.el.addClass('carousel-item-' +lr); // or right
20639 pan.el.addClass('carousel-item-' +lr);
20643 cur.el.on('transitionend', function() {
20644 Roo.log("trans end?");
20646 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20647 pan.setActive(true);
20649 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20650 cur.setActive(false);
20652 _this.transition = false;
20654 }, this, { single: true } );
20659 cur.setActive(false);
20660 pan.setActive(true);
20665 showPanelNext : function()
20667 var i = this.indexOfPanel(this.getActivePanel());
20669 if (i >= this.tabs.length - 1 && !this.autoslide) {
20673 if (i >= this.tabs.length - 1 && this.autoslide) {
20677 this.showPanel(this.tabs[i+1]);
20680 showPanelPrev : function()
20682 var i = this.indexOfPanel(this.getActivePanel());
20684 if (i < 1 && !this.autoslide) {
20688 if (i < 1 && this.autoslide) {
20689 i = this.tabs.length;
20692 this.showPanel(this.tabs[i-1]);
20696 addBullet: function()
20698 if(!this.bullets || Roo.isTouch){
20701 var ctr = this.el.select('.carousel-bullets',true).first();
20702 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20703 var bullet = ctr.createChild({
20704 cls : 'bullet bullet-' + i
20705 },ctr.dom.lastChild);
20710 bullet.on('click', (function(e, el, o, ii, t){
20712 e.preventDefault();
20714 this.showPanel(ii);
20716 if(this.autoslide && this.slideFn){
20717 clearInterval(this.slideFn);
20718 this.slideFn = window.setInterval(function() {
20719 _this.showPanelNext();
20723 }).createDelegate(this, [i, bullet], true));
20728 setActiveBullet : function(i)
20734 Roo.each(this.el.select('.bullet', true).elements, function(el){
20735 el.removeClass('selected');
20738 var bullet = this.el.select('.bullet-' + i, true).first();
20744 bullet.addClass('selected');
20755 Roo.apply(Roo.bootstrap.TabGroup, {
20759 * register a Navigation Group
20760 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20762 register : function(navgrp)
20764 this.groups[navgrp.navId] = navgrp;
20768 * fetch a Navigation Group based on the navigation ID
20769 * if one does not exist , it will get created.
20770 * @param {string} the navgroup to add
20771 * @returns {Roo.bootstrap.NavGroup} the navgroup
20773 get: function(navId) {
20774 if (typeof(this.groups[navId]) == 'undefined') {
20775 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20777 return this.groups[navId] ;
20792 * @class Roo.bootstrap.TabPanel
20793 * @extends Roo.bootstrap.Component
20794 * Bootstrap TabPanel class
20795 * @cfg {Boolean} active panel active
20796 * @cfg {String} html panel content
20797 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20798 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20799 * @cfg {String} href click to link..
20800 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20804 * Create a new TabPanel
20805 * @param {Object} config The config object
20808 Roo.bootstrap.TabPanel = function(config){
20809 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20813 * Fires when the active status changes
20814 * @param {Roo.bootstrap.TabPanel} this
20815 * @param {Boolean} state the new state
20820 * @event beforedeactivate
20821 * Fires before a tab is de-activated - can be used to do validation on a form.
20822 * @param {Roo.bootstrap.TabPanel} this
20823 * @return {Boolean} false if there is an error
20826 'beforedeactivate': true
20829 this.tabId = this.tabId || Roo.id();
20833 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20840 touchSlide : false,
20841 getAutoCreate : function(){
20846 // item is needed for carousel - not sure if it has any effect otherwise
20847 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20848 html: this.html || ''
20852 cfg.cls += ' active';
20856 cfg.tabId = this.tabId;
20864 initEvents: function()
20866 var p = this.parent();
20868 this.navId = this.navId || p.navId;
20870 if (typeof(this.navId) != 'undefined') {
20871 // not really needed.. but just in case.. parent should be a NavGroup.
20872 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20876 var i = tg.tabs.length - 1;
20878 if(this.active && tg.bullets > 0 && i < tg.bullets){
20879 tg.setActiveBullet(i);
20883 this.el.on('click', this.onClick, this);
20885 if(Roo.isTouch && this.touchSlide){
20886 this.el.on("touchstart", this.onTouchStart, this);
20887 this.el.on("touchmove", this.onTouchMove, this);
20888 this.el.on("touchend", this.onTouchEnd, this);
20893 onRender : function(ct, position)
20895 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20898 setActive : function(state)
20900 Roo.log("panel - set active " + this.tabId + "=" + state);
20902 this.active = state;
20904 this.el.removeClass('active');
20906 } else if (!this.el.hasClass('active')) {
20907 this.el.addClass('active');
20910 this.fireEvent('changed', this, state);
20913 onClick : function(e)
20915 e.preventDefault();
20917 if(!this.href.length){
20921 window.location.href = this.href;
20930 onTouchStart : function(e)
20932 this.swiping = false;
20934 this.startX = e.browserEvent.touches[0].clientX;
20935 this.startY = e.browserEvent.touches[0].clientY;
20938 onTouchMove : function(e)
20940 this.swiping = true;
20942 this.endX = e.browserEvent.touches[0].clientX;
20943 this.endY = e.browserEvent.touches[0].clientY;
20946 onTouchEnd : function(e)
20953 var tabGroup = this.parent();
20955 if(this.endX > this.startX){ // swiping right
20956 tabGroup.showPanelPrev();
20960 if(this.startX > this.endX){ // swiping left
20961 tabGroup.showPanelNext();
20980 * @class Roo.bootstrap.DateField
20981 * @extends Roo.bootstrap.Input
20982 * Bootstrap DateField class
20983 * @cfg {Number} weekStart default 0
20984 * @cfg {String} viewMode default empty, (months|years)
20985 * @cfg {String} minViewMode default empty, (months|years)
20986 * @cfg {Number} startDate default -Infinity
20987 * @cfg {Number} endDate default Infinity
20988 * @cfg {Boolean} todayHighlight default false
20989 * @cfg {Boolean} todayBtn default false
20990 * @cfg {Boolean} calendarWeeks default false
20991 * @cfg {Object} daysOfWeekDisabled default empty
20992 * @cfg {Boolean} singleMode default false (true | false)
20994 * @cfg {Boolean} keyboardNavigation default true
20995 * @cfg {String} language default en
20998 * Create a new DateField
20999 * @param {Object} config The config object
21002 Roo.bootstrap.DateField = function(config){
21003 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21007 * Fires when this field show.
21008 * @param {Roo.bootstrap.DateField} this
21009 * @param {Mixed} date The date value
21014 * Fires when this field hide.
21015 * @param {Roo.bootstrap.DateField} this
21016 * @param {Mixed} date The date value
21021 * Fires when select a date.
21022 * @param {Roo.bootstrap.DateField} this
21023 * @param {Mixed} date The date value
21027 * @event beforeselect
21028 * Fires when before select a date.
21029 * @param {Roo.bootstrap.DateField} this
21030 * @param {Mixed} date The date value
21032 beforeselect : true
21036 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21039 * @cfg {String} format
21040 * The default date format string which can be overriden for localization support. The format must be
21041 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21045 * @cfg {String} altFormats
21046 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21047 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21049 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21057 todayHighlight : false,
21063 keyboardNavigation: true,
21065 calendarWeeks: false,
21067 startDate: -Infinity,
21071 daysOfWeekDisabled: [],
21075 singleMode : false,
21077 UTCDate: function()
21079 return new Date(Date.UTC.apply(Date, arguments));
21082 UTCToday: function()
21084 var today = new Date();
21085 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21088 getDate: function() {
21089 var d = this.getUTCDate();
21090 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21093 getUTCDate: function() {
21097 setDate: function(d) {
21098 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21101 setUTCDate: function(d) {
21103 this.setValue(this.formatDate(this.date));
21106 onRender: function(ct, position)
21109 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21111 this.language = this.language || 'en';
21112 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21113 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21115 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21116 this.format = this.format || 'm/d/y';
21117 this.isInline = false;
21118 this.isInput = true;
21119 this.component = this.el.select('.add-on', true).first() || false;
21120 this.component = (this.component && this.component.length === 0) ? false : this.component;
21121 this.hasInput = this.component && this.inputEl().length;
21123 if (typeof(this.minViewMode === 'string')) {
21124 switch (this.minViewMode) {
21126 this.minViewMode = 1;
21129 this.minViewMode = 2;
21132 this.minViewMode = 0;
21137 if (typeof(this.viewMode === 'string')) {
21138 switch (this.viewMode) {
21151 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21153 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21155 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21157 this.picker().on('mousedown', this.onMousedown, this);
21158 this.picker().on('click', this.onClick, this);
21160 this.picker().addClass('datepicker-dropdown');
21162 this.startViewMode = this.viewMode;
21164 if(this.singleMode){
21165 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21166 v.setVisibilityMode(Roo.Element.DISPLAY);
21170 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21171 v.setStyle('width', '189px');
21175 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21176 if(!this.calendarWeeks){
21181 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21182 v.attr('colspan', function(i, val){
21183 return parseInt(val) + 1;
21188 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21190 this.setStartDate(this.startDate);
21191 this.setEndDate(this.endDate);
21193 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21200 if(this.isInline) {
21205 picker : function()
21207 return this.pickerEl;
21208 // return this.el.select('.datepicker', true).first();
21211 fillDow: function()
21213 var dowCnt = this.weekStart;
21222 if(this.calendarWeeks){
21230 while (dowCnt < this.weekStart + 7) {
21234 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21238 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21241 fillMonths: function()
21244 var months = this.picker().select('>.datepicker-months td', true).first();
21246 months.dom.innerHTML = '';
21252 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21255 months.createChild(month);
21262 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;
21264 if (this.date < this.startDate) {
21265 this.viewDate = new Date(this.startDate);
21266 } else if (this.date > this.endDate) {
21267 this.viewDate = new Date(this.endDate);
21269 this.viewDate = new Date(this.date);
21277 var d = new Date(this.viewDate),
21278 year = d.getUTCFullYear(),
21279 month = d.getUTCMonth(),
21280 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21281 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21282 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21283 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21284 currentDate = this.date && this.date.valueOf(),
21285 today = this.UTCToday();
21287 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21289 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21291 // this.picker.select('>tfoot th.today').
21292 // .text(dates[this.language].today)
21293 // .toggle(this.todayBtn !== false);
21295 this.updateNavArrows();
21298 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21300 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21302 prevMonth.setUTCDate(day);
21304 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21306 var nextMonth = new Date(prevMonth);
21308 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21310 nextMonth = nextMonth.valueOf();
21312 var fillMonths = false;
21314 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21316 while(prevMonth.valueOf() <= nextMonth) {
21319 if (prevMonth.getUTCDay() === this.weekStart) {
21321 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21329 if(this.calendarWeeks){
21330 // ISO 8601: First week contains first thursday.
21331 // ISO also states week starts on Monday, but we can be more abstract here.
21333 // Start of current week: based on weekstart/current date
21334 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21335 // Thursday of this week
21336 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21337 // First Thursday of year, year from thursday
21338 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21339 // Calendar week: ms between thursdays, div ms per day, div 7 days
21340 calWeek = (th - yth) / 864e5 / 7 + 1;
21342 fillMonths.cn.push({
21350 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21352 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21355 if (this.todayHighlight &&
21356 prevMonth.getUTCFullYear() == today.getFullYear() &&
21357 prevMonth.getUTCMonth() == today.getMonth() &&
21358 prevMonth.getUTCDate() == today.getDate()) {
21359 clsName += ' today';
21362 if (currentDate && prevMonth.valueOf() === currentDate) {
21363 clsName += ' active';
21366 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21367 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21368 clsName += ' disabled';
21371 fillMonths.cn.push({
21373 cls: 'day ' + clsName,
21374 html: prevMonth.getDate()
21377 prevMonth.setDate(prevMonth.getDate()+1);
21380 var currentYear = this.date && this.date.getUTCFullYear();
21381 var currentMonth = this.date && this.date.getUTCMonth();
21383 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21385 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21386 v.removeClass('active');
21388 if(currentYear === year && k === currentMonth){
21389 v.addClass('active');
21392 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21393 v.addClass('disabled');
21399 year = parseInt(year/10, 10) * 10;
21401 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21403 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21406 for (var i = -1; i < 11; i++) {
21407 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21409 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21417 showMode: function(dir)
21420 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21423 Roo.each(this.picker().select('>div',true).elements, function(v){
21424 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21427 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21432 if(this.isInline) {
21436 this.picker().removeClass(['bottom', 'top']);
21438 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21440 * place to the top of element!
21444 this.picker().addClass('top');
21445 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21450 this.picker().addClass('bottom');
21452 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21455 parseDate : function(value)
21457 if(!value || value instanceof Date){
21460 var v = Date.parseDate(value, this.format);
21461 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21462 v = Date.parseDate(value, 'Y-m-d');
21464 if(!v && this.altFormats){
21465 if(!this.altFormatsArray){
21466 this.altFormatsArray = this.altFormats.split("|");
21468 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21469 v = Date.parseDate(value, this.altFormatsArray[i]);
21475 formatDate : function(date, fmt)
21477 return (!date || !(date instanceof Date)) ?
21478 date : date.dateFormat(fmt || this.format);
21481 onFocus : function()
21483 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21487 onBlur : function()
21489 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21491 var d = this.inputEl().getValue();
21498 showPopup : function()
21500 this.picker().show();
21504 this.fireEvent('showpopup', this, this.date);
21507 hidePopup : function()
21509 if(this.isInline) {
21512 this.picker().hide();
21513 this.viewMode = this.startViewMode;
21516 this.fireEvent('hidepopup', this, this.date);
21520 onMousedown: function(e)
21522 e.stopPropagation();
21523 e.preventDefault();
21528 Roo.bootstrap.DateField.superclass.keyup.call(this);
21532 setValue: function(v)
21534 if(this.fireEvent('beforeselect', this, v) !== false){
21535 var d = new Date(this.parseDate(v) ).clearTime();
21537 if(isNaN(d.getTime())){
21538 this.date = this.viewDate = '';
21539 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21543 v = this.formatDate(d);
21545 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21547 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21551 this.fireEvent('select', this, this.date);
21555 getValue: function()
21557 return this.formatDate(this.date);
21560 fireKey: function(e)
21562 if (!this.picker().isVisible()){
21563 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21569 var dateChanged = false,
21571 newDate, newViewDate;
21576 e.preventDefault();
21580 if (!this.keyboardNavigation) {
21583 dir = e.keyCode == 37 ? -1 : 1;
21586 newDate = this.moveYear(this.date, dir);
21587 newViewDate = this.moveYear(this.viewDate, dir);
21588 } else if (e.shiftKey){
21589 newDate = this.moveMonth(this.date, dir);
21590 newViewDate = this.moveMonth(this.viewDate, dir);
21592 newDate = new Date(this.date);
21593 newDate.setUTCDate(this.date.getUTCDate() + dir);
21594 newViewDate = new Date(this.viewDate);
21595 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21597 if (this.dateWithinRange(newDate)){
21598 this.date = newDate;
21599 this.viewDate = newViewDate;
21600 this.setValue(this.formatDate(this.date));
21602 e.preventDefault();
21603 dateChanged = true;
21608 if (!this.keyboardNavigation) {
21611 dir = e.keyCode == 38 ? -1 : 1;
21613 newDate = this.moveYear(this.date, dir);
21614 newViewDate = this.moveYear(this.viewDate, dir);
21615 } else if (e.shiftKey){
21616 newDate = this.moveMonth(this.date, dir);
21617 newViewDate = this.moveMonth(this.viewDate, dir);
21619 newDate = new Date(this.date);
21620 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21621 newViewDate = new Date(this.viewDate);
21622 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21624 if (this.dateWithinRange(newDate)){
21625 this.date = newDate;
21626 this.viewDate = newViewDate;
21627 this.setValue(this.formatDate(this.date));
21629 e.preventDefault();
21630 dateChanged = true;
21634 this.setValue(this.formatDate(this.date));
21636 e.preventDefault();
21639 this.setValue(this.formatDate(this.date));
21653 onClick: function(e)
21655 e.stopPropagation();
21656 e.preventDefault();
21658 var target = e.getTarget();
21660 if(target.nodeName.toLowerCase() === 'i'){
21661 target = Roo.get(target).dom.parentNode;
21664 var nodeName = target.nodeName;
21665 var className = target.className;
21666 var html = target.innerHTML;
21667 //Roo.log(nodeName);
21669 switch(nodeName.toLowerCase()) {
21671 switch(className) {
21677 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21678 switch(this.viewMode){
21680 this.viewDate = this.moveMonth(this.viewDate, dir);
21684 this.viewDate = this.moveYear(this.viewDate, dir);
21690 var date = new Date();
21691 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21693 this.setValue(this.formatDate(this.date));
21700 if (className.indexOf('disabled') < 0) {
21701 this.viewDate.setUTCDate(1);
21702 if (className.indexOf('month') > -1) {
21703 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21705 var year = parseInt(html, 10) || 0;
21706 this.viewDate.setUTCFullYear(year);
21710 if(this.singleMode){
21711 this.setValue(this.formatDate(this.viewDate));
21722 //Roo.log(className);
21723 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21724 var day = parseInt(html, 10) || 1;
21725 var year = (this.viewDate || new Date()).getUTCFullYear(),
21726 month = (this.viewDate || new Date()).getUTCMonth();
21728 if (className.indexOf('old') > -1) {
21735 } else if (className.indexOf('new') > -1) {
21743 //Roo.log([year,month,day]);
21744 this.date = this.UTCDate(year, month, day,0,0,0,0);
21745 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21747 //Roo.log(this.formatDate(this.date));
21748 this.setValue(this.formatDate(this.date));
21755 setStartDate: function(startDate)
21757 this.startDate = startDate || -Infinity;
21758 if (this.startDate !== -Infinity) {
21759 this.startDate = this.parseDate(this.startDate);
21762 this.updateNavArrows();
21765 setEndDate: function(endDate)
21767 this.endDate = endDate || Infinity;
21768 if (this.endDate !== Infinity) {
21769 this.endDate = this.parseDate(this.endDate);
21772 this.updateNavArrows();
21775 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21777 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21778 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21779 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21781 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21782 return parseInt(d, 10);
21785 this.updateNavArrows();
21788 updateNavArrows: function()
21790 if(this.singleMode){
21794 var d = new Date(this.viewDate),
21795 year = d.getUTCFullYear(),
21796 month = d.getUTCMonth();
21798 Roo.each(this.picker().select('.prev', true).elements, function(v){
21800 switch (this.viewMode) {
21803 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21809 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21816 Roo.each(this.picker().select('.next', true).elements, function(v){
21818 switch (this.viewMode) {
21821 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21827 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21835 moveMonth: function(date, dir)
21840 var new_date = new Date(date.valueOf()),
21841 day = new_date.getUTCDate(),
21842 month = new_date.getUTCMonth(),
21843 mag = Math.abs(dir),
21845 dir = dir > 0 ? 1 : -1;
21848 // If going back one month, make sure month is not current month
21849 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21851 return new_date.getUTCMonth() == month;
21853 // If going forward one month, make sure month is as expected
21854 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21856 return new_date.getUTCMonth() != new_month;
21858 new_month = month + dir;
21859 new_date.setUTCMonth(new_month);
21860 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21861 if (new_month < 0 || new_month > 11) {
21862 new_month = (new_month + 12) % 12;
21865 // For magnitudes >1, move one month at a time...
21866 for (var i=0; i<mag; i++) {
21867 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21868 new_date = this.moveMonth(new_date, dir);
21870 // ...then reset the day, keeping it in the new month
21871 new_month = new_date.getUTCMonth();
21872 new_date.setUTCDate(day);
21874 return new_month != new_date.getUTCMonth();
21877 // Common date-resetting loop -- if date is beyond end of month, make it
21880 new_date.setUTCDate(--day);
21881 new_date.setUTCMonth(new_month);
21886 moveYear: function(date, dir)
21888 return this.moveMonth(date, dir*12);
21891 dateWithinRange: function(date)
21893 return date >= this.startDate && date <= this.endDate;
21899 this.picker().remove();
21902 validateValue : function(value)
21904 if(this.getVisibilityEl().hasClass('hidden')){
21908 if(value.length < 1) {
21909 if(this.allowBlank){
21915 if(value.length < this.minLength){
21918 if(value.length > this.maxLength){
21922 var vt = Roo.form.VTypes;
21923 if(!vt[this.vtype](value, this)){
21927 if(typeof this.validator == "function"){
21928 var msg = this.validator(value);
21934 if(this.regex && !this.regex.test(value)){
21938 if(typeof(this.parseDate(value)) == 'undefined'){
21942 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21946 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21956 this.date = this.viewDate = '';
21958 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21963 Roo.apply(Roo.bootstrap.DateField, {
21974 html: '<i class="fa fa-arrow-left"/>'
21984 html: '<i class="fa fa-arrow-right"/>'
22026 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22027 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22028 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22029 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22030 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22043 navFnc: 'FullYear',
22048 navFnc: 'FullYear',
22053 Roo.apply(Roo.bootstrap.DateField, {
22057 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22061 cls: 'datepicker-days',
22065 cls: 'table-condensed',
22067 Roo.bootstrap.DateField.head,
22071 Roo.bootstrap.DateField.footer
22078 cls: 'datepicker-months',
22082 cls: 'table-condensed',
22084 Roo.bootstrap.DateField.head,
22085 Roo.bootstrap.DateField.content,
22086 Roo.bootstrap.DateField.footer
22093 cls: 'datepicker-years',
22097 cls: 'table-condensed',
22099 Roo.bootstrap.DateField.head,
22100 Roo.bootstrap.DateField.content,
22101 Roo.bootstrap.DateField.footer
22120 * @class Roo.bootstrap.TimeField
22121 * @extends Roo.bootstrap.Input
22122 * Bootstrap DateField class
22126 * Create a new TimeField
22127 * @param {Object} config The config object
22130 Roo.bootstrap.TimeField = function(config){
22131 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22135 * Fires when this field show.
22136 * @param {Roo.bootstrap.DateField} thisthis
22137 * @param {Mixed} date The date value
22142 * Fires when this field hide.
22143 * @param {Roo.bootstrap.DateField} this
22144 * @param {Mixed} date The date value
22149 * Fires when select a date.
22150 * @param {Roo.bootstrap.DateField} this
22151 * @param {Mixed} date The date value
22157 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22160 * @cfg {String} format
22161 * The default time format string which can be overriden for localization support. The format must be
22162 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22166 getAutoCreate : function()
22168 this.after = '<i class="fa far fa-clock"></i>';
22169 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22173 onRender: function(ct, position)
22176 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22178 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22180 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22182 this.pop = this.picker().select('>.datepicker-time',true).first();
22183 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22185 this.picker().on('mousedown', this.onMousedown, this);
22186 this.picker().on('click', this.onClick, this);
22188 this.picker().addClass('datepicker-dropdown');
22193 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22194 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22195 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22196 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22197 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22198 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22202 fireKey: function(e){
22203 if (!this.picker().isVisible()){
22204 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22210 e.preventDefault();
22218 this.onTogglePeriod();
22221 this.onIncrementMinutes();
22224 this.onDecrementMinutes();
22233 onClick: function(e) {
22234 e.stopPropagation();
22235 e.preventDefault();
22238 picker : function()
22240 return this.pickerEl;
22243 fillTime: function()
22245 var time = this.pop.select('tbody', true).first();
22247 time.dom.innerHTML = '';
22262 cls: 'hours-up fa fas fa-chevron-up'
22282 cls: 'minutes-up fa fas fa-chevron-up'
22303 cls: 'timepicker-hour',
22318 cls: 'timepicker-minute',
22333 cls: 'btn btn-primary period',
22355 cls: 'hours-down fa fas fa-chevron-down'
22375 cls: 'minutes-down fa fas fa-chevron-down'
22393 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22400 var hours = this.time.getHours();
22401 var minutes = this.time.getMinutes();
22414 hours = hours - 12;
22418 hours = '0' + hours;
22422 minutes = '0' + minutes;
22425 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22426 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22427 this.pop.select('button', true).first().dom.innerHTML = period;
22433 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22435 var cls = ['bottom'];
22437 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22444 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22448 //this.picker().setXY(20000,20000);
22449 this.picker().addClass(cls.join('-'));
22453 Roo.each(cls, function(c){
22458 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22459 //_this.picker().setTop(_this.inputEl().getHeight());
22463 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22465 //_this.picker().setTop(0 - _this.picker().getHeight());
22470 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22474 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22482 onFocus : function()
22484 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22488 onBlur : function()
22490 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22496 this.picker().show();
22501 this.fireEvent('show', this, this.date);
22506 this.picker().hide();
22509 this.fireEvent('hide', this, this.date);
22512 setTime : function()
22515 this.setValue(this.time.format(this.format));
22517 this.fireEvent('select', this, this.date);
22522 onMousedown: function(e){
22523 e.stopPropagation();
22524 e.preventDefault();
22527 onIncrementHours: function()
22529 Roo.log('onIncrementHours');
22530 this.time = this.time.add(Date.HOUR, 1);
22535 onDecrementHours: function()
22537 Roo.log('onDecrementHours');
22538 this.time = this.time.add(Date.HOUR, -1);
22542 onIncrementMinutes: function()
22544 Roo.log('onIncrementMinutes');
22545 this.time = this.time.add(Date.MINUTE, 1);
22549 onDecrementMinutes: function()
22551 Roo.log('onDecrementMinutes');
22552 this.time = this.time.add(Date.MINUTE, -1);
22556 onTogglePeriod: function()
22558 Roo.log('onTogglePeriod');
22559 this.time = this.time.add(Date.HOUR, 12);
22567 Roo.apply(Roo.bootstrap.TimeField, {
22571 cls: 'datepicker dropdown-menu',
22575 cls: 'datepicker-time',
22579 cls: 'table-condensed',
22608 cls: 'btn btn-info ok',
22636 * @class Roo.bootstrap.MonthField
22637 * @extends Roo.bootstrap.Input
22638 * Bootstrap MonthField class
22640 * @cfg {String} language default en
22643 * Create a new MonthField
22644 * @param {Object} config The config object
22647 Roo.bootstrap.MonthField = function(config){
22648 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22653 * Fires when this field show.
22654 * @param {Roo.bootstrap.MonthField} this
22655 * @param {Mixed} date The date value
22660 * Fires when this field hide.
22661 * @param {Roo.bootstrap.MonthField} this
22662 * @param {Mixed} date The date value
22667 * Fires when select a date.
22668 * @param {Roo.bootstrap.MonthField} this
22669 * @param {String} oldvalue The old value
22670 * @param {String} newvalue The new value
22676 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22678 onRender: function(ct, position)
22681 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22683 this.language = this.language || 'en';
22684 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22685 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22687 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22688 this.isInline = false;
22689 this.isInput = true;
22690 this.component = this.el.select('.add-on', true).first() || false;
22691 this.component = (this.component && this.component.length === 0) ? false : this.component;
22692 this.hasInput = this.component && this.inputEL().length;
22694 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22696 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22698 this.picker().on('mousedown', this.onMousedown, this);
22699 this.picker().on('click', this.onClick, this);
22701 this.picker().addClass('datepicker-dropdown');
22703 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22704 v.setStyle('width', '189px');
22711 if(this.isInline) {
22717 setValue: function(v, suppressEvent)
22719 var o = this.getValue();
22721 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22725 if(suppressEvent !== true){
22726 this.fireEvent('select', this, o, v);
22731 getValue: function()
22736 onClick: function(e)
22738 e.stopPropagation();
22739 e.preventDefault();
22741 var target = e.getTarget();
22743 if(target.nodeName.toLowerCase() === 'i'){
22744 target = Roo.get(target).dom.parentNode;
22747 var nodeName = target.nodeName;
22748 var className = target.className;
22749 var html = target.innerHTML;
22751 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22755 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22757 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22763 picker : function()
22765 return this.pickerEl;
22768 fillMonths: function()
22771 var months = this.picker().select('>.datepicker-months td', true).first();
22773 months.dom.innerHTML = '';
22779 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22782 months.createChild(month);
22791 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22792 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22795 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22796 e.removeClass('active');
22798 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22799 e.addClass('active');
22806 if(this.isInline) {
22810 this.picker().removeClass(['bottom', 'top']);
22812 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22814 * place to the top of element!
22818 this.picker().addClass('top');
22819 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22824 this.picker().addClass('bottom');
22826 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22829 onFocus : function()
22831 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22835 onBlur : function()
22837 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22839 var d = this.inputEl().getValue();
22848 this.picker().show();
22849 this.picker().select('>.datepicker-months', true).first().show();
22853 this.fireEvent('show', this, this.date);
22858 if(this.isInline) {
22861 this.picker().hide();
22862 this.fireEvent('hide', this, this.date);
22866 onMousedown: function(e)
22868 e.stopPropagation();
22869 e.preventDefault();
22874 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22878 fireKey: function(e)
22880 if (!this.picker().isVisible()){
22881 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22892 e.preventDefault();
22896 dir = e.keyCode == 37 ? -1 : 1;
22898 this.vIndex = this.vIndex + dir;
22900 if(this.vIndex < 0){
22904 if(this.vIndex > 11){
22908 if(isNaN(this.vIndex)){
22912 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22918 dir = e.keyCode == 38 ? -1 : 1;
22920 this.vIndex = this.vIndex + dir * 4;
22922 if(this.vIndex < 0){
22926 if(this.vIndex > 11){
22930 if(isNaN(this.vIndex)){
22934 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22939 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22940 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22944 e.preventDefault();
22947 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22948 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22964 this.picker().remove();
22969 Roo.apply(Roo.bootstrap.MonthField, {
22988 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22989 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22994 Roo.apply(Roo.bootstrap.MonthField, {
22998 cls: 'datepicker dropdown-menu roo-dynamic',
23002 cls: 'datepicker-months',
23006 cls: 'table-condensed',
23008 Roo.bootstrap.DateField.content
23028 * @class Roo.bootstrap.CheckBox
23029 * @extends Roo.bootstrap.Input
23030 * Bootstrap CheckBox class
23032 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23033 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23034 * @cfg {String} boxLabel The text that appears beside the checkbox
23035 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23036 * @cfg {Boolean} checked initnal the element
23037 * @cfg {Boolean} inline inline the element (default false)
23038 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23039 * @cfg {String} tooltip label tooltip
23042 * Create a new CheckBox
23043 * @param {Object} config The config object
23046 Roo.bootstrap.CheckBox = function(config){
23047 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23052 * Fires when the element is checked or unchecked.
23053 * @param {Roo.bootstrap.CheckBox} this This input
23054 * @param {Boolean} checked The new checked value
23059 * Fires when the element is click.
23060 * @param {Roo.bootstrap.CheckBox} this This input
23067 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23069 inputType: 'checkbox',
23078 // checkbox success does not make any sense really..
23083 getAutoCreate : function()
23085 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23091 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23094 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23100 type : this.inputType,
23101 value : this.inputValue,
23102 cls : 'roo-' + this.inputType, //'form-box',
23103 placeholder : this.placeholder || ''
23107 if(this.inputType != 'radio'){
23111 cls : 'roo-hidden-value',
23112 value : this.checked ? this.inputValue : this.valueOff
23117 if (this.weight) { // Validity check?
23118 cfg.cls += " " + this.inputType + "-" + this.weight;
23121 if (this.disabled) {
23122 input.disabled=true;
23126 input.checked = this.checked;
23131 input.name = this.name;
23133 if(this.inputType != 'radio'){
23134 hidden.name = this.name;
23135 input.name = '_hidden_' + this.name;
23140 input.cls += ' input-' + this.size;
23145 ['xs','sm','md','lg'].map(function(size){
23146 if (settings[size]) {
23147 cfg.cls += ' col-' + size + '-' + settings[size];
23151 var inputblock = input;
23153 if (this.before || this.after) {
23156 cls : 'input-group',
23161 inputblock.cn.push({
23163 cls : 'input-group-addon',
23168 inputblock.cn.push(input);
23170 if(this.inputType != 'radio'){
23171 inputblock.cn.push(hidden);
23175 inputblock.cn.push({
23177 cls : 'input-group-addon',
23183 var boxLabelCfg = false;
23189 //'for': id, // box label is handled by onclick - so no for...
23191 html: this.boxLabel
23194 boxLabelCfg.tooltip = this.tooltip;
23200 if (align ==='left' && this.fieldLabel.length) {
23201 // Roo.log("left and has label");
23206 cls : 'control-label',
23207 html : this.fieldLabel
23218 cfg.cn[1].cn.push(boxLabelCfg);
23221 if(this.labelWidth > 12){
23222 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23225 if(this.labelWidth < 13 && this.labelmd == 0){
23226 this.labelmd = this.labelWidth;
23229 if(this.labellg > 0){
23230 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23231 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23234 if(this.labelmd > 0){
23235 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23236 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23239 if(this.labelsm > 0){
23240 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23241 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23244 if(this.labelxs > 0){
23245 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23246 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23249 } else if ( this.fieldLabel.length) {
23250 // Roo.log(" label");
23254 tag: this.boxLabel ? 'span' : 'label',
23256 cls: 'control-label box-input-label',
23257 //cls : 'input-group-addon',
23258 html : this.fieldLabel
23265 cfg.cn.push(boxLabelCfg);
23270 // Roo.log(" no label && no align");
23271 cfg.cn = [ inputblock ] ;
23273 cfg.cn.push(boxLabelCfg);
23281 if(this.inputType != 'radio'){
23282 cfg.cn.push(hidden);
23290 * return the real input element.
23292 inputEl: function ()
23294 return this.el.select('input.roo-' + this.inputType,true).first();
23296 hiddenEl: function ()
23298 return this.el.select('input.roo-hidden-value',true).first();
23301 labelEl: function()
23303 return this.el.select('label.control-label',true).first();
23305 /* depricated... */
23309 return this.labelEl();
23312 boxLabelEl: function()
23314 return this.el.select('label.box-label',true).first();
23317 initEvents : function()
23319 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23321 this.inputEl().on('click', this.onClick, this);
23323 if (this.boxLabel) {
23324 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23327 this.startValue = this.getValue();
23330 Roo.bootstrap.CheckBox.register(this);
23334 onClick : function(e)
23336 if(this.fireEvent('click', this, e) !== false){
23337 this.setChecked(!this.checked);
23342 setChecked : function(state,suppressEvent)
23344 this.startValue = this.getValue();
23346 if(this.inputType == 'radio'){
23348 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23349 e.dom.checked = false;
23352 this.inputEl().dom.checked = true;
23354 this.inputEl().dom.value = this.inputValue;
23356 if(suppressEvent !== true){
23357 this.fireEvent('check', this, true);
23365 this.checked = state;
23367 this.inputEl().dom.checked = state;
23370 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23372 if(suppressEvent !== true){
23373 this.fireEvent('check', this, state);
23379 getValue : function()
23381 if(this.inputType == 'radio'){
23382 return this.getGroupValue();
23385 return this.hiddenEl().dom.value;
23389 getGroupValue : function()
23391 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23395 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23398 setValue : function(v,suppressEvent)
23400 if(this.inputType == 'radio'){
23401 this.setGroupValue(v, suppressEvent);
23405 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23410 setGroupValue : function(v, suppressEvent)
23412 this.startValue = this.getValue();
23414 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23415 e.dom.checked = false;
23417 if(e.dom.value == v){
23418 e.dom.checked = true;
23422 if(suppressEvent !== true){
23423 this.fireEvent('check', this, true);
23431 validate : function()
23433 if(this.getVisibilityEl().hasClass('hidden')){
23439 (this.inputType == 'radio' && this.validateRadio()) ||
23440 (this.inputType == 'checkbox' && this.validateCheckbox())
23446 this.markInvalid();
23450 validateRadio : function()
23452 if(this.getVisibilityEl().hasClass('hidden')){
23456 if(this.allowBlank){
23462 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23463 if(!e.dom.checked){
23475 validateCheckbox : function()
23478 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23479 //return (this.getValue() == this.inputValue) ? true : false;
23482 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23490 for(var i in group){
23491 if(group[i].el.isVisible(true)){
23499 for(var i in group){
23504 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23511 * Mark this field as valid
23513 markValid : function()
23517 this.fireEvent('valid', this);
23519 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23522 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23529 if(this.inputType == 'radio'){
23530 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23531 var fg = e.findParent('.form-group', false, true);
23532 if (Roo.bootstrap.version == 3) {
23533 fg.removeClass([_this.invalidClass, _this.validClass]);
23534 fg.addClass(_this.validClass);
23536 fg.removeClass(['is-valid', 'is-invalid']);
23537 fg.addClass('is-valid');
23545 var fg = this.el.findParent('.form-group', false, true);
23546 if (Roo.bootstrap.version == 3) {
23547 fg.removeClass([this.invalidClass, this.validClass]);
23548 fg.addClass(this.validClass);
23550 fg.removeClass(['is-valid', 'is-invalid']);
23551 fg.addClass('is-valid');
23556 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23562 for(var i in group){
23563 var fg = group[i].el.findParent('.form-group', false, true);
23564 if (Roo.bootstrap.version == 3) {
23565 fg.removeClass([this.invalidClass, this.validClass]);
23566 fg.addClass(this.validClass);
23568 fg.removeClass(['is-valid', 'is-invalid']);
23569 fg.addClass('is-valid');
23575 * Mark this field as invalid
23576 * @param {String} msg The validation message
23578 markInvalid : function(msg)
23580 if(this.allowBlank){
23586 this.fireEvent('invalid', this, msg);
23588 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23591 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23595 label.markInvalid();
23598 if(this.inputType == 'radio'){
23600 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23601 var fg = e.findParent('.form-group', false, true);
23602 if (Roo.bootstrap.version == 3) {
23603 fg.removeClass([_this.invalidClass, _this.validClass]);
23604 fg.addClass(_this.invalidClass);
23606 fg.removeClass(['is-invalid', 'is-valid']);
23607 fg.addClass('is-invalid');
23615 var fg = this.el.findParent('.form-group', false, true);
23616 if (Roo.bootstrap.version == 3) {
23617 fg.removeClass([_this.invalidClass, _this.validClass]);
23618 fg.addClass(_this.invalidClass);
23620 fg.removeClass(['is-invalid', 'is-valid']);
23621 fg.addClass('is-invalid');
23626 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23632 for(var i in group){
23633 var fg = group[i].el.findParent('.form-group', false, true);
23634 if (Roo.bootstrap.version == 3) {
23635 fg.removeClass([_this.invalidClass, _this.validClass]);
23636 fg.addClass(_this.invalidClass);
23638 fg.removeClass(['is-invalid', 'is-valid']);
23639 fg.addClass('is-invalid');
23645 clearInvalid : function()
23647 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23649 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23651 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23653 if (label && label.iconEl) {
23654 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23655 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23659 disable : function()
23661 if(this.inputType != 'radio'){
23662 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23669 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23670 _this.getActionEl().addClass(this.disabledClass);
23671 e.dom.disabled = true;
23675 this.disabled = true;
23676 this.fireEvent("disable", this);
23680 enable : function()
23682 if(this.inputType != 'radio'){
23683 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23690 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23691 _this.getActionEl().removeClass(this.disabledClass);
23692 e.dom.disabled = false;
23696 this.disabled = false;
23697 this.fireEvent("enable", this);
23701 setBoxLabel : function(v)
23706 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23712 Roo.apply(Roo.bootstrap.CheckBox, {
23717 * register a CheckBox Group
23718 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23720 register : function(checkbox)
23722 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23723 this.groups[checkbox.groupId] = {};
23726 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23730 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23734 * fetch a CheckBox Group based on the group ID
23735 * @param {string} the group ID
23736 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23738 get: function(groupId) {
23739 if (typeof(this.groups[groupId]) == 'undefined') {
23743 return this.groups[groupId] ;
23756 * @class Roo.bootstrap.Radio
23757 * @extends Roo.bootstrap.Component
23758 * Bootstrap Radio class
23759 * @cfg {String} boxLabel - the label associated
23760 * @cfg {String} value - the value of radio
23763 * Create a new Radio
23764 * @param {Object} config The config object
23766 Roo.bootstrap.Radio = function(config){
23767 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23771 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23777 getAutoCreate : function()
23781 cls : 'form-group radio',
23786 html : this.boxLabel
23794 initEvents : function()
23796 this.parent().register(this);
23798 this.el.on('click', this.onClick, this);
23802 onClick : function(e)
23804 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23805 this.setChecked(true);
23809 setChecked : function(state, suppressEvent)
23811 this.parent().setValue(this.value, suppressEvent);
23815 setBoxLabel : function(v)
23820 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23835 * @class Roo.bootstrap.SecurePass
23836 * @extends Roo.bootstrap.Input
23837 * Bootstrap SecurePass class
23841 * Create a new SecurePass
23842 * @param {Object} config The config object
23845 Roo.bootstrap.SecurePass = function (config) {
23846 // these go here, so the translation tool can replace them..
23848 PwdEmpty: "Please type a password, and then retype it to confirm.",
23849 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23850 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23851 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23852 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23853 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23854 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23855 TooWeak: "Your password is Too Weak."
23857 this.meterLabel = "Password strength:";
23858 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23859 this.meterClass = [
23860 "roo-password-meter-tooweak",
23861 "roo-password-meter-weak",
23862 "roo-password-meter-medium",
23863 "roo-password-meter-strong",
23864 "roo-password-meter-grey"
23869 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23872 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23874 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23876 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23877 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23878 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23879 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23880 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23881 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23882 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23892 * @cfg {String/Object} Label for the strength meter (defaults to
23893 * 'Password strength:')
23898 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23899 * ['Weak', 'Medium', 'Strong'])
23902 pwdStrengths: false,
23915 initEvents: function ()
23917 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23919 if (this.el.is('input[type=password]') && Roo.isSafari) {
23920 this.el.on('keydown', this.SafariOnKeyDown, this);
23923 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23926 onRender: function (ct, position)
23928 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23929 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23930 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23932 this.trigger.createChild({
23937 cls: 'roo-password-meter-grey col-xs-12',
23940 //width: this.meterWidth + 'px'
23944 cls: 'roo-password-meter-text'
23950 if (this.hideTrigger) {
23951 this.trigger.setDisplayed(false);
23953 this.setSize(this.width || '', this.height || '');
23956 onDestroy: function ()
23958 if (this.trigger) {
23959 this.trigger.removeAllListeners();
23960 this.trigger.remove();
23963 this.wrap.remove();
23965 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23968 checkStrength: function ()
23970 var pwd = this.inputEl().getValue();
23971 if (pwd == this._lastPwd) {
23976 if (this.ClientSideStrongPassword(pwd)) {
23978 } else if (this.ClientSideMediumPassword(pwd)) {
23980 } else if (this.ClientSideWeakPassword(pwd)) {
23986 Roo.log('strength1: ' + strength);
23988 //var pm = this.trigger.child('div/div/div').dom;
23989 var pm = this.trigger.child('div/div');
23990 pm.removeClass(this.meterClass);
23991 pm.addClass(this.meterClass[strength]);
23994 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23996 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23998 this._lastPwd = pwd;
24002 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24004 this._lastPwd = '';
24006 var pm = this.trigger.child('div/div');
24007 pm.removeClass(this.meterClass);
24008 pm.addClass('roo-password-meter-grey');
24011 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24014 this.inputEl().dom.type='password';
24017 validateValue: function (value)
24019 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24022 if (value.length == 0) {
24023 if (this.allowBlank) {
24024 this.clearInvalid();
24028 this.markInvalid(this.errors.PwdEmpty);
24029 this.errorMsg = this.errors.PwdEmpty;
24037 if (!value.match(/[\x21-\x7e]+/)) {
24038 this.markInvalid(this.errors.PwdBadChar);
24039 this.errorMsg = this.errors.PwdBadChar;
24042 if (value.length < 6) {
24043 this.markInvalid(this.errors.PwdShort);
24044 this.errorMsg = this.errors.PwdShort;
24047 if (value.length > 16) {
24048 this.markInvalid(this.errors.PwdLong);
24049 this.errorMsg = this.errors.PwdLong;
24053 if (this.ClientSideStrongPassword(value)) {
24055 } else if (this.ClientSideMediumPassword(value)) {
24057 } else if (this.ClientSideWeakPassword(value)) {
24064 if (strength < 2) {
24065 //this.markInvalid(this.errors.TooWeak);
24066 this.errorMsg = this.errors.TooWeak;
24071 console.log('strength2: ' + strength);
24073 //var pm = this.trigger.child('div/div/div').dom;
24075 var pm = this.trigger.child('div/div');
24076 pm.removeClass(this.meterClass);
24077 pm.addClass(this.meterClass[strength]);
24079 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24081 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24083 this.errorMsg = '';
24087 CharacterSetChecks: function (type)
24090 this.fResult = false;
24093 isctype: function (character, type)
24096 case this.kCapitalLetter:
24097 if (character >= 'A' && character <= 'Z') {
24102 case this.kSmallLetter:
24103 if (character >= 'a' && character <= 'z') {
24109 if (character >= '0' && character <= '9') {
24114 case this.kPunctuation:
24115 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24126 IsLongEnough: function (pwd, size)
24128 return !(pwd == null || isNaN(size) || pwd.length < size);
24131 SpansEnoughCharacterSets: function (word, nb)
24133 if (!this.IsLongEnough(word, nb))
24138 var characterSetChecks = new Array(
24139 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24140 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24143 for (var index = 0; index < word.length; ++index) {
24144 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24145 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24146 characterSetChecks[nCharSet].fResult = true;
24153 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24154 if (characterSetChecks[nCharSet].fResult) {
24159 if (nCharSets < nb) {
24165 ClientSideStrongPassword: function (pwd)
24167 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24170 ClientSideMediumPassword: function (pwd)
24172 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24175 ClientSideWeakPassword: function (pwd)
24177 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24180 })//<script type="text/javascript">
24183 * Based Ext JS Library 1.1.1
24184 * Copyright(c) 2006-2007, Ext JS, LLC.
24190 * @class Roo.HtmlEditorCore
24191 * @extends Roo.Component
24192 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24194 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24197 Roo.HtmlEditorCore = function(config){
24200 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24205 * @event initialize
24206 * Fires when the editor is fully initialized (including the iframe)
24207 * @param {Roo.HtmlEditorCore} this
24212 * Fires when the editor is first receives the focus. Any insertion must wait
24213 * until after this event.
24214 * @param {Roo.HtmlEditorCore} this
24218 * @event beforesync
24219 * Fires before the textarea is updated with content from the editor iframe. Return false
24220 * to cancel the sync.
24221 * @param {Roo.HtmlEditorCore} this
24222 * @param {String} html
24226 * @event beforepush
24227 * Fires before the iframe editor is updated with content from the textarea. Return false
24228 * to cancel the push.
24229 * @param {Roo.HtmlEditorCore} this
24230 * @param {String} html
24235 * Fires when the textarea is updated with content from the editor iframe.
24236 * @param {Roo.HtmlEditorCore} this
24237 * @param {String} html
24242 * Fires when the iframe editor is updated with content from the textarea.
24243 * @param {Roo.HtmlEditorCore} this
24244 * @param {String} html
24249 * @event editorevent
24250 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24251 * @param {Roo.HtmlEditorCore} this
24257 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24259 // defaults : white / black...
24260 this.applyBlacklists();
24267 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24271 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24277 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24282 * @cfg {Number} height (in pixels)
24286 * @cfg {Number} width (in pixels)
24291 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24294 stylesheets: false,
24299 // private properties
24300 validationEvent : false,
24302 initialized : false,
24304 sourceEditMode : false,
24305 onFocus : Roo.emptyFn,
24307 hideMode:'offsets',
24311 // blacklist + whitelisted elements..
24318 * Protected method that will not generally be called directly. It
24319 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24320 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24322 getDocMarkup : function(){
24326 // inherit styels from page...??
24327 if (this.stylesheets === false) {
24329 Roo.get(document.head).select('style').each(function(node) {
24330 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24333 Roo.get(document.head).select('link').each(function(node) {
24334 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24337 } else if (!this.stylesheets.length) {
24339 st = '<style type="text/css">' +
24340 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24343 for (var i in this.stylesheets) {
24344 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24349 st += '<style type="text/css">' +
24350 'IMG { cursor: pointer } ' +
24353 var cls = 'roo-htmleditor-body';
24355 if(this.bodyCls.length){
24356 cls += ' ' + this.bodyCls;
24359 return '<html><head>' + st +
24360 //<style type="text/css">' +
24361 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24363 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24367 onRender : function(ct, position)
24370 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24371 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24374 this.el.dom.style.border = '0 none';
24375 this.el.dom.setAttribute('tabIndex', -1);
24376 this.el.addClass('x-hidden hide');
24380 if(Roo.isIE){ // fix IE 1px bogus margin
24381 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24385 this.frameId = Roo.id();
24389 var iframe = this.owner.wrap.createChild({
24391 cls: 'form-control', // bootstrap..
24393 name: this.frameId,
24394 frameBorder : 'no',
24395 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24400 this.iframe = iframe.dom;
24402 this.assignDocWin();
24404 this.doc.designMode = 'on';
24407 this.doc.write(this.getDocMarkup());
24411 var task = { // must defer to wait for browser to be ready
24413 //console.log("run task?" + this.doc.readyState);
24414 this.assignDocWin();
24415 if(this.doc.body || this.doc.readyState == 'complete'){
24417 this.doc.designMode="on";
24421 Roo.TaskMgr.stop(task);
24422 this.initEditor.defer(10, this);
24429 Roo.TaskMgr.start(task);
24434 onResize : function(w, h)
24436 Roo.log('resize: ' +w + ',' + h );
24437 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24441 if(typeof w == 'number'){
24443 this.iframe.style.width = w + 'px';
24445 if(typeof h == 'number'){
24447 this.iframe.style.height = h + 'px';
24449 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24456 * Toggles the editor between standard and source edit mode.
24457 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24459 toggleSourceEdit : function(sourceEditMode){
24461 this.sourceEditMode = sourceEditMode === true;
24463 if(this.sourceEditMode){
24465 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24468 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24469 //this.iframe.className = '';
24472 //this.setSize(this.owner.wrap.getSize());
24473 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24480 * Protected method that will not generally be called directly. If you need/want
24481 * custom HTML cleanup, this is the method you should override.
24482 * @param {String} html The HTML to be cleaned
24483 * return {String} The cleaned HTML
24485 cleanHtml : function(html){
24486 html = String(html);
24487 if(html.length > 5){
24488 if(Roo.isSafari){ // strip safari nonsense
24489 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24492 if(html == ' '){
24499 * HTML Editor -> Textarea
24500 * Protected method that will not generally be called directly. Syncs the contents
24501 * of the editor iframe with the textarea.
24503 syncValue : function(){
24504 if(this.initialized){
24505 var bd = (this.doc.body || this.doc.documentElement);
24506 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24507 var html = bd.innerHTML;
24509 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24510 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24512 html = '<div style="'+m[0]+'">' + html + '</div>';
24515 html = this.cleanHtml(html);
24516 // fix up the special chars.. normaly like back quotes in word...
24517 // however we do not want to do this with chinese..
24518 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24520 var cc = match.charCodeAt();
24522 // Get the character value, handling surrogate pairs
24523 if (match.length == 2) {
24524 // It's a surrogate pair, calculate the Unicode code point
24525 var high = match.charCodeAt(0) - 0xD800;
24526 var low = match.charCodeAt(1) - 0xDC00;
24527 cc = (high * 0x400) + low + 0x10000;
24529 (cc >= 0x4E00 && cc < 0xA000 ) ||
24530 (cc >= 0x3400 && cc < 0x4E00 ) ||
24531 (cc >= 0xf900 && cc < 0xfb00 )
24536 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24537 return "&#" + cc + ";";
24544 if(this.owner.fireEvent('beforesync', this, html) !== false){
24545 this.el.dom.value = html;
24546 this.owner.fireEvent('sync', this, html);
24552 * Protected method that will not generally be called directly. Pushes the value of the textarea
24553 * into the iframe editor.
24555 pushValue : function(){
24556 if(this.initialized){
24557 var v = this.el.dom.value.trim();
24559 // if(v.length < 1){
24563 if(this.owner.fireEvent('beforepush', this, v) !== false){
24564 var d = (this.doc.body || this.doc.documentElement);
24566 this.cleanUpPaste();
24567 this.el.dom.value = d.innerHTML;
24568 this.owner.fireEvent('push', this, v);
24574 deferFocus : function(){
24575 this.focus.defer(10, this);
24579 focus : function(){
24580 if(this.win && !this.sourceEditMode){
24587 assignDocWin: function()
24589 var iframe = this.iframe;
24592 this.doc = iframe.contentWindow.document;
24593 this.win = iframe.contentWindow;
24595 // if (!Roo.get(this.frameId)) {
24598 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24599 // this.win = Roo.get(this.frameId).dom.contentWindow;
24601 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24605 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24606 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24611 initEditor : function(){
24612 //console.log("INIT EDITOR");
24613 this.assignDocWin();
24617 this.doc.designMode="on";
24619 this.doc.write(this.getDocMarkup());
24622 var dbody = (this.doc.body || this.doc.documentElement);
24623 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24624 // this copies styles from the containing element into thsi one..
24625 // not sure why we need all of this..
24626 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24628 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24629 //ss['background-attachment'] = 'fixed'; // w3c
24630 dbody.bgProperties = 'fixed'; // ie
24631 //Roo.DomHelper.applyStyles(dbody, ss);
24632 Roo.EventManager.on(this.doc, {
24633 //'mousedown': this.onEditorEvent,
24634 'mouseup': this.onEditorEvent,
24635 'dblclick': this.onEditorEvent,
24636 'click': this.onEditorEvent,
24637 'keyup': this.onEditorEvent,
24642 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24644 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24645 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24647 this.initialized = true;
24649 this.owner.fireEvent('initialize', this);
24654 onDestroy : function(){
24660 //for (var i =0; i < this.toolbars.length;i++) {
24661 // // fixme - ask toolbars for heights?
24662 // this.toolbars[i].onDestroy();
24665 //this.wrap.dom.innerHTML = '';
24666 //this.wrap.remove();
24671 onFirstFocus : function(){
24673 this.assignDocWin();
24676 this.activated = true;
24679 if(Roo.isGecko){ // prevent silly gecko errors
24681 var s = this.win.getSelection();
24682 if(!s.focusNode || s.focusNode.nodeType != 3){
24683 var r = s.getRangeAt(0);
24684 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24689 this.execCmd('useCSS', true);
24690 this.execCmd('styleWithCSS', false);
24693 this.owner.fireEvent('activate', this);
24697 adjustFont: function(btn){
24698 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24699 //if(Roo.isSafari){ // safari
24702 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24703 if(Roo.isSafari){ // safari
24704 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24705 v = (v < 10) ? 10 : v;
24706 v = (v > 48) ? 48 : v;
24707 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24712 v = Math.max(1, v+adjust);
24714 this.execCmd('FontSize', v );
24717 onEditorEvent : function(e)
24719 this.owner.fireEvent('editorevent', this, e);
24720 // this.updateToolbar();
24721 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24724 insertTag : function(tg)
24726 // could be a bit smarter... -> wrap the current selected tRoo..
24727 if (tg.toLowerCase() == 'span' ||
24728 tg.toLowerCase() == 'code' ||
24729 tg.toLowerCase() == 'sup' ||
24730 tg.toLowerCase() == 'sub'
24733 range = this.createRange(this.getSelection());
24734 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24735 wrappingNode.appendChild(range.extractContents());
24736 range.insertNode(wrappingNode);
24743 this.execCmd("formatblock", tg);
24747 insertText : function(txt)
24751 var range = this.createRange();
24752 range.deleteContents();
24753 //alert(Sender.getAttribute('label'));
24755 range.insertNode(this.doc.createTextNode(txt));
24761 * Executes a Midas editor command on the editor document and performs necessary focus and
24762 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24763 * @param {String} cmd The Midas command
24764 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24766 relayCmd : function(cmd, value){
24768 this.execCmd(cmd, value);
24769 this.owner.fireEvent('editorevent', this);
24770 //this.updateToolbar();
24771 this.owner.deferFocus();
24775 * Executes a Midas editor command directly on the editor document.
24776 * For visual commands, you should use {@link #relayCmd} instead.
24777 * <b>This should only be called after the editor is initialized.</b>
24778 * @param {String} cmd The Midas command
24779 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24781 execCmd : function(cmd, value){
24782 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24789 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24791 * @param {String} text | dom node..
24793 insertAtCursor : function(text)
24796 if(!this.activated){
24802 var r = this.doc.selection.createRange();
24813 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24817 // from jquery ui (MIT licenced)
24819 var win = this.win;
24821 if (win.getSelection && win.getSelection().getRangeAt) {
24822 range = win.getSelection().getRangeAt(0);
24823 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24824 range.insertNode(node);
24825 } else if (win.document.selection && win.document.selection.createRange) {
24826 // no firefox support
24827 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24828 win.document.selection.createRange().pasteHTML(txt);
24830 // no firefox support
24831 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24832 this.execCmd('InsertHTML', txt);
24841 mozKeyPress : function(e){
24843 var c = e.getCharCode(), cmd;
24846 c = String.fromCharCode(c).toLowerCase();
24860 this.cleanUpPaste.defer(100, this);
24868 e.preventDefault();
24876 fixKeys : function(){ // load time branching for fastest keydown performance
24878 return function(e){
24879 var k = e.getKey(), r;
24882 r = this.doc.selection.createRange();
24885 r.pasteHTML('    ');
24892 r = this.doc.selection.createRange();
24894 var target = r.parentElement();
24895 if(!target || target.tagName.toLowerCase() != 'li'){
24897 r.pasteHTML('<br />');
24903 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24904 this.cleanUpPaste.defer(100, this);
24910 }else if(Roo.isOpera){
24911 return function(e){
24912 var k = e.getKey();
24916 this.execCmd('InsertHTML','    ');
24919 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24920 this.cleanUpPaste.defer(100, this);
24925 }else if(Roo.isSafari){
24926 return function(e){
24927 var k = e.getKey();
24931 this.execCmd('InsertText','\t');
24935 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24936 this.cleanUpPaste.defer(100, this);
24944 getAllAncestors: function()
24946 var p = this.getSelectedNode();
24949 a.push(p); // push blank onto stack..
24950 p = this.getParentElement();
24954 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24958 a.push(this.doc.body);
24962 lastSelNode : false,
24965 getSelection : function()
24967 this.assignDocWin();
24968 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24971 getSelectedNode: function()
24973 // this may only work on Gecko!!!
24975 // should we cache this!!!!
24980 var range = this.createRange(this.getSelection()).cloneRange();
24983 var parent = range.parentElement();
24985 var testRange = range.duplicate();
24986 testRange.moveToElementText(parent);
24987 if (testRange.inRange(range)) {
24990 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24993 parent = parent.parentElement;
24998 // is ancestor a text element.
24999 var ac = range.commonAncestorContainer;
25000 if (ac.nodeType == 3) {
25001 ac = ac.parentNode;
25004 var ar = ac.childNodes;
25007 var other_nodes = [];
25008 var has_other_nodes = false;
25009 for (var i=0;i<ar.length;i++) {
25010 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25013 // fullly contained node.
25015 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25020 // probably selected..
25021 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25022 other_nodes.push(ar[i]);
25026 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25031 has_other_nodes = true;
25033 if (!nodes.length && other_nodes.length) {
25034 nodes= other_nodes;
25036 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25042 createRange: function(sel)
25044 // this has strange effects when using with
25045 // top toolbar - not sure if it's a great idea.
25046 //this.editor.contentWindow.focus();
25047 if (typeof sel != "undefined") {
25049 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25051 return this.doc.createRange();
25054 return this.doc.createRange();
25057 getParentElement: function()
25060 this.assignDocWin();
25061 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25063 var range = this.createRange(sel);
25066 var p = range.commonAncestorContainer;
25067 while (p.nodeType == 3) { // text node
25078 * Range intersection.. the hard stuff...
25082 * [ -- selected range --- ]
25086 * if end is before start or hits it. fail.
25087 * if start is after end or hits it fail.
25089 * if either hits (but other is outside. - then it's not
25095 // @see http://www.thismuchiknow.co.uk/?p=64.
25096 rangeIntersectsNode : function(range, node)
25098 var nodeRange = node.ownerDocument.createRange();
25100 nodeRange.selectNode(node);
25102 nodeRange.selectNodeContents(node);
25105 var rangeStartRange = range.cloneRange();
25106 rangeStartRange.collapse(true);
25108 var rangeEndRange = range.cloneRange();
25109 rangeEndRange.collapse(false);
25111 var nodeStartRange = nodeRange.cloneRange();
25112 nodeStartRange.collapse(true);
25114 var nodeEndRange = nodeRange.cloneRange();
25115 nodeEndRange.collapse(false);
25117 return rangeStartRange.compareBoundaryPoints(
25118 Range.START_TO_START, nodeEndRange) == -1 &&
25119 rangeEndRange.compareBoundaryPoints(
25120 Range.START_TO_START, nodeStartRange) == 1;
25124 rangeCompareNode : function(range, node)
25126 var nodeRange = node.ownerDocument.createRange();
25128 nodeRange.selectNode(node);
25130 nodeRange.selectNodeContents(node);
25134 range.collapse(true);
25136 nodeRange.collapse(true);
25138 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25139 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25141 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25143 var nodeIsBefore = ss == 1;
25144 var nodeIsAfter = ee == -1;
25146 if (nodeIsBefore && nodeIsAfter) {
25149 if (!nodeIsBefore && nodeIsAfter) {
25150 return 1; //right trailed.
25153 if (nodeIsBefore && !nodeIsAfter) {
25154 return 2; // left trailed.
25160 // private? - in a new class?
25161 cleanUpPaste : function()
25163 // cleans up the whole document..
25164 Roo.log('cleanuppaste');
25166 this.cleanUpChildren(this.doc.body);
25167 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25168 if (clean != this.doc.body.innerHTML) {
25169 this.doc.body.innerHTML = clean;
25174 cleanWordChars : function(input) {// change the chars to hex code
25175 var he = Roo.HtmlEditorCore;
25177 var output = input;
25178 Roo.each(he.swapCodes, function(sw) {
25179 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25181 output = output.replace(swapper, sw[1]);
25188 cleanUpChildren : function (n)
25190 if (!n.childNodes.length) {
25193 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25194 this.cleanUpChild(n.childNodes[i]);
25201 cleanUpChild : function (node)
25204 //console.log(node);
25205 if (node.nodeName == "#text") {
25206 // clean up silly Windows -- stuff?
25209 if (node.nodeName == "#comment") {
25210 node.parentNode.removeChild(node);
25211 // clean up silly Windows -- stuff?
25214 var lcname = node.tagName.toLowerCase();
25215 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25216 // whitelist of tags..
25218 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25220 node.parentNode.removeChild(node);
25225 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25227 // spans with no attributes - just remove them..
25228 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25229 remove_keep_children = true;
25232 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25233 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25235 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25236 // remove_keep_children = true;
25239 if (remove_keep_children) {
25240 this.cleanUpChildren(node);
25241 // inserts everything just before this node...
25242 while (node.childNodes.length) {
25243 var cn = node.childNodes[0];
25244 node.removeChild(cn);
25245 node.parentNode.insertBefore(cn, node);
25247 node.parentNode.removeChild(node);
25251 if (!node.attributes || !node.attributes.length) {
25256 this.cleanUpChildren(node);
25260 function cleanAttr(n,v)
25263 if (v.match(/^\./) || v.match(/^\//)) {
25266 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25269 if (v.match(/^#/)) {
25272 if (v.match(/^\{/)) { // allow template editing.
25275 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25276 node.removeAttribute(n);
25280 var cwhite = this.cwhite;
25281 var cblack = this.cblack;
25283 function cleanStyle(n,v)
25285 if (v.match(/expression/)) { //XSS?? should we even bother..
25286 node.removeAttribute(n);
25290 var parts = v.split(/;/);
25293 Roo.each(parts, function(p) {
25294 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25298 var l = p.split(':').shift().replace(/\s+/g,'');
25299 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25301 if ( cwhite.length && cblack.indexOf(l) > -1) {
25302 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25303 //node.removeAttribute(n);
25307 // only allow 'c whitelisted system attributes'
25308 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25309 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25310 //node.removeAttribute(n);
25320 if (clean.length) {
25321 node.setAttribute(n, clean.join(';'));
25323 node.removeAttribute(n);
25329 for (var i = node.attributes.length-1; i > -1 ; i--) {
25330 var a = node.attributes[i];
25333 if (a.name.toLowerCase().substr(0,2)=='on') {
25334 node.removeAttribute(a.name);
25337 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25338 node.removeAttribute(a.name);
25341 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25342 cleanAttr(a.name,a.value); // fixme..
25345 if (a.name == 'style') {
25346 cleanStyle(a.name,a.value);
25349 /// clean up MS crap..
25350 // tecnically this should be a list of valid class'es..
25353 if (a.name == 'class') {
25354 if (a.value.match(/^Mso/)) {
25355 node.removeAttribute('class');
25358 if (a.value.match(/^body$/)) {
25359 node.removeAttribute('class');
25370 this.cleanUpChildren(node);
25376 * Clean up MS wordisms...
25378 cleanWord : function(node)
25381 this.cleanWord(this.doc.body);
25386 node.nodeName == 'SPAN' &&
25387 !node.hasAttributes() &&
25388 node.childNodes.length == 1 &&
25389 node.firstChild.nodeName == "#text"
25391 var textNode = node.firstChild;
25392 node.removeChild(textNode);
25393 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25394 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25396 node.parentNode.insertBefore(textNode, node);
25397 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25398 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25400 node.parentNode.removeChild(node);
25403 if (node.nodeName == "#text") {
25404 // clean up silly Windows -- stuff?
25407 if (node.nodeName == "#comment") {
25408 node.parentNode.removeChild(node);
25409 // clean up silly Windows -- stuff?
25413 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25414 node.parentNode.removeChild(node);
25417 //Roo.log(node.tagName);
25418 // remove - but keep children..
25419 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25420 //Roo.log('-- removed');
25421 while (node.childNodes.length) {
25422 var cn = node.childNodes[0];
25423 node.removeChild(cn);
25424 node.parentNode.insertBefore(cn, node);
25425 // move node to parent - and clean it..
25426 this.cleanWord(cn);
25428 node.parentNode.removeChild(node);
25429 /// no need to iterate chidlren = it's got none..
25430 //this.iterateChildren(node, this.cleanWord);
25434 if (node.className.length) {
25436 var cn = node.className.split(/\W+/);
25438 Roo.each(cn, function(cls) {
25439 if (cls.match(/Mso[a-zA-Z]+/)) {
25444 node.className = cna.length ? cna.join(' ') : '';
25446 node.removeAttribute("class");
25450 if (node.hasAttribute("lang")) {
25451 node.removeAttribute("lang");
25454 if (node.hasAttribute("style")) {
25456 var styles = node.getAttribute("style").split(";");
25458 Roo.each(styles, function(s) {
25459 if (!s.match(/:/)) {
25462 var kv = s.split(":");
25463 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25466 // what ever is left... we allow.
25469 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25470 if (!nstyle.length) {
25471 node.removeAttribute('style');
25474 this.iterateChildren(node, this.cleanWord);
25480 * iterateChildren of a Node, calling fn each time, using this as the scole..
25481 * @param {DomNode} node node to iterate children of.
25482 * @param {Function} fn method of this class to call on each item.
25484 iterateChildren : function(node, fn)
25486 if (!node.childNodes.length) {
25489 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25490 fn.call(this, node.childNodes[i])
25496 * cleanTableWidths.
25498 * Quite often pasting from word etc.. results in tables with column and widths.
25499 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25502 cleanTableWidths : function(node)
25507 this.cleanTableWidths(this.doc.body);
25512 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25515 Roo.log(node.tagName);
25516 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25517 this.iterateChildren(node, this.cleanTableWidths);
25520 if (node.hasAttribute('width')) {
25521 node.removeAttribute('width');
25525 if (node.hasAttribute("style")) {
25528 var styles = node.getAttribute("style").split(";");
25530 Roo.each(styles, function(s) {
25531 if (!s.match(/:/)) {
25534 var kv = s.split(":");
25535 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25538 // what ever is left... we allow.
25541 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25542 if (!nstyle.length) {
25543 node.removeAttribute('style');
25547 this.iterateChildren(node, this.cleanTableWidths);
25555 domToHTML : function(currentElement, depth, nopadtext) {
25557 depth = depth || 0;
25558 nopadtext = nopadtext || false;
25560 if (!currentElement) {
25561 return this.domToHTML(this.doc.body);
25564 //Roo.log(currentElement);
25566 var allText = false;
25567 var nodeName = currentElement.nodeName;
25568 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25570 if (nodeName == '#text') {
25572 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25577 if (nodeName != 'BODY') {
25580 // Prints the node tagName, such as <A>, <IMG>, etc
25583 for(i = 0; i < currentElement.attributes.length;i++) {
25585 var aname = currentElement.attributes.item(i).name;
25586 if (!currentElement.attributes.item(i).value.length) {
25589 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25592 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25601 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25604 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25609 // Traverse the tree
25611 var currentElementChild = currentElement.childNodes.item(i);
25612 var allText = true;
25613 var innerHTML = '';
25615 while (currentElementChild) {
25616 // Formatting code (indent the tree so it looks nice on the screen)
25617 var nopad = nopadtext;
25618 if (lastnode == 'SPAN') {
25622 if (currentElementChild.nodeName == '#text') {
25623 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25624 toadd = nopadtext ? toadd : toadd.trim();
25625 if (!nopad && toadd.length > 80) {
25626 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25628 innerHTML += toadd;
25631 currentElementChild = currentElement.childNodes.item(i);
25637 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25639 // Recursively traverse the tree structure of the child node
25640 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25641 lastnode = currentElementChild.nodeName;
25643 currentElementChild=currentElement.childNodes.item(i);
25649 // The remaining code is mostly for formatting the tree
25650 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25655 ret+= "</"+tagName+">";
25661 applyBlacklists : function()
25663 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25664 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25668 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25669 if (b.indexOf(tag) > -1) {
25672 this.white.push(tag);
25676 Roo.each(w, function(tag) {
25677 if (b.indexOf(tag) > -1) {
25680 if (this.white.indexOf(tag) > -1) {
25683 this.white.push(tag);
25688 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25689 if (w.indexOf(tag) > -1) {
25692 this.black.push(tag);
25696 Roo.each(b, function(tag) {
25697 if (w.indexOf(tag) > -1) {
25700 if (this.black.indexOf(tag) > -1) {
25703 this.black.push(tag);
25708 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25709 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25713 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25714 if (b.indexOf(tag) > -1) {
25717 this.cwhite.push(tag);
25721 Roo.each(w, function(tag) {
25722 if (b.indexOf(tag) > -1) {
25725 if (this.cwhite.indexOf(tag) > -1) {
25728 this.cwhite.push(tag);
25733 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25734 if (w.indexOf(tag) > -1) {
25737 this.cblack.push(tag);
25741 Roo.each(b, function(tag) {
25742 if (w.indexOf(tag) > -1) {
25745 if (this.cblack.indexOf(tag) > -1) {
25748 this.cblack.push(tag);
25753 setStylesheets : function(stylesheets)
25755 if(typeof(stylesheets) == 'string'){
25756 Roo.get(this.iframe.contentDocument.head).createChild({
25758 rel : 'stylesheet',
25767 Roo.each(stylesheets, function(s) {
25772 Roo.get(_this.iframe.contentDocument.head).createChild({
25774 rel : 'stylesheet',
25783 removeStylesheets : function()
25787 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25792 setStyle : function(style)
25794 Roo.get(this.iframe.contentDocument.head).createChild({
25803 // hide stuff that is not compatible
25817 * @event specialkey
25821 * @cfg {String} fieldClass @hide
25824 * @cfg {String} focusClass @hide
25827 * @cfg {String} autoCreate @hide
25830 * @cfg {String} inputType @hide
25833 * @cfg {String} invalidClass @hide
25836 * @cfg {String} invalidText @hide
25839 * @cfg {String} msgFx @hide
25842 * @cfg {String} validateOnBlur @hide
25846 Roo.HtmlEditorCore.white = [
25847 'area', 'br', 'img', 'input', 'hr', 'wbr',
25849 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25850 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25851 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25852 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25853 'table', 'ul', 'xmp',
25855 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25858 'dir', 'menu', 'ol', 'ul', 'dl',
25864 Roo.HtmlEditorCore.black = [
25865 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25867 'base', 'basefont', 'bgsound', 'blink', 'body',
25868 'frame', 'frameset', 'head', 'html', 'ilayer',
25869 'iframe', 'layer', 'link', 'meta', 'object',
25870 'script', 'style' ,'title', 'xml' // clean later..
25872 Roo.HtmlEditorCore.clean = [
25873 'script', 'style', 'title', 'xml'
25875 Roo.HtmlEditorCore.remove = [
25880 Roo.HtmlEditorCore.ablack = [
25884 Roo.HtmlEditorCore.aclean = [
25885 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25889 Roo.HtmlEditorCore.pwhite= [
25890 'http', 'https', 'mailto'
25893 // white listed style attributes.
25894 Roo.HtmlEditorCore.cwhite= [
25895 // 'text-align', /// default is to allow most things..
25901 // black listed style attributes.
25902 Roo.HtmlEditorCore.cblack= [
25903 // 'font-size' -- this can be set by the project
25907 Roo.HtmlEditorCore.swapCodes =[
25908 [ 8211, "–" ],
25909 [ 8212, "—" ],
25926 * @class Roo.bootstrap.HtmlEditor
25927 * @extends Roo.bootstrap.TextArea
25928 * Bootstrap HtmlEditor class
25931 * Create a new HtmlEditor
25932 * @param {Object} config The config object
25935 Roo.bootstrap.HtmlEditor = function(config){
25936 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25937 if (!this.toolbars) {
25938 this.toolbars = [];
25941 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25944 * @event initialize
25945 * Fires when the editor is fully initialized (including the iframe)
25946 * @param {HtmlEditor} this
25951 * Fires when the editor is first receives the focus. Any insertion must wait
25952 * until after this event.
25953 * @param {HtmlEditor} this
25957 * @event beforesync
25958 * Fires before the textarea is updated with content from the editor iframe. Return false
25959 * to cancel the sync.
25960 * @param {HtmlEditor} this
25961 * @param {String} html
25965 * @event beforepush
25966 * Fires before the iframe editor is updated with content from the textarea. Return false
25967 * to cancel the push.
25968 * @param {HtmlEditor} this
25969 * @param {String} html
25974 * Fires when the textarea is updated with content from the editor iframe.
25975 * @param {HtmlEditor} this
25976 * @param {String} html
25981 * Fires when the iframe editor is updated with content from the textarea.
25982 * @param {HtmlEditor} this
25983 * @param {String} html
25987 * @event editmodechange
25988 * Fires when the editor switches edit modes
25989 * @param {HtmlEditor} this
25990 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25992 editmodechange: true,
25994 * @event editorevent
25995 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25996 * @param {HtmlEditor} this
26000 * @event firstfocus
26001 * Fires when on first focus - needed by toolbars..
26002 * @param {HtmlEditor} this
26007 * Auto save the htmlEditor value as a file into Events
26008 * @param {HtmlEditor} this
26012 * @event savedpreview
26013 * preview the saved version of htmlEditor
26014 * @param {HtmlEditor} this
26021 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26025 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26030 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26035 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26040 * @cfg {Number} height (in pixels)
26044 * @cfg {Number} width (in pixels)
26049 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26052 stylesheets: false,
26057 // private properties
26058 validationEvent : false,
26060 initialized : false,
26063 onFocus : Roo.emptyFn,
26065 hideMode:'offsets',
26067 tbContainer : false,
26071 toolbarContainer :function() {
26072 return this.wrap.select('.x-html-editor-tb',true).first();
26076 * Protected method that will not generally be called directly. It
26077 * is called when the editor creates its toolbar. Override this method if you need to
26078 * add custom toolbar buttons.
26079 * @param {HtmlEditor} editor
26081 createToolbar : function(){
26082 Roo.log('renewing');
26083 Roo.log("create toolbars");
26085 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26086 this.toolbars[0].render(this.toolbarContainer());
26090 // if (!editor.toolbars || !editor.toolbars.length) {
26091 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26094 // for (var i =0 ; i < editor.toolbars.length;i++) {
26095 // editor.toolbars[i] = Roo.factory(
26096 // typeof(editor.toolbars[i]) == 'string' ?
26097 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26098 // Roo.bootstrap.HtmlEditor);
26099 // editor.toolbars[i].init(editor);
26105 onRender : function(ct, position)
26107 // Roo.log("Call onRender: " + this.xtype);
26109 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26111 this.wrap = this.inputEl().wrap({
26112 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26115 this.editorcore.onRender(ct, position);
26117 if (this.resizable) {
26118 this.resizeEl = new Roo.Resizable(this.wrap, {
26122 minHeight : this.height,
26123 height: this.height,
26124 handles : this.resizable,
26127 resize : function(r, w, h) {
26128 _t.onResize(w,h); // -something
26134 this.createToolbar(this);
26137 if(!this.width && this.resizable){
26138 this.setSize(this.wrap.getSize());
26140 if (this.resizeEl) {
26141 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26142 // should trigger onReize..
26148 onResize : function(w, h)
26150 Roo.log('resize: ' +w + ',' + h );
26151 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26155 if(this.inputEl() ){
26156 if(typeof w == 'number'){
26157 var aw = w - this.wrap.getFrameWidth('lr');
26158 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26161 if(typeof h == 'number'){
26162 var tbh = -11; // fixme it needs to tool bar size!
26163 for (var i =0; i < this.toolbars.length;i++) {
26164 // fixme - ask toolbars for heights?
26165 tbh += this.toolbars[i].el.getHeight();
26166 //if (this.toolbars[i].footer) {
26167 // tbh += this.toolbars[i].footer.el.getHeight();
26175 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26176 ah -= 5; // knock a few pixes off for look..
26177 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26181 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26182 this.editorcore.onResize(ew,eh);
26187 * Toggles the editor between standard and source edit mode.
26188 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26190 toggleSourceEdit : function(sourceEditMode)
26192 this.editorcore.toggleSourceEdit(sourceEditMode);
26194 if(this.editorcore.sourceEditMode){
26195 Roo.log('editor - showing textarea');
26198 // Roo.log(this.syncValue());
26200 this.inputEl().removeClass(['hide', 'x-hidden']);
26201 this.inputEl().dom.removeAttribute('tabIndex');
26202 this.inputEl().focus();
26204 Roo.log('editor - hiding textarea');
26206 // Roo.log(this.pushValue());
26209 this.inputEl().addClass(['hide', 'x-hidden']);
26210 this.inputEl().dom.setAttribute('tabIndex', -1);
26211 //this.deferFocus();
26214 if(this.resizable){
26215 this.setSize(this.wrap.getSize());
26218 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26221 // private (for BoxComponent)
26222 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26224 // private (for BoxComponent)
26225 getResizeEl : function(){
26229 // private (for BoxComponent)
26230 getPositionEl : function(){
26235 initEvents : function(){
26236 this.originalValue = this.getValue();
26240 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26243 // markInvalid : Roo.emptyFn,
26245 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26248 // clearInvalid : Roo.emptyFn,
26250 setValue : function(v){
26251 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26252 this.editorcore.pushValue();
26257 deferFocus : function(){
26258 this.focus.defer(10, this);
26262 focus : function(){
26263 this.editorcore.focus();
26269 onDestroy : function(){
26275 for (var i =0; i < this.toolbars.length;i++) {
26276 // fixme - ask toolbars for heights?
26277 this.toolbars[i].onDestroy();
26280 this.wrap.dom.innerHTML = '';
26281 this.wrap.remove();
26286 onFirstFocus : function(){
26287 //Roo.log("onFirstFocus");
26288 this.editorcore.onFirstFocus();
26289 for (var i =0; i < this.toolbars.length;i++) {
26290 this.toolbars[i].onFirstFocus();
26296 syncValue : function()
26298 this.editorcore.syncValue();
26301 pushValue : function()
26303 this.editorcore.pushValue();
26307 // hide stuff that is not compatible
26321 * @event specialkey
26325 * @cfg {String} fieldClass @hide
26328 * @cfg {String} focusClass @hide
26331 * @cfg {String} autoCreate @hide
26334 * @cfg {String} inputType @hide
26338 * @cfg {String} invalidText @hide
26341 * @cfg {String} msgFx @hide
26344 * @cfg {String} validateOnBlur @hide
26353 Roo.namespace('Roo.bootstrap.htmleditor');
26355 * @class Roo.bootstrap.HtmlEditorToolbar1
26361 new Roo.bootstrap.HtmlEditor({
26364 new Roo.bootstrap.HtmlEditorToolbar1({
26365 disable : { fonts: 1 , format: 1, ..., ... , ...],
26371 * @cfg {Object} disable List of elements to disable..
26372 * @cfg {Array} btns List of additional buttons.
26376 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26379 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26382 Roo.apply(this, config);
26384 // default disabled, based on 'good practice'..
26385 this.disable = this.disable || {};
26386 Roo.applyIf(this.disable, {
26389 specialElements : true
26391 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26393 this.editor = config.editor;
26394 this.editorcore = config.editor.editorcore;
26396 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26398 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26399 // dont call parent... till later.
26401 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26406 editorcore : false,
26411 "h1","h2","h3","h4","h5","h6",
26413 "abbr", "acronym", "address", "cite", "samp", "var",
26417 onRender : function(ct, position)
26419 // Roo.log("Call onRender: " + this.xtype);
26421 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26423 this.el.dom.style.marginBottom = '0';
26425 var editorcore = this.editorcore;
26426 var editor= this.editor;
26429 var btn = function(id,cmd , toggle, handler, html){
26431 var event = toggle ? 'toggle' : 'click';
26436 xns: Roo.bootstrap,
26440 enableToggle:toggle !== false,
26442 pressed : toggle ? false : null,
26445 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26446 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26452 // var cb_box = function...
26457 xns: Roo.bootstrap,
26462 xns: Roo.bootstrap,
26466 Roo.each(this.formats, function(f) {
26467 style.menu.items.push({
26469 xns: Roo.bootstrap,
26470 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26475 editorcore.insertTag(this.tagname);
26482 children.push(style);
26484 btn('bold',false,true);
26485 btn('italic',false,true);
26486 btn('align-left', 'justifyleft',true);
26487 btn('align-center', 'justifycenter',true);
26488 btn('align-right' , 'justifyright',true);
26489 btn('link', false, false, function(btn) {
26490 //Roo.log("create link?");
26491 var url = prompt(this.createLinkText, this.defaultLinkValue);
26492 if(url && url != 'http:/'+'/'){
26493 this.editorcore.relayCmd('createlink', url);
26496 btn('list','insertunorderedlist',true);
26497 btn('pencil', false,true, function(btn){
26499 this.toggleSourceEdit(btn.pressed);
26502 if (this.editor.btns.length > 0) {
26503 for (var i = 0; i<this.editor.btns.length; i++) {
26504 children.push(this.editor.btns[i]);
26512 xns: Roo.bootstrap,
26517 xns: Roo.bootstrap,
26522 cog.menu.items.push({
26524 xns: Roo.bootstrap,
26525 html : Clean styles,
26530 editorcore.insertTag(this.tagname);
26539 this.xtype = 'NavSimplebar';
26541 for(var i=0;i< children.length;i++) {
26543 this.buttons.add(this.addxtypeChild(children[i]));
26547 editor.on('editorevent', this.updateToolbar, this);
26549 onBtnClick : function(id)
26551 this.editorcore.relayCmd(id);
26552 this.editorcore.focus();
26556 * Protected method that will not generally be called directly. It triggers
26557 * a toolbar update by reading the markup state of the current selection in the editor.
26559 updateToolbar: function(){
26561 if(!this.editorcore.activated){
26562 this.editor.onFirstFocus(); // is this neeed?
26566 var btns = this.buttons;
26567 var doc = this.editorcore.doc;
26568 btns.get('bold').setActive(doc.queryCommandState('bold'));
26569 btns.get('italic').setActive(doc.queryCommandState('italic'));
26570 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26572 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26573 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26574 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26576 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26577 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26580 var ans = this.editorcore.getAllAncestors();
26581 if (this.formatCombo) {
26584 var store = this.formatCombo.store;
26585 this.formatCombo.setValue("");
26586 for (var i =0; i < ans.length;i++) {
26587 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26589 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26597 // hides menus... - so this cant be on a menu...
26598 Roo.bootstrap.MenuMgr.hideAll();
26600 Roo.bootstrap.MenuMgr.hideAll();
26601 //this.editorsyncValue();
26603 onFirstFocus: function() {
26604 this.buttons.each(function(item){
26608 toggleSourceEdit : function(sourceEditMode){
26611 if(sourceEditMode){
26612 Roo.log("disabling buttons");
26613 this.buttons.each( function(item){
26614 if(item.cmd != 'pencil'){
26620 Roo.log("enabling buttons");
26621 if(this.editorcore.initialized){
26622 this.buttons.each( function(item){
26628 Roo.log("calling toggole on editor");
26629 // tell the editor that it's been pressed..
26630 this.editor.toggleSourceEdit(sourceEditMode);
26644 * @class Roo.bootstrap.Markdown
26645 * @extends Roo.bootstrap.TextArea
26646 * Bootstrap Showdown editable area
26647 * @cfg {string} content
26650 * Create a new Showdown
26653 Roo.bootstrap.Markdown = function(config){
26654 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26658 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26662 initEvents : function()
26665 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26666 this.markdownEl = this.el.createChild({
26667 cls : 'roo-markdown-area'
26669 this.inputEl().addClass('d-none');
26670 if (this.getValue() == '') {
26671 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26674 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26676 this.markdownEl.on('click', this.toggleTextEdit, this);
26677 this.on('blur', this.toggleTextEdit, this);
26678 this.on('specialkey', this.resizeTextArea, this);
26681 toggleTextEdit : function()
26683 var sh = this.markdownEl.getHeight();
26684 this.inputEl().addClass('d-none');
26685 this.markdownEl.addClass('d-none');
26686 if (!this.editing) {
26688 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26689 this.inputEl().removeClass('d-none');
26690 this.inputEl().focus();
26691 this.editing = true;
26694 // show showdown...
26695 this.updateMarkdown();
26696 this.markdownEl.removeClass('d-none');
26697 this.editing = false;
26700 updateMarkdown : function()
26702 if (this.getValue() == '') {
26703 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26707 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26710 resizeTextArea: function () {
26713 Roo.log([sh, this.getValue().split("\n").length * 30]);
26714 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26716 setValue : function(val)
26718 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26719 if (!this.editing) {
26720 this.updateMarkdown();
26726 if (!this.editing) {
26727 this.toggleTextEdit();
26735 * @class Roo.bootstrap.Table.AbstractSelectionModel
26736 * @extends Roo.util.Observable
26737 * Abstract base class for grid SelectionModels. It provides the interface that should be
26738 * implemented by descendant classes. This class should not be directly instantiated.
26741 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26742 this.locked = false;
26743 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26747 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26748 /** @ignore Called by the grid automatically. Do not call directly. */
26749 init : function(grid){
26755 * Locks the selections.
26758 this.locked = true;
26762 * Unlocks the selections.
26764 unlock : function(){
26765 this.locked = false;
26769 * Returns true if the selections are locked.
26770 * @return {Boolean}
26772 isLocked : function(){
26773 return this.locked;
26777 initEvents : function ()
26783 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26784 * @class Roo.bootstrap.Table.RowSelectionModel
26785 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26786 * It supports multiple selections and keyboard selection/navigation.
26788 * @param {Object} config
26791 Roo.bootstrap.Table.RowSelectionModel = function(config){
26792 Roo.apply(this, config);
26793 this.selections = new Roo.util.MixedCollection(false, function(o){
26798 this.lastActive = false;
26802 * @event selectionchange
26803 * Fires when the selection changes
26804 * @param {SelectionModel} this
26806 "selectionchange" : true,
26808 * @event afterselectionchange
26809 * Fires after the selection changes (eg. by key press or clicking)
26810 * @param {SelectionModel} this
26812 "afterselectionchange" : true,
26814 * @event beforerowselect
26815 * Fires when a row is selected being selected, return false to cancel.
26816 * @param {SelectionModel} this
26817 * @param {Number} rowIndex The selected index
26818 * @param {Boolean} keepExisting False if other selections will be cleared
26820 "beforerowselect" : true,
26823 * Fires when a row is selected.
26824 * @param {SelectionModel} this
26825 * @param {Number} rowIndex The selected index
26826 * @param {Roo.data.Record} r The record
26828 "rowselect" : true,
26830 * @event rowdeselect
26831 * Fires when a row is deselected.
26832 * @param {SelectionModel} this
26833 * @param {Number} rowIndex The selected index
26835 "rowdeselect" : true
26837 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26838 this.locked = false;
26841 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26843 * @cfg {Boolean} singleSelect
26844 * True to allow selection of only one row at a time (defaults to false)
26846 singleSelect : false,
26849 initEvents : function()
26852 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26853 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26854 //}else{ // allow click to work like normal
26855 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26857 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26858 this.grid.on("rowclick", this.handleMouseDown, this);
26860 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26861 "up" : function(e){
26863 this.selectPrevious(e.shiftKey);
26864 }else if(this.last !== false && this.lastActive !== false){
26865 var last = this.last;
26866 this.selectRange(this.last, this.lastActive-1);
26867 this.grid.getView().focusRow(this.lastActive);
26868 if(last !== false){
26872 this.selectFirstRow();
26874 this.fireEvent("afterselectionchange", this);
26876 "down" : function(e){
26878 this.selectNext(e.shiftKey);
26879 }else if(this.last !== false && this.lastActive !== false){
26880 var last = this.last;
26881 this.selectRange(this.last, this.lastActive+1);
26882 this.grid.getView().focusRow(this.lastActive);
26883 if(last !== false){
26887 this.selectFirstRow();
26889 this.fireEvent("afterselectionchange", this);
26893 this.grid.store.on('load', function(){
26894 this.selections.clear();
26897 var view = this.grid.view;
26898 view.on("refresh", this.onRefresh, this);
26899 view.on("rowupdated", this.onRowUpdated, this);
26900 view.on("rowremoved", this.onRemove, this);
26905 onRefresh : function()
26907 var ds = this.grid.store, i, v = this.grid.view;
26908 var s = this.selections;
26909 s.each(function(r){
26910 if((i = ds.indexOfId(r.id)) != -1){
26919 onRemove : function(v, index, r){
26920 this.selections.remove(r);
26924 onRowUpdated : function(v, index, r){
26925 if(this.isSelected(r)){
26926 v.onRowSelect(index);
26932 * @param {Array} records The records to select
26933 * @param {Boolean} keepExisting (optional) True to keep existing selections
26935 selectRecords : function(records, keepExisting)
26938 this.clearSelections();
26940 var ds = this.grid.store;
26941 for(var i = 0, len = records.length; i < len; i++){
26942 this.selectRow(ds.indexOf(records[i]), true);
26947 * Gets the number of selected rows.
26950 getCount : function(){
26951 return this.selections.length;
26955 * Selects the first row in the grid.
26957 selectFirstRow : function(){
26962 * Select the last row.
26963 * @param {Boolean} keepExisting (optional) True to keep existing selections
26965 selectLastRow : function(keepExisting){
26966 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26967 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26971 * Selects the row immediately following the last selected row.
26972 * @param {Boolean} keepExisting (optional) True to keep existing selections
26974 selectNext : function(keepExisting)
26976 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26977 this.selectRow(this.last+1, keepExisting);
26978 this.grid.getView().focusRow(this.last);
26983 * Selects the row that precedes the last selected row.
26984 * @param {Boolean} keepExisting (optional) True to keep existing selections
26986 selectPrevious : function(keepExisting){
26988 this.selectRow(this.last-1, keepExisting);
26989 this.grid.getView().focusRow(this.last);
26994 * Returns the selected records
26995 * @return {Array} Array of selected records
26997 getSelections : function(){
26998 return [].concat(this.selections.items);
27002 * Returns the first selected record.
27005 getSelected : function(){
27006 return this.selections.itemAt(0);
27011 * Clears all selections.
27013 clearSelections : function(fast)
27019 var ds = this.grid.store;
27020 var s = this.selections;
27021 s.each(function(r){
27022 this.deselectRow(ds.indexOfId(r.id));
27026 this.selections.clear();
27033 * Selects all rows.
27035 selectAll : function(){
27039 this.selections.clear();
27040 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27041 this.selectRow(i, true);
27046 * Returns True if there is a selection.
27047 * @return {Boolean}
27049 hasSelection : function(){
27050 return this.selections.length > 0;
27054 * Returns True if the specified row is selected.
27055 * @param {Number/Record} record The record or index of the record to check
27056 * @return {Boolean}
27058 isSelected : function(index){
27059 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27060 return (r && this.selections.key(r.id) ? true : false);
27064 * Returns True if the specified record id is selected.
27065 * @param {String} id The id of record to check
27066 * @return {Boolean}
27068 isIdSelected : function(id){
27069 return (this.selections.key(id) ? true : false);
27074 handleMouseDBClick : function(e, t){
27078 handleMouseDown : function(e, t)
27080 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27081 if(this.isLocked() || rowIndex < 0 ){
27084 if(e.shiftKey && this.last !== false){
27085 var last = this.last;
27086 this.selectRange(last, rowIndex, e.ctrlKey);
27087 this.last = last; // reset the last
27091 var isSelected = this.isSelected(rowIndex);
27092 //Roo.log("select row:" + rowIndex);
27094 this.deselectRow(rowIndex);
27096 this.selectRow(rowIndex, true);
27100 if(e.button !== 0 && isSelected){
27101 alert('rowIndex 2: ' + rowIndex);
27102 view.focusRow(rowIndex);
27103 }else if(e.ctrlKey && isSelected){
27104 this.deselectRow(rowIndex);
27105 }else if(!isSelected){
27106 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27107 view.focusRow(rowIndex);
27111 this.fireEvent("afterselectionchange", this);
27114 handleDragableRowClick : function(grid, rowIndex, e)
27116 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27117 this.selectRow(rowIndex, false);
27118 grid.view.focusRow(rowIndex);
27119 this.fireEvent("afterselectionchange", this);
27124 * Selects multiple rows.
27125 * @param {Array} rows Array of the indexes of the row to select
27126 * @param {Boolean} keepExisting (optional) True to keep existing selections
27128 selectRows : function(rows, keepExisting){
27130 this.clearSelections();
27132 for(var i = 0, len = rows.length; i < len; i++){
27133 this.selectRow(rows[i], true);
27138 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27139 * @param {Number} startRow The index of the first row in the range
27140 * @param {Number} endRow The index of the last row in the range
27141 * @param {Boolean} keepExisting (optional) True to retain existing selections
27143 selectRange : function(startRow, endRow, keepExisting){
27148 this.clearSelections();
27150 if(startRow <= endRow){
27151 for(var i = startRow; i <= endRow; i++){
27152 this.selectRow(i, true);
27155 for(var i = startRow; i >= endRow; i--){
27156 this.selectRow(i, true);
27162 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27163 * @param {Number} startRow The index of the first row in the range
27164 * @param {Number} endRow The index of the last row in the range
27166 deselectRange : function(startRow, endRow, preventViewNotify){
27170 for(var i = startRow; i <= endRow; i++){
27171 this.deselectRow(i, preventViewNotify);
27177 * @param {Number} row The index of the row to select
27178 * @param {Boolean} keepExisting (optional) True to keep existing selections
27180 selectRow : function(index, keepExisting, preventViewNotify)
27182 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27185 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27186 if(!keepExisting || this.singleSelect){
27187 this.clearSelections();
27190 var r = this.grid.store.getAt(index);
27191 //console.log('selectRow - record id :' + r.id);
27193 this.selections.add(r);
27194 this.last = this.lastActive = index;
27195 if(!preventViewNotify){
27196 var proxy = new Roo.Element(
27197 this.grid.getRowDom(index)
27199 proxy.addClass('bg-info info');
27201 this.fireEvent("rowselect", this, index, r);
27202 this.fireEvent("selectionchange", this);
27208 * @param {Number} row The index of the row to deselect
27210 deselectRow : function(index, preventViewNotify)
27215 if(this.last == index){
27218 if(this.lastActive == index){
27219 this.lastActive = false;
27222 var r = this.grid.store.getAt(index);
27227 this.selections.remove(r);
27228 //.console.log('deselectRow - record id :' + r.id);
27229 if(!preventViewNotify){
27231 var proxy = new Roo.Element(
27232 this.grid.getRowDom(index)
27234 proxy.removeClass('bg-info info');
27236 this.fireEvent("rowdeselect", this, index);
27237 this.fireEvent("selectionchange", this);
27241 restoreLast : function(){
27243 this.last = this._last;
27248 acceptsNav : function(row, col, cm){
27249 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27253 onEditorKey : function(field, e){
27254 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27259 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27261 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27263 }else if(k == e.ENTER && !e.ctrlKey){
27267 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27269 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27271 }else if(k == e.ESC){
27275 g.startEditing(newCell[0], newCell[1]);
27281 * Ext JS Library 1.1.1
27282 * Copyright(c) 2006-2007, Ext JS, LLC.
27284 * Originally Released Under LGPL - original licence link has changed is not relivant.
27287 * <script type="text/javascript">
27291 * @class Roo.bootstrap.PagingToolbar
27292 * @extends Roo.bootstrap.NavSimplebar
27293 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27295 * Create a new PagingToolbar
27296 * @param {Object} config The config object
27297 * @param {Roo.data.Store} store
27299 Roo.bootstrap.PagingToolbar = function(config)
27301 // old args format still supported... - xtype is prefered..
27302 // created from xtype...
27304 this.ds = config.dataSource;
27306 if (config.store && !this.ds) {
27307 this.store= Roo.factory(config.store, Roo.data);
27308 this.ds = this.store;
27309 this.ds.xmodule = this.xmodule || false;
27312 this.toolbarItems = [];
27313 if (config.items) {
27314 this.toolbarItems = config.items;
27317 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27322 this.bind(this.ds);
27325 if (Roo.bootstrap.version == 4) {
27326 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27328 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27333 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27335 * @cfg {Roo.data.Store} dataSource
27336 * The underlying data store providing the paged data
27339 * @cfg {String/HTMLElement/Element} container
27340 * container The id or element that will contain the toolbar
27343 * @cfg {Boolean} displayInfo
27344 * True to display the displayMsg (defaults to false)
27347 * @cfg {Number} pageSize
27348 * The number of records to display per page (defaults to 20)
27352 * @cfg {String} displayMsg
27353 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27355 displayMsg : 'Displaying {0} - {1} of {2}',
27357 * @cfg {String} emptyMsg
27358 * The message to display when no records are found (defaults to "No data to display")
27360 emptyMsg : 'No data to display',
27362 * Customizable piece of the default paging text (defaults to "Page")
27365 beforePageText : "Page",
27367 * Customizable piece of the default paging text (defaults to "of %0")
27370 afterPageText : "of {0}",
27372 * Customizable piece of the default paging text (defaults to "First Page")
27375 firstText : "First Page",
27377 * Customizable piece of the default paging text (defaults to "Previous Page")
27380 prevText : "Previous Page",
27382 * Customizable piece of the default paging text (defaults to "Next Page")
27385 nextText : "Next Page",
27387 * Customizable piece of the default paging text (defaults to "Last Page")
27390 lastText : "Last Page",
27392 * Customizable piece of the default paging text (defaults to "Refresh")
27395 refreshText : "Refresh",
27399 onRender : function(ct, position)
27401 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27402 this.navgroup.parentId = this.id;
27403 this.navgroup.onRender(this.el, null);
27404 // add the buttons to the navgroup
27406 if(this.displayInfo){
27407 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27408 this.displayEl = this.el.select('.x-paging-info', true).first();
27409 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27410 // this.displayEl = navel.el.select('span',true).first();
27416 Roo.each(_this.buttons, function(e){ // this might need to use render????
27417 Roo.factory(e).render(_this.el);
27421 Roo.each(_this.toolbarItems, function(e) {
27422 _this.navgroup.addItem(e);
27426 this.first = this.navgroup.addItem({
27427 tooltip: this.firstText,
27428 cls: "prev btn-outline-secondary",
27429 html : ' <i class="fa fa-step-backward"></i>',
27431 preventDefault: true,
27432 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27435 this.prev = this.navgroup.addItem({
27436 tooltip: this.prevText,
27437 cls: "prev btn-outline-secondary",
27438 html : ' <i class="fa fa-backward"></i>',
27440 preventDefault: true,
27441 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27443 //this.addSeparator();
27446 var field = this.navgroup.addItem( {
27448 cls : 'x-paging-position btn-outline-secondary',
27450 html : this.beforePageText +
27451 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27452 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27455 this.field = field.el.select('input', true).first();
27456 this.field.on("keydown", this.onPagingKeydown, this);
27457 this.field.on("focus", function(){this.dom.select();});
27460 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27461 //this.field.setHeight(18);
27462 //this.addSeparator();
27463 this.next = this.navgroup.addItem({
27464 tooltip: this.nextText,
27465 cls: "next btn-outline-secondary",
27466 html : ' <i class="fa fa-forward"></i>',
27468 preventDefault: true,
27469 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27471 this.last = this.navgroup.addItem({
27472 tooltip: this.lastText,
27473 html : ' <i class="fa fa-step-forward"></i>',
27474 cls: "next btn-outline-secondary",
27476 preventDefault: true,
27477 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27479 //this.addSeparator();
27480 this.loading = this.navgroup.addItem({
27481 tooltip: this.refreshText,
27482 cls: "btn-outline-secondary",
27483 html : ' <i class="fa fa-refresh"></i>',
27484 preventDefault: true,
27485 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27491 updateInfo : function(){
27492 if(this.displayEl){
27493 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27494 var msg = count == 0 ?
27498 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27500 this.displayEl.update(msg);
27505 onLoad : function(ds, r, o)
27507 this.cursor = o.params && o.params.start ? o.params.start : 0;
27509 var d = this.getPageData(),
27514 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27515 this.field.dom.value = ap;
27516 this.first.setDisabled(ap == 1);
27517 this.prev.setDisabled(ap == 1);
27518 this.next.setDisabled(ap == ps);
27519 this.last.setDisabled(ap == ps);
27520 this.loading.enable();
27525 getPageData : function(){
27526 var total = this.ds.getTotalCount();
27529 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27530 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27535 onLoadError : function(){
27536 this.loading.enable();
27540 onPagingKeydown : function(e){
27541 var k = e.getKey();
27542 var d = this.getPageData();
27544 var v = this.field.dom.value, pageNum;
27545 if(!v || isNaN(pageNum = parseInt(v, 10))){
27546 this.field.dom.value = d.activePage;
27549 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27550 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27553 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))
27555 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27556 this.field.dom.value = pageNum;
27557 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27560 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27562 var v = this.field.dom.value, pageNum;
27563 var increment = (e.shiftKey) ? 10 : 1;
27564 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27567 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27568 this.field.dom.value = d.activePage;
27571 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27573 this.field.dom.value = parseInt(v, 10) + increment;
27574 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27575 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27582 beforeLoad : function(){
27584 this.loading.disable();
27589 onClick : function(which){
27598 ds.load({params:{start: 0, limit: this.pageSize}});
27601 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27604 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27607 var total = ds.getTotalCount();
27608 var extra = total % this.pageSize;
27609 var lastStart = extra ? (total - extra) : total-this.pageSize;
27610 ds.load({params:{start: lastStart, limit: this.pageSize}});
27613 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27619 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27620 * @param {Roo.data.Store} store The data store to unbind
27622 unbind : function(ds){
27623 ds.un("beforeload", this.beforeLoad, this);
27624 ds.un("load", this.onLoad, this);
27625 ds.un("loadexception", this.onLoadError, this);
27626 ds.un("remove", this.updateInfo, this);
27627 ds.un("add", this.updateInfo, this);
27628 this.ds = undefined;
27632 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27633 * @param {Roo.data.Store} store The data store to bind
27635 bind : function(ds){
27636 ds.on("beforeload", this.beforeLoad, this);
27637 ds.on("load", this.onLoad, this);
27638 ds.on("loadexception", this.onLoadError, this);
27639 ds.on("remove", this.updateInfo, this);
27640 ds.on("add", this.updateInfo, this);
27651 * @class Roo.bootstrap.MessageBar
27652 * @extends Roo.bootstrap.Component
27653 * Bootstrap MessageBar class
27654 * @cfg {String} html contents of the MessageBar
27655 * @cfg {String} weight (info | success | warning | danger) default info
27656 * @cfg {String} beforeClass insert the bar before the given class
27657 * @cfg {Boolean} closable (true | false) default false
27658 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27661 * Create a new Element
27662 * @param {Object} config The config object
27665 Roo.bootstrap.MessageBar = function(config){
27666 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27669 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27675 beforeClass: 'bootstrap-sticky-wrap',
27677 getAutoCreate : function(){
27681 cls: 'alert alert-dismissable alert-' + this.weight,
27686 html: this.html || ''
27692 cfg.cls += ' alert-messages-fixed';
27706 onRender : function(ct, position)
27708 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27711 var cfg = Roo.apply({}, this.getAutoCreate());
27715 cfg.cls += ' ' + this.cls;
27718 cfg.style = this.style;
27720 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27722 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27725 this.el.select('>button.close').on('click', this.hide, this);
27731 if (!this.rendered) {
27737 this.fireEvent('show', this);
27743 if (!this.rendered) {
27749 this.fireEvent('hide', this);
27752 update : function()
27754 // var e = this.el.dom.firstChild;
27756 // if(this.closable){
27757 // e = e.nextSibling;
27760 // e.data = this.html || '';
27762 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27778 * @class Roo.bootstrap.Graph
27779 * @extends Roo.bootstrap.Component
27780 * Bootstrap Graph class
27784 @cfg {String} graphtype bar | vbar | pie
27785 @cfg {number} g_x coodinator | centre x (pie)
27786 @cfg {number} g_y coodinator | centre y (pie)
27787 @cfg {number} g_r radius (pie)
27788 @cfg {number} g_height height of the chart (respected by all elements in the set)
27789 @cfg {number} g_width width of the chart (respected by all elements in the set)
27790 @cfg {Object} title The title of the chart
27793 -opts (object) options for the chart
27795 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27796 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27798 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.
27799 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27801 o stretch (boolean)
27803 -opts (object) options for the pie
27806 o startAngle (number)
27807 o endAngle (number)
27811 * Create a new Input
27812 * @param {Object} config The config object
27815 Roo.bootstrap.Graph = function(config){
27816 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27822 * The img click event for the img.
27823 * @param {Roo.EventObject} e
27829 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27840 //g_colors: this.colors,
27847 getAutoCreate : function(){
27858 onRender : function(ct,position){
27861 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27863 if (typeof(Raphael) == 'undefined') {
27864 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27868 this.raphael = Raphael(this.el.dom);
27870 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27871 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27872 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27873 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27875 r.text(160, 10, "Single Series Chart").attr(txtattr);
27876 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27877 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27878 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27880 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27881 r.barchart(330, 10, 300, 220, data1);
27882 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27883 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27886 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27887 // r.barchart(30, 30, 560, 250, xdata, {
27888 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27889 // axis : "0 0 1 1",
27890 // axisxlabels : xdata
27891 // //yvalues : cols,
27894 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27896 // this.load(null,xdata,{
27897 // axis : "0 0 1 1",
27898 // axisxlabels : xdata
27903 load : function(graphtype,xdata,opts)
27905 this.raphael.clear();
27907 graphtype = this.graphtype;
27912 var r = this.raphael,
27913 fin = function () {
27914 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27916 fout = function () {
27917 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27919 pfin = function() {
27920 this.sector.stop();
27921 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27924 this.label[0].stop();
27925 this.label[0].attr({ r: 7.5 });
27926 this.label[1].attr({ "font-weight": 800 });
27929 pfout = function() {
27930 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27933 this.label[0].animate({ r: 5 }, 500, "bounce");
27934 this.label[1].attr({ "font-weight": 400 });
27940 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27943 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27946 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27947 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27949 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27956 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27961 setTitle: function(o)
27966 initEvents: function() {
27969 this.el.on('click', this.onClick, this);
27973 onClick : function(e)
27975 Roo.log('img onclick');
27976 this.fireEvent('click', this, e);
27988 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27991 * @class Roo.bootstrap.dash.NumberBox
27992 * @extends Roo.bootstrap.Component
27993 * Bootstrap NumberBox class
27994 * @cfg {String} headline Box headline
27995 * @cfg {String} content Box content
27996 * @cfg {String} icon Box icon
27997 * @cfg {String} footer Footer text
27998 * @cfg {String} fhref Footer href
28001 * Create a new NumberBox
28002 * @param {Object} config The config object
28006 Roo.bootstrap.dash.NumberBox = function(config){
28007 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28011 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28020 getAutoCreate : function(){
28024 cls : 'small-box ',
28032 cls : 'roo-headline',
28033 html : this.headline
28037 cls : 'roo-content',
28038 html : this.content
28052 cls : 'ion ' + this.icon
28061 cls : 'small-box-footer',
28062 href : this.fhref || '#',
28066 cfg.cn.push(footer);
28073 onRender : function(ct,position){
28074 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28081 setHeadline: function (value)
28083 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28086 setFooter: function (value, href)
28088 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28091 this.el.select('a.small-box-footer',true).first().attr('href', href);
28096 setContent: function (value)
28098 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28101 initEvents: function()
28115 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28118 * @class Roo.bootstrap.dash.TabBox
28119 * @extends Roo.bootstrap.Component
28120 * Bootstrap TabBox class
28121 * @cfg {String} title Title of the TabBox
28122 * @cfg {String} icon Icon of the TabBox
28123 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28124 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28127 * Create a new TabBox
28128 * @param {Object} config The config object
28132 Roo.bootstrap.dash.TabBox = function(config){
28133 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28138 * When a pane is added
28139 * @param {Roo.bootstrap.dash.TabPane} pane
28143 * @event activatepane
28144 * When a pane is activated
28145 * @param {Roo.bootstrap.dash.TabPane} pane
28147 "activatepane" : true
28155 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28160 tabScrollable : false,
28162 getChildContainer : function()
28164 return this.el.select('.tab-content', true).first();
28167 getAutoCreate : function(){
28171 cls: 'pull-left header',
28179 cls: 'fa ' + this.icon
28185 cls: 'nav nav-tabs pull-right',
28191 if(this.tabScrollable){
28198 cls: 'nav nav-tabs pull-right',
28209 cls: 'nav-tabs-custom',
28214 cls: 'tab-content no-padding',
28222 initEvents : function()
28224 //Roo.log('add add pane handler');
28225 this.on('addpane', this.onAddPane, this);
28228 * Updates the box title
28229 * @param {String} html to set the title to.
28231 setTitle : function(value)
28233 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28235 onAddPane : function(pane)
28237 this.panes.push(pane);
28238 //Roo.log('addpane');
28240 // tabs are rendere left to right..
28241 if(!this.showtabs){
28245 var ctr = this.el.select('.nav-tabs', true).first();
28248 var existing = ctr.select('.nav-tab',true);
28249 var qty = existing.getCount();;
28252 var tab = ctr.createChild({
28254 cls : 'nav-tab' + (qty ? '' : ' active'),
28262 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28265 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28267 pane.el.addClass('active');
28272 onTabClick : function(ev,un,ob,pane)
28274 //Roo.log('tab - prev default');
28275 ev.preventDefault();
28278 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28279 pane.tab.addClass('active');
28280 //Roo.log(pane.title);
28281 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28282 // technically we should have a deactivate event.. but maybe add later.
28283 // and it should not de-activate the selected tab...
28284 this.fireEvent('activatepane', pane);
28285 pane.el.addClass('active');
28286 pane.fireEvent('activate');
28291 getActivePane : function()
28294 Roo.each(this.panes, function(p) {
28295 if(p.el.hasClass('active')){
28316 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28318 * @class Roo.bootstrap.TabPane
28319 * @extends Roo.bootstrap.Component
28320 * Bootstrap TabPane class
28321 * @cfg {Boolean} active (false | true) Default false
28322 * @cfg {String} title title of panel
28326 * Create a new TabPane
28327 * @param {Object} config The config object
28330 Roo.bootstrap.dash.TabPane = function(config){
28331 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28337 * When a pane is activated
28338 * @param {Roo.bootstrap.dash.TabPane} pane
28345 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28350 // the tabBox that this is attached to.
28353 getAutoCreate : function()
28361 cfg.cls += ' active';
28366 initEvents : function()
28368 //Roo.log('trigger add pane handler');
28369 this.parent().fireEvent('addpane', this)
28373 * Updates the tab title
28374 * @param {String} html to set the title to.
28376 setTitle: function(str)
28382 this.tab.select('a', true).first().dom.innerHTML = str;
28399 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28402 * @class Roo.bootstrap.menu.Menu
28403 * @extends Roo.bootstrap.Component
28404 * Bootstrap Menu class - container for Menu
28405 * @cfg {String} html Text of the menu
28406 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28407 * @cfg {String} icon Font awesome icon
28408 * @cfg {String} pos Menu align to (top | bottom) default bottom
28412 * Create a new Menu
28413 * @param {Object} config The config object
28417 Roo.bootstrap.menu.Menu = function(config){
28418 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28422 * @event beforeshow
28423 * Fires before this menu is displayed
28424 * @param {Roo.bootstrap.menu.Menu} this
28428 * @event beforehide
28429 * Fires before this menu is hidden
28430 * @param {Roo.bootstrap.menu.Menu} this
28435 * Fires after this menu is displayed
28436 * @param {Roo.bootstrap.menu.Menu} this
28441 * Fires after this menu is hidden
28442 * @param {Roo.bootstrap.menu.Menu} this
28447 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28448 * @param {Roo.bootstrap.menu.Menu} this
28449 * @param {Roo.EventObject} e
28456 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28460 weight : 'default',
28465 getChildContainer : function() {
28466 if(this.isSubMenu){
28470 return this.el.select('ul.dropdown-menu', true).first();
28473 getAutoCreate : function()
28478 cls : 'roo-menu-text',
28486 cls : 'fa ' + this.icon
28497 cls : 'dropdown-button btn btn-' + this.weight,
28502 cls : 'dropdown-toggle btn btn-' + this.weight,
28512 cls : 'dropdown-menu'
28518 if(this.pos == 'top'){
28519 cfg.cls += ' dropup';
28522 if(this.isSubMenu){
28525 cls : 'dropdown-menu'
28532 onRender : function(ct, position)
28534 this.isSubMenu = ct.hasClass('dropdown-submenu');
28536 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28539 initEvents : function()
28541 if(this.isSubMenu){
28545 this.hidden = true;
28547 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28548 this.triggerEl.on('click', this.onTriggerPress, this);
28550 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28551 this.buttonEl.on('click', this.onClick, this);
28557 if(this.isSubMenu){
28561 return this.el.select('ul.dropdown-menu', true).first();
28564 onClick : function(e)
28566 this.fireEvent("click", this, e);
28569 onTriggerPress : function(e)
28571 if (this.isVisible()) {
28578 isVisible : function(){
28579 return !this.hidden;
28584 this.fireEvent("beforeshow", this);
28586 this.hidden = false;
28587 this.el.addClass('open');
28589 Roo.get(document).on("mouseup", this.onMouseUp, this);
28591 this.fireEvent("show", this);
28598 this.fireEvent("beforehide", this);
28600 this.hidden = true;
28601 this.el.removeClass('open');
28603 Roo.get(document).un("mouseup", this.onMouseUp);
28605 this.fireEvent("hide", this);
28608 onMouseUp : function()
28622 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28625 * @class Roo.bootstrap.menu.Item
28626 * @extends Roo.bootstrap.Component
28627 * Bootstrap MenuItem class
28628 * @cfg {Boolean} submenu (true | false) default false
28629 * @cfg {String} html text of the item
28630 * @cfg {String} href the link
28631 * @cfg {Boolean} disable (true | false) default false
28632 * @cfg {Boolean} preventDefault (true | false) default true
28633 * @cfg {String} icon Font awesome icon
28634 * @cfg {String} pos Submenu align to (left | right) default right
28638 * Create a new Item
28639 * @param {Object} config The config object
28643 Roo.bootstrap.menu.Item = function(config){
28644 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28648 * Fires when the mouse is hovering over this menu
28649 * @param {Roo.bootstrap.menu.Item} this
28650 * @param {Roo.EventObject} e
28655 * Fires when the mouse exits this menu
28656 * @param {Roo.bootstrap.menu.Item} this
28657 * @param {Roo.EventObject} e
28663 * The raw click event for the entire grid.
28664 * @param {Roo.EventObject} e
28670 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28675 preventDefault: true,
28680 getAutoCreate : function()
28685 cls : 'roo-menu-item-text',
28693 cls : 'fa ' + this.icon
28702 href : this.href || '#',
28709 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28713 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28715 if(this.pos == 'left'){
28716 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28723 initEvents : function()
28725 this.el.on('mouseover', this.onMouseOver, this);
28726 this.el.on('mouseout', this.onMouseOut, this);
28728 this.el.select('a', true).first().on('click', this.onClick, this);
28732 onClick : function(e)
28734 if(this.preventDefault){
28735 e.preventDefault();
28738 this.fireEvent("click", this, e);
28741 onMouseOver : function(e)
28743 if(this.submenu && this.pos == 'left'){
28744 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28747 this.fireEvent("mouseover", this, e);
28750 onMouseOut : function(e)
28752 this.fireEvent("mouseout", this, e);
28764 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28767 * @class Roo.bootstrap.menu.Separator
28768 * @extends Roo.bootstrap.Component
28769 * Bootstrap Separator class
28772 * Create a new Separator
28773 * @param {Object} config The config object
28777 Roo.bootstrap.menu.Separator = function(config){
28778 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28781 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28783 getAutoCreate : function(){
28804 * @class Roo.bootstrap.Tooltip
28805 * Bootstrap Tooltip class
28806 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28807 * to determine which dom element triggers the tooltip.
28809 * It needs to add support for additional attributes like tooltip-position
28812 * Create a new Toolti
28813 * @param {Object} config The config object
28816 Roo.bootstrap.Tooltip = function(config){
28817 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28819 this.alignment = Roo.bootstrap.Tooltip.alignment;
28821 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28822 this.alignment = config.alignment;
28827 Roo.apply(Roo.bootstrap.Tooltip, {
28829 * @function init initialize tooltip monitoring.
28833 currentTip : false,
28834 currentRegion : false,
28840 Roo.get(document).on('mouseover', this.enter ,this);
28841 Roo.get(document).on('mouseout', this.leave, this);
28844 this.currentTip = new Roo.bootstrap.Tooltip();
28847 enter : function(ev)
28849 var dom = ev.getTarget();
28851 //Roo.log(['enter',dom]);
28852 var el = Roo.fly(dom);
28853 if (this.currentEl) {
28855 //Roo.log(this.currentEl);
28856 //Roo.log(this.currentEl.contains(dom));
28857 if (this.currentEl == el) {
28860 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28866 if (this.currentTip.el) {
28867 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28871 if(!el || el.dom == document){
28877 // you can not look for children, as if el is the body.. then everythign is the child..
28878 if (!el.attr('tooltip')) { //
28879 if (!el.select("[tooltip]").elements.length) {
28882 // is the mouse over this child...?
28883 bindEl = el.select("[tooltip]").first();
28884 var xy = ev.getXY();
28885 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28886 //Roo.log("not in region.");
28889 //Roo.log("child element over..");
28892 this.currentEl = bindEl;
28893 this.currentTip.bind(bindEl);
28894 this.currentRegion = Roo.lib.Region.getRegion(dom);
28895 this.currentTip.enter();
28898 leave : function(ev)
28900 var dom = ev.getTarget();
28901 //Roo.log(['leave',dom]);
28902 if (!this.currentEl) {
28907 if (dom != this.currentEl.dom) {
28910 var xy = ev.getXY();
28911 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28914 // only activate leave if mouse cursor is outside... bounding box..
28919 if (this.currentTip) {
28920 this.currentTip.leave();
28922 //Roo.log('clear currentEl');
28923 this.currentEl = false;
28928 'left' : ['r-l', [-2,0], 'right'],
28929 'right' : ['l-r', [2,0], 'left'],
28930 'bottom' : ['t-b', [0,2], 'top'],
28931 'top' : [ 'b-t', [0,-2], 'bottom']
28937 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28942 delay : null, // can be { show : 300 , hide: 500}
28946 hoverState : null, //???
28948 placement : 'bottom',
28952 getAutoCreate : function(){
28959 cls : 'tooltip-arrow arrow'
28962 cls : 'tooltip-inner'
28969 bind : function(el)
28974 initEvents : function()
28976 this.arrowEl = this.el.select('.arrow', true).first();
28977 this.innerEl = this.el.select('.tooltip-inner', true).first();
28980 enter : function () {
28982 if (this.timeout != null) {
28983 clearTimeout(this.timeout);
28986 this.hoverState = 'in';
28987 //Roo.log("enter - show");
28988 if (!this.delay || !this.delay.show) {
28993 this.timeout = setTimeout(function () {
28994 if (_t.hoverState == 'in') {
28997 }, this.delay.show);
29001 clearTimeout(this.timeout);
29003 this.hoverState = 'out';
29004 if (!this.delay || !this.delay.hide) {
29010 this.timeout = setTimeout(function () {
29011 //Roo.log("leave - timeout");
29013 if (_t.hoverState == 'out') {
29015 Roo.bootstrap.Tooltip.currentEl = false;
29020 show : function (msg)
29023 this.render(document.body);
29026 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29028 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29030 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29032 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29033 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29035 var placement = typeof this.placement == 'function' ?
29036 this.placement.call(this, this.el, on_el) :
29039 var autoToken = /\s?auto?\s?/i;
29040 var autoPlace = autoToken.test(placement);
29042 placement = placement.replace(autoToken, '') || 'top';
29046 //this.el.setXY([0,0]);
29048 //this.el.dom.style.display='block';
29050 //this.el.appendTo(on_el);
29052 var p = this.getPosition();
29053 var box = this.el.getBox();
29059 var align = this.alignment[placement];
29061 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29063 if(placement == 'top' || placement == 'bottom'){
29065 placement = 'right';
29068 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29069 placement = 'left';
29072 var scroll = Roo.select('body', true).first().getScroll();
29074 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29078 align = this.alignment[placement];
29080 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29084 this.el.alignTo(this.bindEl, align[0],align[1]);
29085 //var arrow = this.el.select('.arrow',true).first();
29086 //arrow.set(align[2],
29088 this.el.addClass(placement);
29089 this.el.addClass("bs-tooltip-"+ placement);
29091 this.el.addClass('in fade show');
29093 this.hoverState = null;
29095 if (this.el.hasClass('fade')) {
29110 //this.el.setXY([0,0]);
29111 this.el.removeClass(['show', 'in']);
29127 * @class Roo.bootstrap.LocationPicker
29128 * @extends Roo.bootstrap.Component
29129 * Bootstrap LocationPicker class
29130 * @cfg {Number} latitude Position when init default 0
29131 * @cfg {Number} longitude Position when init default 0
29132 * @cfg {Number} zoom default 15
29133 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29134 * @cfg {Boolean} mapTypeControl default false
29135 * @cfg {Boolean} disableDoubleClickZoom default false
29136 * @cfg {Boolean} scrollwheel default true
29137 * @cfg {Boolean} streetViewControl default false
29138 * @cfg {Number} radius default 0
29139 * @cfg {String} locationName
29140 * @cfg {Boolean} draggable default true
29141 * @cfg {Boolean} enableAutocomplete default false
29142 * @cfg {Boolean} enableReverseGeocode default true
29143 * @cfg {String} markerTitle
29146 * Create a new LocationPicker
29147 * @param {Object} config The config object
29151 Roo.bootstrap.LocationPicker = function(config){
29153 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29158 * Fires when the picker initialized.
29159 * @param {Roo.bootstrap.LocationPicker} this
29160 * @param {Google Location} location
29164 * @event positionchanged
29165 * Fires when the picker position changed.
29166 * @param {Roo.bootstrap.LocationPicker} this
29167 * @param {Google Location} location
29169 positionchanged : true,
29172 * Fires when the map resize.
29173 * @param {Roo.bootstrap.LocationPicker} this
29178 * Fires when the map show.
29179 * @param {Roo.bootstrap.LocationPicker} this
29184 * Fires when the map hide.
29185 * @param {Roo.bootstrap.LocationPicker} this
29190 * Fires when click the map.
29191 * @param {Roo.bootstrap.LocationPicker} this
29192 * @param {Map event} e
29196 * @event mapRightClick
29197 * Fires when right click the map.
29198 * @param {Roo.bootstrap.LocationPicker} this
29199 * @param {Map event} e
29201 mapRightClick : true,
29203 * @event markerClick
29204 * Fires when click the marker.
29205 * @param {Roo.bootstrap.LocationPicker} this
29206 * @param {Map event} e
29208 markerClick : true,
29210 * @event markerRightClick
29211 * Fires when right click the marker.
29212 * @param {Roo.bootstrap.LocationPicker} this
29213 * @param {Map event} e
29215 markerRightClick : true,
29217 * @event OverlayViewDraw
29218 * Fires when OverlayView Draw
29219 * @param {Roo.bootstrap.LocationPicker} this
29221 OverlayViewDraw : true,
29223 * @event OverlayViewOnAdd
29224 * Fires when OverlayView Draw
29225 * @param {Roo.bootstrap.LocationPicker} this
29227 OverlayViewOnAdd : true,
29229 * @event OverlayViewOnRemove
29230 * Fires when OverlayView Draw
29231 * @param {Roo.bootstrap.LocationPicker} this
29233 OverlayViewOnRemove : true,
29235 * @event OverlayViewShow
29236 * Fires when OverlayView Draw
29237 * @param {Roo.bootstrap.LocationPicker} this
29238 * @param {Pixel} cpx
29240 OverlayViewShow : true,
29242 * @event OverlayViewHide
29243 * Fires when OverlayView Draw
29244 * @param {Roo.bootstrap.LocationPicker} this
29246 OverlayViewHide : true,
29248 * @event loadexception
29249 * Fires when load google lib failed.
29250 * @param {Roo.bootstrap.LocationPicker} this
29252 loadexception : true
29257 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29259 gMapContext: false,
29265 mapTypeControl: false,
29266 disableDoubleClickZoom: false,
29268 streetViewControl: false,
29272 enableAutocomplete: false,
29273 enableReverseGeocode: true,
29276 getAutoCreate: function()
29281 cls: 'roo-location-picker'
29287 initEvents: function(ct, position)
29289 if(!this.el.getWidth() || this.isApplied()){
29293 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29298 initial: function()
29300 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29301 this.fireEvent('loadexception', this);
29305 if(!this.mapTypeId){
29306 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29309 this.gMapContext = this.GMapContext();
29311 this.initOverlayView();
29313 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29317 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29318 _this.setPosition(_this.gMapContext.marker.position);
29321 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29322 _this.fireEvent('mapClick', this, event);
29326 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29327 _this.fireEvent('mapRightClick', this, event);
29331 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29332 _this.fireEvent('markerClick', this, event);
29336 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29337 _this.fireEvent('markerRightClick', this, event);
29341 this.setPosition(this.gMapContext.location);
29343 this.fireEvent('initial', this, this.gMapContext.location);
29346 initOverlayView: function()
29350 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29354 _this.fireEvent('OverlayViewDraw', _this);
29359 _this.fireEvent('OverlayViewOnAdd', _this);
29362 onRemove: function()
29364 _this.fireEvent('OverlayViewOnRemove', _this);
29367 show: function(cpx)
29369 _this.fireEvent('OverlayViewShow', _this, cpx);
29374 _this.fireEvent('OverlayViewHide', _this);
29380 fromLatLngToContainerPixel: function(event)
29382 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29385 isApplied: function()
29387 return this.getGmapContext() == false ? false : true;
29390 getGmapContext: function()
29392 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29395 GMapContext: function()
29397 var position = new google.maps.LatLng(this.latitude, this.longitude);
29399 var _map = new google.maps.Map(this.el.dom, {
29402 mapTypeId: this.mapTypeId,
29403 mapTypeControl: this.mapTypeControl,
29404 disableDoubleClickZoom: this.disableDoubleClickZoom,
29405 scrollwheel: this.scrollwheel,
29406 streetViewControl: this.streetViewControl,
29407 locationName: this.locationName,
29408 draggable: this.draggable,
29409 enableAutocomplete: this.enableAutocomplete,
29410 enableReverseGeocode: this.enableReverseGeocode
29413 var _marker = new google.maps.Marker({
29414 position: position,
29416 title: this.markerTitle,
29417 draggable: this.draggable
29424 location: position,
29425 radius: this.radius,
29426 locationName: this.locationName,
29427 addressComponents: {
29428 formatted_address: null,
29429 addressLine1: null,
29430 addressLine2: null,
29432 streetNumber: null,
29436 stateOrProvince: null
29439 domContainer: this.el.dom,
29440 geodecoder: new google.maps.Geocoder()
29444 drawCircle: function(center, radius, options)
29446 if (this.gMapContext.circle != null) {
29447 this.gMapContext.circle.setMap(null);
29451 options = Roo.apply({}, options, {
29452 strokeColor: "#0000FF",
29453 strokeOpacity: .35,
29455 fillColor: "#0000FF",
29459 options.map = this.gMapContext.map;
29460 options.radius = radius;
29461 options.center = center;
29462 this.gMapContext.circle = new google.maps.Circle(options);
29463 return this.gMapContext.circle;
29469 setPosition: function(location)
29471 this.gMapContext.location = location;
29472 this.gMapContext.marker.setPosition(location);
29473 this.gMapContext.map.panTo(location);
29474 this.drawCircle(location, this.gMapContext.radius, {});
29478 if (this.gMapContext.settings.enableReverseGeocode) {
29479 this.gMapContext.geodecoder.geocode({
29480 latLng: this.gMapContext.location
29481 }, function(results, status) {
29483 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29484 _this.gMapContext.locationName = results[0].formatted_address;
29485 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29487 _this.fireEvent('positionchanged', this, location);
29494 this.fireEvent('positionchanged', this, location);
29499 google.maps.event.trigger(this.gMapContext.map, "resize");
29501 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29503 this.fireEvent('resize', this);
29506 setPositionByLatLng: function(latitude, longitude)
29508 this.setPosition(new google.maps.LatLng(latitude, longitude));
29511 getCurrentPosition: function()
29514 latitude: this.gMapContext.location.lat(),
29515 longitude: this.gMapContext.location.lng()
29519 getAddressName: function()
29521 return this.gMapContext.locationName;
29524 getAddressComponents: function()
29526 return this.gMapContext.addressComponents;
29529 address_component_from_google_geocode: function(address_components)
29533 for (var i = 0; i < address_components.length; i++) {
29534 var component = address_components[i];
29535 if (component.types.indexOf("postal_code") >= 0) {
29536 result.postalCode = component.short_name;
29537 } else if (component.types.indexOf("street_number") >= 0) {
29538 result.streetNumber = component.short_name;
29539 } else if (component.types.indexOf("route") >= 0) {
29540 result.streetName = component.short_name;
29541 } else if (component.types.indexOf("neighborhood") >= 0) {
29542 result.city = component.short_name;
29543 } else if (component.types.indexOf("locality") >= 0) {
29544 result.city = component.short_name;
29545 } else if (component.types.indexOf("sublocality") >= 0) {
29546 result.district = component.short_name;
29547 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29548 result.stateOrProvince = component.short_name;
29549 } else if (component.types.indexOf("country") >= 0) {
29550 result.country = component.short_name;
29554 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29555 result.addressLine2 = "";
29559 setZoomLevel: function(zoom)
29561 this.gMapContext.map.setZoom(zoom);
29574 this.fireEvent('show', this);
29585 this.fireEvent('hide', this);
29590 Roo.apply(Roo.bootstrap.LocationPicker, {
29592 OverlayView : function(map, options)
29594 options = options || {};
29601 * @class Roo.bootstrap.Alert
29602 * @extends Roo.bootstrap.Component
29603 * Bootstrap Alert class - shows an alert area box
29605 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29606 Enter a valid email address
29609 * @cfg {String} title The title of alert
29610 * @cfg {String} html The content of alert
29611 * @cfg {String} weight ( success | info | warning | danger )
29612 * @cfg {String} faicon font-awesomeicon
29615 * Create a new alert
29616 * @param {Object} config The config object
29620 Roo.bootstrap.Alert = function(config){
29621 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29625 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29632 getAutoCreate : function()
29641 cls : 'roo-alert-icon'
29646 cls : 'roo-alert-title',
29651 cls : 'roo-alert-text',
29658 cfg.cn[0].cls += ' fa ' + this.faicon;
29662 cfg.cls += ' alert-' + this.weight;
29668 initEvents: function()
29670 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29673 setTitle : function(str)
29675 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29678 setText : function(str)
29680 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29683 setWeight : function(weight)
29686 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29689 this.weight = weight;
29691 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29694 setIcon : function(icon)
29697 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29700 this.faicon = icon;
29702 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29723 * @class Roo.bootstrap.UploadCropbox
29724 * @extends Roo.bootstrap.Component
29725 * Bootstrap UploadCropbox class
29726 * @cfg {String} emptyText show when image has been loaded
29727 * @cfg {String} rotateNotify show when image too small to rotate
29728 * @cfg {Number} errorTimeout default 3000
29729 * @cfg {Number} minWidth default 300
29730 * @cfg {Number} minHeight default 300
29731 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29732 * @cfg {Boolean} isDocument (true|false) default false
29733 * @cfg {String} url action url
29734 * @cfg {String} paramName default 'imageUpload'
29735 * @cfg {String} method default POST
29736 * @cfg {Boolean} loadMask (true|false) default true
29737 * @cfg {Boolean} loadingText default 'Loading...'
29740 * Create a new UploadCropbox
29741 * @param {Object} config The config object
29744 Roo.bootstrap.UploadCropbox = function(config){
29745 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29749 * @event beforeselectfile
29750 * Fire before select file
29751 * @param {Roo.bootstrap.UploadCropbox} this
29753 "beforeselectfile" : true,
29756 * Fire after initEvent
29757 * @param {Roo.bootstrap.UploadCropbox} this
29762 * Fire after initEvent
29763 * @param {Roo.bootstrap.UploadCropbox} this
29764 * @param {String} data
29769 * Fire when preparing the file data
29770 * @param {Roo.bootstrap.UploadCropbox} this
29771 * @param {Object} file
29776 * Fire when get exception
29777 * @param {Roo.bootstrap.UploadCropbox} this
29778 * @param {XMLHttpRequest} xhr
29780 "exception" : true,
29782 * @event beforeloadcanvas
29783 * Fire before load the canvas
29784 * @param {Roo.bootstrap.UploadCropbox} this
29785 * @param {String} src
29787 "beforeloadcanvas" : true,
29790 * Fire when trash image
29791 * @param {Roo.bootstrap.UploadCropbox} this
29796 * Fire when download the image
29797 * @param {Roo.bootstrap.UploadCropbox} this
29801 * @event footerbuttonclick
29802 * Fire when footerbuttonclick
29803 * @param {Roo.bootstrap.UploadCropbox} this
29804 * @param {String} type
29806 "footerbuttonclick" : true,
29810 * @param {Roo.bootstrap.UploadCropbox} this
29815 * Fire when rotate the image
29816 * @param {Roo.bootstrap.UploadCropbox} this
29817 * @param {String} pos
29822 * Fire when inspect the file
29823 * @param {Roo.bootstrap.UploadCropbox} this
29824 * @param {Object} file
29829 * Fire when xhr upload the file
29830 * @param {Roo.bootstrap.UploadCropbox} this
29831 * @param {Object} data
29836 * Fire when arrange the file data
29837 * @param {Roo.bootstrap.UploadCropbox} this
29838 * @param {Object} formData
29843 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29846 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29848 emptyText : 'Click to upload image',
29849 rotateNotify : 'Image is too small to rotate',
29850 errorTimeout : 3000,
29864 cropType : 'image/jpeg',
29866 canvasLoaded : false,
29867 isDocument : false,
29869 paramName : 'imageUpload',
29871 loadingText : 'Loading...',
29874 getAutoCreate : function()
29878 cls : 'roo-upload-cropbox',
29882 cls : 'roo-upload-cropbox-selector',
29887 cls : 'roo-upload-cropbox-body',
29888 style : 'cursor:pointer',
29892 cls : 'roo-upload-cropbox-preview'
29896 cls : 'roo-upload-cropbox-thumb'
29900 cls : 'roo-upload-cropbox-empty-notify',
29901 html : this.emptyText
29905 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29906 html : this.rotateNotify
29912 cls : 'roo-upload-cropbox-footer',
29915 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29925 onRender : function(ct, position)
29927 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29929 if (this.buttons.length) {
29931 Roo.each(this.buttons, function(bb) {
29933 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29935 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29941 this.maskEl = this.el;
29945 initEvents : function()
29947 this.urlAPI = (window.createObjectURL && window) ||
29948 (window.URL && URL.revokeObjectURL && URL) ||
29949 (window.webkitURL && webkitURL);
29951 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29952 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29954 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29955 this.selectorEl.hide();
29957 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29958 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29960 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29961 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29962 this.thumbEl.hide();
29964 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29965 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29967 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29968 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29969 this.errorEl.hide();
29971 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29972 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29973 this.footerEl.hide();
29975 this.setThumbBoxSize();
29981 this.fireEvent('initial', this);
29988 window.addEventListener("resize", function() { _this.resize(); } );
29990 this.bodyEl.on('click', this.beforeSelectFile, this);
29993 this.bodyEl.on('touchstart', this.onTouchStart, this);
29994 this.bodyEl.on('touchmove', this.onTouchMove, this);
29995 this.bodyEl.on('touchend', this.onTouchEnd, this);
29999 this.bodyEl.on('mousedown', this.onMouseDown, this);
30000 this.bodyEl.on('mousemove', this.onMouseMove, this);
30001 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30002 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30003 Roo.get(document).on('mouseup', this.onMouseUp, this);
30006 this.selectorEl.on('change', this.onFileSelected, this);
30012 this.baseScale = 1;
30014 this.baseRotate = 1;
30015 this.dragable = false;
30016 this.pinching = false;
30019 this.cropData = false;
30020 this.notifyEl.dom.innerHTML = this.emptyText;
30022 this.selectorEl.dom.value = '';
30026 resize : function()
30028 if(this.fireEvent('resize', this) != false){
30029 this.setThumbBoxPosition();
30030 this.setCanvasPosition();
30034 onFooterButtonClick : function(e, el, o, type)
30037 case 'rotate-left' :
30038 this.onRotateLeft(e);
30040 case 'rotate-right' :
30041 this.onRotateRight(e);
30044 this.beforeSelectFile(e);
30059 this.fireEvent('footerbuttonclick', this, type);
30062 beforeSelectFile : function(e)
30064 e.preventDefault();
30066 if(this.fireEvent('beforeselectfile', this) != false){
30067 this.selectorEl.dom.click();
30071 onFileSelected : function(e)
30073 e.preventDefault();
30075 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30079 var file = this.selectorEl.dom.files[0];
30081 if(this.fireEvent('inspect', this, file) != false){
30082 this.prepare(file);
30087 trash : function(e)
30089 this.fireEvent('trash', this);
30092 download : function(e)
30094 this.fireEvent('download', this);
30097 loadCanvas : function(src)
30099 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30103 this.imageEl = document.createElement('img');
30107 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30109 this.imageEl.src = src;
30113 onLoadCanvas : function()
30115 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30116 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30118 this.bodyEl.un('click', this.beforeSelectFile, this);
30120 this.notifyEl.hide();
30121 this.thumbEl.show();
30122 this.footerEl.show();
30124 this.baseRotateLevel();
30126 if(this.isDocument){
30127 this.setThumbBoxSize();
30130 this.setThumbBoxPosition();
30132 this.baseScaleLevel();
30138 this.canvasLoaded = true;
30141 this.maskEl.unmask();
30146 setCanvasPosition : function()
30148 if(!this.canvasEl){
30152 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30153 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30155 this.previewEl.setLeft(pw);
30156 this.previewEl.setTop(ph);
30160 onMouseDown : function(e)
30164 this.dragable = true;
30165 this.pinching = false;
30167 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30168 this.dragable = false;
30172 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30173 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30177 onMouseMove : function(e)
30181 if(!this.canvasLoaded){
30185 if (!this.dragable){
30189 var minX = Math.ceil(this.thumbEl.getLeft(true));
30190 var minY = Math.ceil(this.thumbEl.getTop(true));
30192 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30193 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30195 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30196 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30198 x = x - this.mouseX;
30199 y = y - this.mouseY;
30201 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30202 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30204 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30205 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30207 this.previewEl.setLeft(bgX);
30208 this.previewEl.setTop(bgY);
30210 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30211 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30214 onMouseUp : function(e)
30218 this.dragable = false;
30221 onMouseWheel : function(e)
30225 this.startScale = this.scale;
30227 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30229 if(!this.zoomable()){
30230 this.scale = this.startScale;
30239 zoomable : function()
30241 var minScale = this.thumbEl.getWidth() / this.minWidth;
30243 if(this.minWidth < this.minHeight){
30244 minScale = this.thumbEl.getHeight() / this.minHeight;
30247 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30248 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30252 (this.rotate == 0 || this.rotate == 180) &&
30254 width > this.imageEl.OriginWidth ||
30255 height > this.imageEl.OriginHeight ||
30256 (width < this.minWidth && height < this.minHeight)
30264 (this.rotate == 90 || this.rotate == 270) &&
30266 width > this.imageEl.OriginWidth ||
30267 height > this.imageEl.OriginHeight ||
30268 (width < this.minHeight && height < this.minWidth)
30275 !this.isDocument &&
30276 (this.rotate == 0 || this.rotate == 180) &&
30278 width < this.minWidth ||
30279 width > this.imageEl.OriginWidth ||
30280 height < this.minHeight ||
30281 height > this.imageEl.OriginHeight
30288 !this.isDocument &&
30289 (this.rotate == 90 || this.rotate == 270) &&
30291 width < this.minHeight ||
30292 width > this.imageEl.OriginWidth ||
30293 height < this.minWidth ||
30294 height > this.imageEl.OriginHeight
30304 onRotateLeft : function(e)
30306 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30308 var minScale = this.thumbEl.getWidth() / this.minWidth;
30310 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30311 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30313 this.startScale = this.scale;
30315 while (this.getScaleLevel() < minScale){
30317 this.scale = this.scale + 1;
30319 if(!this.zoomable()){
30324 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30325 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30330 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30337 this.scale = this.startScale;
30339 this.onRotateFail();
30344 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30346 if(this.isDocument){
30347 this.setThumbBoxSize();
30348 this.setThumbBoxPosition();
30349 this.setCanvasPosition();
30354 this.fireEvent('rotate', this, 'left');
30358 onRotateRight : function(e)
30360 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30362 var minScale = this.thumbEl.getWidth() / this.minWidth;
30364 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30365 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30367 this.startScale = this.scale;
30369 while (this.getScaleLevel() < minScale){
30371 this.scale = this.scale + 1;
30373 if(!this.zoomable()){
30378 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30379 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30384 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30391 this.scale = this.startScale;
30393 this.onRotateFail();
30398 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30400 if(this.isDocument){
30401 this.setThumbBoxSize();
30402 this.setThumbBoxPosition();
30403 this.setCanvasPosition();
30408 this.fireEvent('rotate', this, 'right');
30411 onRotateFail : function()
30413 this.errorEl.show(true);
30417 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30422 this.previewEl.dom.innerHTML = '';
30424 var canvasEl = document.createElement("canvas");
30426 var contextEl = canvasEl.getContext("2d");
30428 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30429 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30430 var center = this.imageEl.OriginWidth / 2;
30432 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30433 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30434 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30435 center = this.imageEl.OriginHeight / 2;
30438 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30440 contextEl.translate(center, center);
30441 contextEl.rotate(this.rotate * Math.PI / 180);
30443 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30445 this.canvasEl = document.createElement("canvas");
30447 this.contextEl = this.canvasEl.getContext("2d");
30449 switch (this.rotate) {
30452 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30453 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30455 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30460 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30461 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30463 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30464 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);
30468 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30473 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30474 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30476 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30477 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);
30481 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);
30486 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30487 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30489 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30490 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30494 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);
30501 this.previewEl.appendChild(this.canvasEl);
30503 this.setCanvasPosition();
30508 if(!this.canvasLoaded){
30512 var imageCanvas = document.createElement("canvas");
30514 var imageContext = imageCanvas.getContext("2d");
30516 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30517 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30519 var center = imageCanvas.width / 2;
30521 imageContext.translate(center, center);
30523 imageContext.rotate(this.rotate * Math.PI / 180);
30525 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30527 var canvas = document.createElement("canvas");
30529 var context = canvas.getContext("2d");
30531 canvas.width = this.minWidth;
30532 canvas.height = this.minHeight;
30534 switch (this.rotate) {
30537 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30538 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30540 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30541 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30543 var targetWidth = this.minWidth - 2 * x;
30544 var targetHeight = this.minHeight - 2 * y;
30548 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30549 scale = targetWidth / width;
30552 if(x > 0 && y == 0){
30553 scale = targetHeight / height;
30556 if(x > 0 && y > 0){
30557 scale = targetWidth / width;
30559 if(width < height){
30560 scale = targetHeight / height;
30564 context.scale(scale, scale);
30566 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30567 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30569 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30570 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30572 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30577 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30578 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30580 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30581 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30583 var targetWidth = this.minWidth - 2 * x;
30584 var targetHeight = this.minHeight - 2 * y;
30588 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30589 scale = targetWidth / width;
30592 if(x > 0 && y == 0){
30593 scale = targetHeight / height;
30596 if(x > 0 && y > 0){
30597 scale = targetWidth / width;
30599 if(width < height){
30600 scale = targetHeight / height;
30604 context.scale(scale, scale);
30606 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30607 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30609 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30610 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30612 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30614 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30619 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30620 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30622 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30623 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30625 var targetWidth = this.minWidth - 2 * x;
30626 var targetHeight = this.minHeight - 2 * y;
30630 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30631 scale = targetWidth / width;
30634 if(x > 0 && y == 0){
30635 scale = targetHeight / height;
30638 if(x > 0 && y > 0){
30639 scale = targetWidth / width;
30641 if(width < height){
30642 scale = targetHeight / height;
30646 context.scale(scale, scale);
30648 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30649 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30651 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30652 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30654 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30655 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30657 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30662 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30663 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30665 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30666 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30668 var targetWidth = this.minWidth - 2 * x;
30669 var targetHeight = this.minHeight - 2 * y;
30673 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30674 scale = targetWidth / width;
30677 if(x > 0 && y == 0){
30678 scale = targetHeight / height;
30681 if(x > 0 && y > 0){
30682 scale = targetWidth / width;
30684 if(width < height){
30685 scale = targetHeight / height;
30689 context.scale(scale, scale);
30691 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30692 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30694 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30695 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30697 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30699 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30706 this.cropData = canvas.toDataURL(this.cropType);
30708 if(this.fireEvent('crop', this, this.cropData) !== false){
30709 this.process(this.file, this.cropData);
30716 setThumbBoxSize : function()
30720 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30721 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30722 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30724 this.minWidth = width;
30725 this.minHeight = height;
30727 if(this.rotate == 90 || this.rotate == 270){
30728 this.minWidth = height;
30729 this.minHeight = width;
30734 width = Math.ceil(this.minWidth * height / this.minHeight);
30736 if(this.minWidth > this.minHeight){
30738 height = Math.ceil(this.minHeight * width / this.minWidth);
30741 this.thumbEl.setStyle({
30742 width : width + 'px',
30743 height : height + 'px'
30750 setThumbBoxPosition : function()
30752 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30753 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30755 this.thumbEl.setLeft(x);
30756 this.thumbEl.setTop(y);
30760 baseRotateLevel : function()
30762 this.baseRotate = 1;
30765 typeof(this.exif) != 'undefined' &&
30766 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30767 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30769 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30772 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30776 baseScaleLevel : function()
30780 if(this.isDocument){
30782 if(this.baseRotate == 6 || this.baseRotate == 8){
30784 height = this.thumbEl.getHeight();
30785 this.baseScale = height / this.imageEl.OriginWidth;
30787 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30788 width = this.thumbEl.getWidth();
30789 this.baseScale = width / this.imageEl.OriginHeight;
30795 height = this.thumbEl.getHeight();
30796 this.baseScale = height / this.imageEl.OriginHeight;
30798 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30799 width = this.thumbEl.getWidth();
30800 this.baseScale = width / this.imageEl.OriginWidth;
30806 if(this.baseRotate == 6 || this.baseRotate == 8){
30808 width = this.thumbEl.getHeight();
30809 this.baseScale = width / this.imageEl.OriginHeight;
30811 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30812 height = this.thumbEl.getWidth();
30813 this.baseScale = height / this.imageEl.OriginHeight;
30816 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30817 height = this.thumbEl.getWidth();
30818 this.baseScale = height / this.imageEl.OriginHeight;
30820 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30821 width = this.thumbEl.getHeight();
30822 this.baseScale = width / this.imageEl.OriginWidth;
30829 width = this.thumbEl.getWidth();
30830 this.baseScale = width / this.imageEl.OriginWidth;
30832 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30833 height = this.thumbEl.getHeight();
30834 this.baseScale = height / this.imageEl.OriginHeight;
30837 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30839 height = this.thumbEl.getHeight();
30840 this.baseScale = height / this.imageEl.OriginHeight;
30842 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30843 width = this.thumbEl.getWidth();
30844 this.baseScale = width / this.imageEl.OriginWidth;
30852 getScaleLevel : function()
30854 return this.baseScale * Math.pow(1.1, this.scale);
30857 onTouchStart : function(e)
30859 if(!this.canvasLoaded){
30860 this.beforeSelectFile(e);
30864 var touches = e.browserEvent.touches;
30870 if(touches.length == 1){
30871 this.onMouseDown(e);
30875 if(touches.length != 2){
30881 for(var i = 0, finger; finger = touches[i]; i++){
30882 coords.push(finger.pageX, finger.pageY);
30885 var x = Math.pow(coords[0] - coords[2], 2);
30886 var y = Math.pow(coords[1] - coords[3], 2);
30888 this.startDistance = Math.sqrt(x + y);
30890 this.startScale = this.scale;
30892 this.pinching = true;
30893 this.dragable = false;
30897 onTouchMove : function(e)
30899 if(!this.pinching && !this.dragable){
30903 var touches = e.browserEvent.touches;
30910 this.onMouseMove(e);
30916 for(var i = 0, finger; finger = touches[i]; i++){
30917 coords.push(finger.pageX, finger.pageY);
30920 var x = Math.pow(coords[0] - coords[2], 2);
30921 var y = Math.pow(coords[1] - coords[3], 2);
30923 this.endDistance = Math.sqrt(x + y);
30925 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30927 if(!this.zoomable()){
30928 this.scale = this.startScale;
30936 onTouchEnd : function(e)
30938 this.pinching = false;
30939 this.dragable = false;
30943 process : function(file, crop)
30946 this.maskEl.mask(this.loadingText);
30949 this.xhr = new XMLHttpRequest();
30951 file.xhr = this.xhr;
30953 this.xhr.open(this.method, this.url, true);
30956 "Accept": "application/json",
30957 "Cache-Control": "no-cache",
30958 "X-Requested-With": "XMLHttpRequest"
30961 for (var headerName in headers) {
30962 var headerValue = headers[headerName];
30964 this.xhr.setRequestHeader(headerName, headerValue);
30970 this.xhr.onload = function()
30972 _this.xhrOnLoad(_this.xhr);
30975 this.xhr.onerror = function()
30977 _this.xhrOnError(_this.xhr);
30980 var formData = new FormData();
30982 formData.append('returnHTML', 'NO');
30985 formData.append('crop', crop);
30988 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30989 formData.append(this.paramName, file, file.name);
30992 if(typeof(file.filename) != 'undefined'){
30993 formData.append('filename', file.filename);
30996 if(typeof(file.mimetype) != 'undefined'){
30997 formData.append('mimetype', file.mimetype);
31000 if(this.fireEvent('arrange', this, formData) != false){
31001 this.xhr.send(formData);
31005 xhrOnLoad : function(xhr)
31008 this.maskEl.unmask();
31011 if (xhr.readyState !== 4) {
31012 this.fireEvent('exception', this, xhr);
31016 var response = Roo.decode(xhr.responseText);
31018 if(!response.success){
31019 this.fireEvent('exception', this, xhr);
31023 var response = Roo.decode(xhr.responseText);
31025 this.fireEvent('upload', this, response);
31029 xhrOnError : function()
31032 this.maskEl.unmask();
31035 Roo.log('xhr on error');
31037 var response = Roo.decode(xhr.responseText);
31043 prepare : function(file)
31046 this.maskEl.mask(this.loadingText);
31052 if(typeof(file) === 'string'){
31053 this.loadCanvas(file);
31057 if(!file || !this.urlAPI){
31062 this.cropType = file.type;
31066 if(this.fireEvent('prepare', this, this.file) != false){
31068 var reader = new FileReader();
31070 reader.onload = function (e) {
31071 if (e.target.error) {
31072 Roo.log(e.target.error);
31076 var buffer = e.target.result,
31077 dataView = new DataView(buffer),
31079 maxOffset = dataView.byteLength - 4,
31083 if (dataView.getUint16(0) === 0xffd8) {
31084 while (offset < maxOffset) {
31085 markerBytes = dataView.getUint16(offset);
31087 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31088 markerLength = dataView.getUint16(offset + 2) + 2;
31089 if (offset + markerLength > dataView.byteLength) {
31090 Roo.log('Invalid meta data: Invalid segment size.');
31094 if(markerBytes == 0xffe1){
31095 _this.parseExifData(
31102 offset += markerLength;
31112 var url = _this.urlAPI.createObjectURL(_this.file);
31114 _this.loadCanvas(url);
31119 reader.readAsArrayBuffer(this.file);
31125 parseExifData : function(dataView, offset, length)
31127 var tiffOffset = offset + 10,
31131 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31132 // No Exif data, might be XMP data instead
31136 // Check for the ASCII code for "Exif" (0x45786966):
31137 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31138 // No Exif data, might be XMP data instead
31141 if (tiffOffset + 8 > dataView.byteLength) {
31142 Roo.log('Invalid Exif data: Invalid segment size.');
31145 // Check for the two null bytes:
31146 if (dataView.getUint16(offset + 8) !== 0x0000) {
31147 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31150 // Check the byte alignment:
31151 switch (dataView.getUint16(tiffOffset)) {
31153 littleEndian = true;
31156 littleEndian = false;
31159 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31162 // Check for the TIFF tag marker (0x002A):
31163 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31164 Roo.log('Invalid Exif data: Missing TIFF marker.');
31167 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31168 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31170 this.parseExifTags(
31173 tiffOffset + dirOffset,
31178 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31183 if (dirOffset + 6 > dataView.byteLength) {
31184 Roo.log('Invalid Exif data: Invalid directory offset.');
31187 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31188 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31189 if (dirEndOffset + 4 > dataView.byteLength) {
31190 Roo.log('Invalid Exif data: Invalid directory size.');
31193 for (i = 0; i < tagsNumber; i += 1) {
31197 dirOffset + 2 + 12 * i, // tag offset
31201 // Return the offset to the next directory:
31202 return dataView.getUint32(dirEndOffset, littleEndian);
31205 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31207 var tag = dataView.getUint16(offset, littleEndian);
31209 this.exif[tag] = this.getExifValue(
31213 dataView.getUint16(offset + 2, littleEndian), // tag type
31214 dataView.getUint32(offset + 4, littleEndian), // tag length
31219 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31221 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31230 Roo.log('Invalid Exif data: Invalid tag type.');
31234 tagSize = tagType.size * length;
31235 // Determine if the value is contained in the dataOffset bytes,
31236 // or if the value at the dataOffset is a pointer to the actual data:
31237 dataOffset = tagSize > 4 ?
31238 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31239 if (dataOffset + tagSize > dataView.byteLength) {
31240 Roo.log('Invalid Exif data: Invalid data offset.');
31243 if (length === 1) {
31244 return tagType.getValue(dataView, dataOffset, littleEndian);
31247 for (i = 0; i < length; i += 1) {
31248 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31251 if (tagType.ascii) {
31253 // Concatenate the chars:
31254 for (i = 0; i < values.length; i += 1) {
31256 // Ignore the terminating NULL byte(s):
31257 if (c === '\u0000') {
31269 Roo.apply(Roo.bootstrap.UploadCropbox, {
31271 'Orientation': 0x0112
31275 1: 0, //'top-left',
31277 3: 180, //'bottom-right',
31278 // 4: 'bottom-left',
31280 6: 90, //'right-top',
31281 // 7: 'right-bottom',
31282 8: 270 //'left-bottom'
31286 // byte, 8-bit unsigned int:
31288 getValue: function (dataView, dataOffset) {
31289 return dataView.getUint8(dataOffset);
31293 // ascii, 8-bit byte:
31295 getValue: function (dataView, dataOffset) {
31296 return String.fromCharCode(dataView.getUint8(dataOffset));
31301 // short, 16 bit int:
31303 getValue: function (dataView, dataOffset, littleEndian) {
31304 return dataView.getUint16(dataOffset, littleEndian);
31308 // long, 32 bit int:
31310 getValue: function (dataView, dataOffset, littleEndian) {
31311 return dataView.getUint32(dataOffset, littleEndian);
31315 // rational = two long values, first is numerator, second is denominator:
31317 getValue: function (dataView, dataOffset, littleEndian) {
31318 return dataView.getUint32(dataOffset, littleEndian) /
31319 dataView.getUint32(dataOffset + 4, littleEndian);
31323 // slong, 32 bit signed int:
31325 getValue: function (dataView, dataOffset, littleEndian) {
31326 return dataView.getInt32(dataOffset, littleEndian);
31330 // srational, two slongs, first is numerator, second is denominator:
31332 getValue: function (dataView, dataOffset, littleEndian) {
31333 return dataView.getInt32(dataOffset, littleEndian) /
31334 dataView.getInt32(dataOffset + 4, littleEndian);
31344 cls : 'btn-group roo-upload-cropbox-rotate-left',
31345 action : 'rotate-left',
31349 cls : 'btn btn-default',
31350 html : '<i class="fa fa-undo"></i>'
31356 cls : 'btn-group roo-upload-cropbox-picture',
31357 action : 'picture',
31361 cls : 'btn btn-default',
31362 html : '<i class="fa fa-picture-o"></i>'
31368 cls : 'btn-group roo-upload-cropbox-rotate-right',
31369 action : 'rotate-right',
31373 cls : 'btn btn-default',
31374 html : '<i class="fa fa-repeat"></i>'
31382 cls : 'btn-group roo-upload-cropbox-rotate-left',
31383 action : 'rotate-left',
31387 cls : 'btn btn-default',
31388 html : '<i class="fa fa-undo"></i>'
31394 cls : 'btn-group roo-upload-cropbox-download',
31395 action : 'download',
31399 cls : 'btn btn-default',
31400 html : '<i class="fa fa-download"></i>'
31406 cls : 'btn-group roo-upload-cropbox-crop',
31411 cls : 'btn btn-default',
31412 html : '<i class="fa fa-crop"></i>'
31418 cls : 'btn-group roo-upload-cropbox-trash',
31423 cls : 'btn btn-default',
31424 html : '<i class="fa fa-trash"></i>'
31430 cls : 'btn-group roo-upload-cropbox-rotate-right',
31431 action : 'rotate-right',
31435 cls : 'btn btn-default',
31436 html : '<i class="fa fa-repeat"></i>'
31444 cls : 'btn-group roo-upload-cropbox-rotate-left',
31445 action : 'rotate-left',
31449 cls : 'btn btn-default',
31450 html : '<i class="fa fa-undo"></i>'
31456 cls : 'btn-group roo-upload-cropbox-rotate-right',
31457 action : 'rotate-right',
31461 cls : 'btn btn-default',
31462 html : '<i class="fa fa-repeat"></i>'
31475 * @class Roo.bootstrap.DocumentManager
31476 * @extends Roo.bootstrap.Component
31477 * Bootstrap DocumentManager class
31478 * @cfg {String} paramName default 'imageUpload'
31479 * @cfg {String} toolTipName default 'filename'
31480 * @cfg {String} method default POST
31481 * @cfg {String} url action url
31482 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31483 * @cfg {Boolean} multiple multiple upload default true
31484 * @cfg {Number} thumbSize default 300
31485 * @cfg {String} fieldLabel
31486 * @cfg {Number} labelWidth default 4
31487 * @cfg {String} labelAlign (left|top) default left
31488 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31489 * @cfg {Number} labellg set the width of label (1-12)
31490 * @cfg {Number} labelmd set the width of label (1-12)
31491 * @cfg {Number} labelsm set the width of label (1-12)
31492 * @cfg {Number} labelxs set the width of label (1-12)
31495 * Create a new DocumentManager
31496 * @param {Object} config The config object
31499 Roo.bootstrap.DocumentManager = function(config){
31500 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31503 this.delegates = [];
31508 * Fire when initial the DocumentManager
31509 * @param {Roo.bootstrap.DocumentManager} this
31514 * inspect selected file
31515 * @param {Roo.bootstrap.DocumentManager} this
31516 * @param {File} file
31521 * Fire when xhr load exception
31522 * @param {Roo.bootstrap.DocumentManager} this
31523 * @param {XMLHttpRequest} xhr
31525 "exception" : true,
31527 * @event afterupload
31528 * Fire when xhr load exception
31529 * @param {Roo.bootstrap.DocumentManager} this
31530 * @param {XMLHttpRequest} xhr
31532 "afterupload" : true,
31535 * prepare the form data
31536 * @param {Roo.bootstrap.DocumentManager} this
31537 * @param {Object} formData
31542 * Fire when remove the file
31543 * @param {Roo.bootstrap.DocumentManager} this
31544 * @param {Object} file
31549 * Fire after refresh the file
31550 * @param {Roo.bootstrap.DocumentManager} this
31555 * Fire after click the image
31556 * @param {Roo.bootstrap.DocumentManager} this
31557 * @param {Object} file
31562 * Fire when upload a image and editable set to true
31563 * @param {Roo.bootstrap.DocumentManager} this
31564 * @param {Object} file
31568 * @event beforeselectfile
31569 * Fire before select file
31570 * @param {Roo.bootstrap.DocumentManager} this
31572 "beforeselectfile" : true,
31575 * Fire before process file
31576 * @param {Roo.bootstrap.DocumentManager} this
31577 * @param {Object} file
31581 * @event previewrendered
31582 * Fire when preview rendered
31583 * @param {Roo.bootstrap.DocumentManager} this
31584 * @param {Object} file
31586 "previewrendered" : true,
31589 "previewResize" : true
31594 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31603 paramName : 'imageUpload',
31604 toolTipName : 'filename',
31607 labelAlign : 'left',
31617 getAutoCreate : function()
31619 var managerWidget = {
31621 cls : 'roo-document-manager',
31625 cls : 'roo-document-manager-selector',
31630 cls : 'roo-document-manager-uploader',
31634 cls : 'roo-document-manager-upload-btn',
31635 html : '<i class="fa fa-plus"></i>'
31646 cls : 'column col-md-12',
31651 if(this.fieldLabel.length){
31656 cls : 'column col-md-12',
31657 html : this.fieldLabel
31661 cls : 'column col-md-12',
31666 if(this.labelAlign == 'left'){
31671 html : this.fieldLabel
31680 if(this.labelWidth > 12){
31681 content[0].style = "width: " + this.labelWidth + 'px';
31684 if(this.labelWidth < 13 && this.labelmd == 0){
31685 this.labelmd = this.labelWidth;
31688 if(this.labellg > 0){
31689 content[0].cls += ' col-lg-' + this.labellg;
31690 content[1].cls += ' col-lg-' + (12 - this.labellg);
31693 if(this.labelmd > 0){
31694 content[0].cls += ' col-md-' + this.labelmd;
31695 content[1].cls += ' col-md-' + (12 - this.labelmd);
31698 if(this.labelsm > 0){
31699 content[0].cls += ' col-sm-' + this.labelsm;
31700 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31703 if(this.labelxs > 0){
31704 content[0].cls += ' col-xs-' + this.labelxs;
31705 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31713 cls : 'row clearfix',
31721 initEvents : function()
31723 this.managerEl = this.el.select('.roo-document-manager', true).first();
31724 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31726 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31727 this.selectorEl.hide();
31730 this.selectorEl.attr('multiple', 'multiple');
31733 this.selectorEl.on('change', this.onFileSelected, this);
31735 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31736 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31738 this.uploader.on('click', this.onUploaderClick, this);
31740 this.renderProgressDialog();
31744 window.addEventListener("resize", function() { _this.refresh(); } );
31746 this.fireEvent('initial', this);
31749 renderProgressDialog : function()
31753 this.progressDialog = new Roo.bootstrap.Modal({
31754 cls : 'roo-document-manager-progress-dialog',
31755 allow_close : false,
31766 btnclick : function() {
31767 _this.uploadCancel();
31773 this.progressDialog.render(Roo.get(document.body));
31775 this.progress = new Roo.bootstrap.Progress({
31776 cls : 'roo-document-manager-progress',
31781 this.progress.render(this.progressDialog.getChildContainer());
31783 this.progressBar = new Roo.bootstrap.ProgressBar({
31784 cls : 'roo-document-manager-progress-bar',
31787 aria_valuemax : 12,
31791 this.progressBar.render(this.progress.getChildContainer());
31794 onUploaderClick : function(e)
31796 e.preventDefault();
31798 if(this.fireEvent('beforeselectfile', this) != false){
31799 this.selectorEl.dom.click();
31804 onFileSelected : function(e)
31806 e.preventDefault();
31808 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31812 Roo.each(this.selectorEl.dom.files, function(file){
31813 if(this.fireEvent('inspect', this, file) != false){
31814 this.files.push(file);
31824 this.selectorEl.dom.value = '';
31826 if(!this.files || !this.files.length){
31830 if(this.boxes > 0 && this.files.length > this.boxes){
31831 this.files = this.files.slice(0, this.boxes);
31834 this.uploader.show();
31836 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31837 this.uploader.hide();
31846 Roo.each(this.files, function(file){
31848 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31849 var f = this.renderPreview(file);
31854 if(file.type.indexOf('image') != -1){
31855 this.delegates.push(
31857 _this.process(file);
31858 }).createDelegate(this)
31866 _this.process(file);
31867 }).createDelegate(this)
31872 this.files = files;
31874 this.delegates = this.delegates.concat(docs);
31876 if(!this.delegates.length){
31881 this.progressBar.aria_valuemax = this.delegates.length;
31888 arrange : function()
31890 if(!this.delegates.length){
31891 this.progressDialog.hide();
31896 var delegate = this.delegates.shift();
31898 this.progressDialog.show();
31900 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31902 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31907 refresh : function()
31909 this.uploader.show();
31911 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31912 this.uploader.hide();
31915 Roo.isTouch ? this.closable(false) : this.closable(true);
31917 this.fireEvent('refresh', this);
31920 onRemove : function(e, el, o)
31922 e.preventDefault();
31924 this.fireEvent('remove', this, o);
31928 remove : function(o)
31932 Roo.each(this.files, function(file){
31933 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31942 this.files = files;
31949 Roo.each(this.files, function(file){
31954 file.target.remove();
31963 onClick : function(e, el, o)
31965 e.preventDefault();
31967 this.fireEvent('click', this, o);
31971 closable : function(closable)
31973 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31975 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31987 xhrOnLoad : function(xhr)
31989 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31993 if (xhr.readyState !== 4) {
31995 this.fireEvent('exception', this, xhr);
31999 var response = Roo.decode(xhr.responseText);
32001 if(!response.success){
32003 this.fireEvent('exception', this, xhr);
32007 var file = this.renderPreview(response.data);
32009 this.files.push(file);
32013 this.fireEvent('afterupload', this, xhr);
32017 xhrOnError : function(xhr)
32019 Roo.log('xhr on error');
32021 var response = Roo.decode(xhr.responseText);
32028 process : function(file)
32030 if(this.fireEvent('process', this, file) !== false){
32031 if(this.editable && file.type.indexOf('image') != -1){
32032 this.fireEvent('edit', this, file);
32036 this.uploadStart(file, false);
32043 uploadStart : function(file, crop)
32045 this.xhr = new XMLHttpRequest();
32047 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32052 file.xhr = this.xhr;
32054 this.managerEl.createChild({
32056 cls : 'roo-document-manager-loading',
32060 tooltip : file.name,
32061 cls : 'roo-document-manager-thumb',
32062 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32068 this.xhr.open(this.method, this.url, true);
32071 "Accept": "application/json",
32072 "Cache-Control": "no-cache",
32073 "X-Requested-With": "XMLHttpRequest"
32076 for (var headerName in headers) {
32077 var headerValue = headers[headerName];
32079 this.xhr.setRequestHeader(headerName, headerValue);
32085 this.xhr.onload = function()
32087 _this.xhrOnLoad(_this.xhr);
32090 this.xhr.onerror = function()
32092 _this.xhrOnError(_this.xhr);
32095 var formData = new FormData();
32097 formData.append('returnHTML', 'NO');
32100 formData.append('crop', crop);
32103 formData.append(this.paramName, file, file.name);
32110 if(this.fireEvent('prepare', this, formData, options) != false){
32112 if(options.manually){
32116 this.xhr.send(formData);
32120 this.uploadCancel();
32123 uploadCancel : function()
32129 this.delegates = [];
32131 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32138 renderPreview : function(file)
32140 if(typeof(file.target) != 'undefined' && file.target){
32144 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32146 var previewEl = this.managerEl.createChild({
32148 cls : 'roo-document-manager-preview',
32152 tooltip : file[this.toolTipName],
32153 cls : 'roo-document-manager-thumb',
32154 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32159 html : '<i class="fa fa-times-circle"></i>'
32164 var close = previewEl.select('button.close', true).first();
32166 close.on('click', this.onRemove, this, file);
32168 file.target = previewEl;
32170 var image = previewEl.select('img', true).first();
32174 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32176 image.on('click', this.onClick, this, file);
32178 this.fireEvent('previewrendered', this, file);
32184 onPreviewLoad : function(file, image)
32186 if(typeof(file.target) == 'undefined' || !file.target){
32190 var width = image.dom.naturalWidth || image.dom.width;
32191 var height = image.dom.naturalHeight || image.dom.height;
32193 if(!this.previewResize) {
32197 if(width > height){
32198 file.target.addClass('wide');
32202 file.target.addClass('tall');
32207 uploadFromSource : function(file, crop)
32209 this.xhr = new XMLHttpRequest();
32211 this.managerEl.createChild({
32213 cls : 'roo-document-manager-loading',
32217 tooltip : file.name,
32218 cls : 'roo-document-manager-thumb',
32219 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32225 this.xhr.open(this.method, this.url, true);
32228 "Accept": "application/json",
32229 "Cache-Control": "no-cache",
32230 "X-Requested-With": "XMLHttpRequest"
32233 for (var headerName in headers) {
32234 var headerValue = headers[headerName];
32236 this.xhr.setRequestHeader(headerName, headerValue);
32242 this.xhr.onload = function()
32244 _this.xhrOnLoad(_this.xhr);
32247 this.xhr.onerror = function()
32249 _this.xhrOnError(_this.xhr);
32252 var formData = new FormData();
32254 formData.append('returnHTML', 'NO');
32256 formData.append('crop', crop);
32258 if(typeof(file.filename) != 'undefined'){
32259 formData.append('filename', file.filename);
32262 if(typeof(file.mimetype) != 'undefined'){
32263 formData.append('mimetype', file.mimetype);
32268 if(this.fireEvent('prepare', this, formData) != false){
32269 this.xhr.send(formData);
32279 * @class Roo.bootstrap.DocumentViewer
32280 * @extends Roo.bootstrap.Component
32281 * Bootstrap DocumentViewer class
32282 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32283 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32286 * Create a new DocumentViewer
32287 * @param {Object} config The config object
32290 Roo.bootstrap.DocumentViewer = function(config){
32291 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32296 * Fire after initEvent
32297 * @param {Roo.bootstrap.DocumentViewer} this
32303 * @param {Roo.bootstrap.DocumentViewer} this
32308 * Fire after download button
32309 * @param {Roo.bootstrap.DocumentViewer} this
32314 * Fire after trash button
32315 * @param {Roo.bootstrap.DocumentViewer} this
32322 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32324 showDownload : true,
32328 getAutoCreate : function()
32332 cls : 'roo-document-viewer',
32336 cls : 'roo-document-viewer-body',
32340 cls : 'roo-document-viewer-thumb',
32344 cls : 'roo-document-viewer-image'
32352 cls : 'roo-document-viewer-footer',
32355 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32359 cls : 'btn-group roo-document-viewer-download',
32363 cls : 'btn btn-default',
32364 html : '<i class="fa fa-download"></i>'
32370 cls : 'btn-group roo-document-viewer-trash',
32374 cls : 'btn btn-default',
32375 html : '<i class="fa fa-trash"></i>'
32388 initEvents : function()
32390 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32391 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32393 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32394 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32396 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32397 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32399 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32400 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32402 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32403 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32405 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32406 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32408 this.bodyEl.on('click', this.onClick, this);
32409 this.downloadBtn.on('click', this.onDownload, this);
32410 this.trashBtn.on('click', this.onTrash, this);
32412 this.downloadBtn.hide();
32413 this.trashBtn.hide();
32415 if(this.showDownload){
32416 this.downloadBtn.show();
32419 if(this.showTrash){
32420 this.trashBtn.show();
32423 if(!this.showDownload && !this.showTrash) {
32424 this.footerEl.hide();
32429 initial : function()
32431 this.fireEvent('initial', this);
32435 onClick : function(e)
32437 e.preventDefault();
32439 this.fireEvent('click', this);
32442 onDownload : function(e)
32444 e.preventDefault();
32446 this.fireEvent('download', this);
32449 onTrash : function(e)
32451 e.preventDefault();
32453 this.fireEvent('trash', this);
32465 * @class Roo.bootstrap.NavProgressBar
32466 * @extends Roo.bootstrap.Component
32467 * Bootstrap NavProgressBar class
32470 * Create a new nav progress bar
32471 * @param {Object} config The config object
32474 Roo.bootstrap.NavProgressBar = function(config){
32475 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32477 this.bullets = this.bullets || [];
32479 // Roo.bootstrap.NavProgressBar.register(this);
32483 * Fires when the active item changes
32484 * @param {Roo.bootstrap.NavProgressBar} this
32485 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32486 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32493 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32498 getAutoCreate : function()
32500 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32504 cls : 'roo-navigation-bar-group',
32508 cls : 'roo-navigation-top-bar'
32512 cls : 'roo-navigation-bullets-bar',
32516 cls : 'roo-navigation-bar'
32523 cls : 'roo-navigation-bottom-bar'
32533 initEvents: function()
32538 onRender : function(ct, position)
32540 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32542 if(this.bullets.length){
32543 Roo.each(this.bullets, function(b){
32552 addItem : function(cfg)
32554 var item = new Roo.bootstrap.NavProgressItem(cfg);
32556 item.parentId = this.id;
32557 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32560 var top = new Roo.bootstrap.Element({
32562 cls : 'roo-navigation-bar-text'
32565 var bottom = new Roo.bootstrap.Element({
32567 cls : 'roo-navigation-bar-text'
32570 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32571 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32573 var topText = new Roo.bootstrap.Element({
32575 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32578 var bottomText = new Roo.bootstrap.Element({
32580 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32583 topText.onRender(top.el, null);
32584 bottomText.onRender(bottom.el, null);
32587 item.bottomEl = bottom;
32590 this.barItems.push(item);
32595 getActive : function()
32597 var active = false;
32599 Roo.each(this.barItems, function(v){
32601 if (!v.isActive()) {
32613 setActiveItem : function(item)
32617 Roo.each(this.barItems, function(v){
32618 if (v.rid == item.rid) {
32622 if (v.isActive()) {
32623 v.setActive(false);
32628 item.setActive(true);
32630 this.fireEvent('changed', this, item, prev);
32633 getBarItem: function(rid)
32637 Roo.each(this.barItems, function(e) {
32638 if (e.rid != rid) {
32649 indexOfItem : function(item)
32653 Roo.each(this.barItems, function(v, i){
32655 if (v.rid != item.rid) {
32666 setActiveNext : function()
32668 var i = this.indexOfItem(this.getActive());
32670 if (i > this.barItems.length) {
32674 this.setActiveItem(this.barItems[i+1]);
32677 setActivePrev : function()
32679 var i = this.indexOfItem(this.getActive());
32685 this.setActiveItem(this.barItems[i-1]);
32688 format : function()
32690 if(!this.barItems.length){
32694 var width = 100 / this.barItems.length;
32696 Roo.each(this.barItems, function(i){
32697 i.el.setStyle('width', width + '%');
32698 i.topEl.el.setStyle('width', width + '%');
32699 i.bottomEl.el.setStyle('width', width + '%');
32708 * Nav Progress Item
32713 * @class Roo.bootstrap.NavProgressItem
32714 * @extends Roo.bootstrap.Component
32715 * Bootstrap NavProgressItem class
32716 * @cfg {String} rid the reference id
32717 * @cfg {Boolean} active (true|false) Is item active default false
32718 * @cfg {Boolean} disabled (true|false) Is item active default false
32719 * @cfg {String} html
32720 * @cfg {String} position (top|bottom) text position default bottom
32721 * @cfg {String} icon show icon instead of number
32724 * Create a new NavProgressItem
32725 * @param {Object} config The config object
32727 Roo.bootstrap.NavProgressItem = function(config){
32728 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32733 * The raw click event for the entire grid.
32734 * @param {Roo.bootstrap.NavProgressItem} this
32735 * @param {Roo.EventObject} e
32742 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32748 position : 'bottom',
32751 getAutoCreate : function()
32753 var iconCls = 'roo-navigation-bar-item-icon';
32755 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32759 cls: 'roo-navigation-bar-item',
32769 cfg.cls += ' active';
32772 cfg.cls += ' disabled';
32778 disable : function()
32780 this.setDisabled(true);
32783 enable : function()
32785 this.setDisabled(false);
32788 initEvents: function()
32790 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32792 this.iconEl.on('click', this.onClick, this);
32795 onClick : function(e)
32797 e.preventDefault();
32803 if(this.fireEvent('click', this, e) === false){
32807 this.parent().setActiveItem(this);
32810 isActive: function ()
32812 return this.active;
32815 setActive : function(state)
32817 if(this.active == state){
32821 this.active = state;
32824 this.el.addClass('active');
32828 this.el.removeClass('active');
32833 setDisabled : function(state)
32835 if(this.disabled == state){
32839 this.disabled = state;
32842 this.el.addClass('disabled');
32846 this.el.removeClass('disabled');
32849 tooltipEl : function()
32851 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32864 * @class Roo.bootstrap.FieldLabel
32865 * @extends Roo.bootstrap.Component
32866 * Bootstrap FieldLabel class
32867 * @cfg {String} html contents of the element
32868 * @cfg {String} tag tag of the element default label
32869 * @cfg {String} cls class of the element
32870 * @cfg {String} target label target
32871 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32872 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32873 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32874 * @cfg {String} iconTooltip default "This field is required"
32875 * @cfg {String} indicatorpos (left|right) default left
32878 * Create a new FieldLabel
32879 * @param {Object} config The config object
32882 Roo.bootstrap.FieldLabel = function(config){
32883 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32888 * Fires after the field has been marked as invalid.
32889 * @param {Roo.form.FieldLabel} this
32890 * @param {String} msg The validation message
32895 * Fires after the field has been validated with no errors.
32896 * @param {Roo.form.FieldLabel} this
32902 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32909 invalidClass : 'has-warning',
32910 validClass : 'has-success',
32911 iconTooltip : 'This field is required',
32912 indicatorpos : 'left',
32914 getAutoCreate : function(){
32917 if (!this.allowBlank) {
32923 cls : 'roo-bootstrap-field-label ' + this.cls,
32928 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32929 tooltip : this.iconTooltip
32938 if(this.indicatorpos == 'right'){
32941 cls : 'roo-bootstrap-field-label ' + this.cls,
32950 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32951 tooltip : this.iconTooltip
32960 initEvents: function()
32962 Roo.bootstrap.Element.superclass.initEvents.call(this);
32964 this.indicator = this.indicatorEl();
32966 if(this.indicator){
32967 this.indicator.removeClass('visible');
32968 this.indicator.addClass('invisible');
32971 Roo.bootstrap.FieldLabel.register(this);
32974 indicatorEl : function()
32976 var indicator = this.el.select('i.roo-required-indicator',true).first();
32987 * Mark this field as valid
32989 markValid : function()
32991 if(this.indicator){
32992 this.indicator.removeClass('visible');
32993 this.indicator.addClass('invisible');
32995 if (Roo.bootstrap.version == 3) {
32996 this.el.removeClass(this.invalidClass);
32997 this.el.addClass(this.validClass);
32999 this.el.removeClass('is-invalid');
33000 this.el.addClass('is-valid');
33004 this.fireEvent('valid', this);
33008 * Mark this field as invalid
33009 * @param {String} msg The validation message
33011 markInvalid : function(msg)
33013 if(this.indicator){
33014 this.indicator.removeClass('invisible');
33015 this.indicator.addClass('visible');
33017 if (Roo.bootstrap.version == 3) {
33018 this.el.removeClass(this.validClass);
33019 this.el.addClass(this.invalidClass);
33021 this.el.removeClass('is-valid');
33022 this.el.addClass('is-invalid');
33026 this.fireEvent('invalid', this, msg);
33032 Roo.apply(Roo.bootstrap.FieldLabel, {
33037 * register a FieldLabel Group
33038 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33040 register : function(label)
33042 if(this.groups.hasOwnProperty(label.target)){
33046 this.groups[label.target] = label;
33050 * fetch a FieldLabel Group based on the target
33051 * @param {string} target
33052 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33054 get: function(target) {
33055 if (typeof(this.groups[target]) == 'undefined') {
33059 return this.groups[target] ;
33068 * page DateSplitField.
33074 * @class Roo.bootstrap.DateSplitField
33075 * @extends Roo.bootstrap.Component
33076 * Bootstrap DateSplitField class
33077 * @cfg {string} fieldLabel - the label associated
33078 * @cfg {Number} labelWidth set the width of label (0-12)
33079 * @cfg {String} labelAlign (top|left)
33080 * @cfg {Boolean} dayAllowBlank (true|false) default false
33081 * @cfg {Boolean} monthAllowBlank (true|false) default false
33082 * @cfg {Boolean} yearAllowBlank (true|false) default false
33083 * @cfg {string} dayPlaceholder
33084 * @cfg {string} monthPlaceholder
33085 * @cfg {string} yearPlaceholder
33086 * @cfg {string} dayFormat default 'd'
33087 * @cfg {string} monthFormat default 'm'
33088 * @cfg {string} yearFormat default 'Y'
33089 * @cfg {Number} labellg set the width of label (1-12)
33090 * @cfg {Number} labelmd set the width of label (1-12)
33091 * @cfg {Number} labelsm set the width of label (1-12)
33092 * @cfg {Number} labelxs set the width of label (1-12)
33096 * Create a new DateSplitField
33097 * @param {Object} config The config object
33100 Roo.bootstrap.DateSplitField = function(config){
33101 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33107 * getting the data of years
33108 * @param {Roo.bootstrap.DateSplitField} this
33109 * @param {Object} years
33114 * getting the data of days
33115 * @param {Roo.bootstrap.DateSplitField} this
33116 * @param {Object} days
33121 * Fires after the field has been marked as invalid.
33122 * @param {Roo.form.Field} this
33123 * @param {String} msg The validation message
33128 * Fires after the field has been validated with no errors.
33129 * @param {Roo.form.Field} this
33135 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33138 labelAlign : 'top',
33140 dayAllowBlank : false,
33141 monthAllowBlank : false,
33142 yearAllowBlank : false,
33143 dayPlaceholder : '',
33144 monthPlaceholder : '',
33145 yearPlaceholder : '',
33149 isFormField : true,
33155 getAutoCreate : function()
33159 cls : 'row roo-date-split-field-group',
33164 cls : 'form-hidden-field roo-date-split-field-group-value',
33170 var labelCls = 'col-md-12';
33171 var contentCls = 'col-md-4';
33173 if(this.fieldLabel){
33177 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33181 html : this.fieldLabel
33186 if(this.labelAlign == 'left'){
33188 if(this.labelWidth > 12){
33189 label.style = "width: " + this.labelWidth + 'px';
33192 if(this.labelWidth < 13 && this.labelmd == 0){
33193 this.labelmd = this.labelWidth;
33196 if(this.labellg > 0){
33197 labelCls = ' col-lg-' + this.labellg;
33198 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33201 if(this.labelmd > 0){
33202 labelCls = ' col-md-' + this.labelmd;
33203 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33206 if(this.labelsm > 0){
33207 labelCls = ' col-sm-' + this.labelsm;
33208 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33211 if(this.labelxs > 0){
33212 labelCls = ' col-xs-' + this.labelxs;
33213 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33217 label.cls += ' ' + labelCls;
33219 cfg.cn.push(label);
33222 Roo.each(['day', 'month', 'year'], function(t){
33225 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33232 inputEl: function ()
33234 return this.el.select('.roo-date-split-field-group-value', true).first();
33237 onRender : function(ct, position)
33241 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33243 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33245 this.dayField = new Roo.bootstrap.ComboBox({
33246 allowBlank : this.dayAllowBlank,
33247 alwaysQuery : true,
33248 displayField : 'value',
33251 forceSelection : true,
33253 placeholder : this.dayPlaceholder,
33254 selectOnFocus : true,
33255 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33256 triggerAction : 'all',
33258 valueField : 'value',
33259 store : new Roo.data.SimpleStore({
33260 data : (function() {
33262 _this.fireEvent('days', _this, days);
33265 fields : [ 'value' ]
33268 select : function (_self, record, index)
33270 _this.setValue(_this.getValue());
33275 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33277 this.monthField = new Roo.bootstrap.MonthField({
33278 after : '<i class=\"fa fa-calendar\"></i>',
33279 allowBlank : this.monthAllowBlank,
33280 placeholder : this.monthPlaceholder,
33283 render : function (_self)
33285 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33286 e.preventDefault();
33290 select : function (_self, oldvalue, newvalue)
33292 _this.setValue(_this.getValue());
33297 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33299 this.yearField = new Roo.bootstrap.ComboBox({
33300 allowBlank : this.yearAllowBlank,
33301 alwaysQuery : true,
33302 displayField : 'value',
33305 forceSelection : true,
33307 placeholder : this.yearPlaceholder,
33308 selectOnFocus : true,
33309 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33310 triggerAction : 'all',
33312 valueField : 'value',
33313 store : new Roo.data.SimpleStore({
33314 data : (function() {
33316 _this.fireEvent('years', _this, years);
33319 fields : [ 'value' ]
33322 select : function (_self, record, index)
33324 _this.setValue(_this.getValue());
33329 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33332 setValue : function(v, format)
33334 this.inputEl.dom.value = v;
33336 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33338 var d = Date.parseDate(v, f);
33345 this.setDay(d.format(this.dayFormat));
33346 this.setMonth(d.format(this.monthFormat));
33347 this.setYear(d.format(this.yearFormat));
33354 setDay : function(v)
33356 this.dayField.setValue(v);
33357 this.inputEl.dom.value = this.getValue();
33362 setMonth : function(v)
33364 this.monthField.setValue(v, true);
33365 this.inputEl.dom.value = this.getValue();
33370 setYear : function(v)
33372 this.yearField.setValue(v);
33373 this.inputEl.dom.value = this.getValue();
33378 getDay : function()
33380 return this.dayField.getValue();
33383 getMonth : function()
33385 return this.monthField.getValue();
33388 getYear : function()
33390 return this.yearField.getValue();
33393 getValue : function()
33395 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33397 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33407 this.inputEl.dom.value = '';
33412 validate : function()
33414 var d = this.dayField.validate();
33415 var m = this.monthField.validate();
33416 var y = this.yearField.validate();
33421 (!this.dayAllowBlank && !d) ||
33422 (!this.monthAllowBlank && !m) ||
33423 (!this.yearAllowBlank && !y)
33428 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33437 this.markInvalid();
33442 markValid : function()
33445 var label = this.el.select('label', true).first();
33446 var icon = this.el.select('i.fa-star', true).first();
33452 this.fireEvent('valid', this);
33456 * Mark this field as invalid
33457 * @param {String} msg The validation message
33459 markInvalid : function(msg)
33462 var label = this.el.select('label', true).first();
33463 var icon = this.el.select('i.fa-star', true).first();
33465 if(label && !icon){
33466 this.el.select('.roo-date-split-field-label', true).createChild({
33468 cls : 'text-danger fa fa-lg fa-star',
33469 tooltip : 'This field is required',
33470 style : 'margin-right:5px;'
33474 this.fireEvent('invalid', this, msg);
33477 clearInvalid : function()
33479 var label = this.el.select('label', true).first();
33480 var icon = this.el.select('i.fa-star', true).first();
33486 this.fireEvent('valid', this);
33489 getName: function()
33499 * http://masonry.desandro.com
33501 * The idea is to render all the bricks based on vertical width...
33503 * The original code extends 'outlayer' - we might need to use that....
33509 * @class Roo.bootstrap.LayoutMasonry
33510 * @extends Roo.bootstrap.Component
33511 * Bootstrap Layout Masonry class
33514 * Create a new Element
33515 * @param {Object} config The config object
33518 Roo.bootstrap.LayoutMasonry = function(config){
33520 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33524 Roo.bootstrap.LayoutMasonry.register(this);
33530 * Fire after layout the items
33531 * @param {Roo.bootstrap.LayoutMasonry} this
33532 * @param {Roo.EventObject} e
33539 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33542 * @cfg {Boolean} isLayoutInstant = no animation?
33544 isLayoutInstant : false, // needed?
33547 * @cfg {Number} boxWidth width of the columns
33552 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33557 * @cfg {Number} padWidth padding below box..
33562 * @cfg {Number} gutter gutter width..
33567 * @cfg {Number} maxCols maximum number of columns
33573 * @cfg {Boolean} isAutoInitial defalut true
33575 isAutoInitial : true,
33580 * @cfg {Boolean} isHorizontal defalut false
33582 isHorizontal : false,
33584 currentSize : null,
33590 bricks: null, //CompositeElement
33594 _isLayoutInited : false,
33596 // isAlternative : false, // only use for vertical layout...
33599 * @cfg {Number} alternativePadWidth padding below box..
33601 alternativePadWidth : 50,
33603 selectedBrick : [],
33605 getAutoCreate : function(){
33607 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33611 cls: 'blog-masonary-wrapper ' + this.cls,
33613 cls : 'mas-boxes masonary'
33620 getChildContainer: function( )
33622 if (this.boxesEl) {
33623 return this.boxesEl;
33626 this.boxesEl = this.el.select('.mas-boxes').first();
33628 return this.boxesEl;
33632 initEvents : function()
33636 if(this.isAutoInitial){
33637 Roo.log('hook children rendered');
33638 this.on('childrenrendered', function() {
33639 Roo.log('children rendered');
33645 initial : function()
33647 this.selectedBrick = [];
33649 this.currentSize = this.el.getBox(true);
33651 Roo.EventManager.onWindowResize(this.resize, this);
33653 if(!this.isAutoInitial){
33661 //this.layout.defer(500,this);
33665 resize : function()
33667 var cs = this.el.getBox(true);
33670 this.currentSize.width == cs.width &&
33671 this.currentSize.x == cs.x &&
33672 this.currentSize.height == cs.height &&
33673 this.currentSize.y == cs.y
33675 Roo.log("no change in with or X or Y");
33679 this.currentSize = cs;
33685 layout : function()
33687 this._resetLayout();
33689 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33691 this.layoutItems( isInstant );
33693 this._isLayoutInited = true;
33695 this.fireEvent('layout', this);
33699 _resetLayout : function()
33701 if(this.isHorizontal){
33702 this.horizontalMeasureColumns();
33706 this.verticalMeasureColumns();
33710 verticalMeasureColumns : function()
33712 this.getContainerWidth();
33714 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33715 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33719 var boxWidth = this.boxWidth + this.padWidth;
33721 if(this.containerWidth < this.boxWidth){
33722 boxWidth = this.containerWidth
33725 var containerWidth = this.containerWidth;
33727 var cols = Math.floor(containerWidth / boxWidth);
33729 this.cols = Math.max( cols, 1 );
33731 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33733 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33735 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33737 this.colWidth = boxWidth + avail - this.padWidth;
33739 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33740 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33743 horizontalMeasureColumns : function()
33745 this.getContainerWidth();
33747 var boxWidth = this.boxWidth;
33749 if(this.containerWidth < boxWidth){
33750 boxWidth = this.containerWidth;
33753 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33755 this.el.setHeight(boxWidth);
33759 getContainerWidth : function()
33761 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33764 layoutItems : function( isInstant )
33766 Roo.log(this.bricks);
33768 var items = Roo.apply([], this.bricks);
33770 if(this.isHorizontal){
33771 this._horizontalLayoutItems( items , isInstant );
33775 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33776 // this._verticalAlternativeLayoutItems( items , isInstant );
33780 this._verticalLayoutItems( items , isInstant );
33784 _verticalLayoutItems : function ( items , isInstant)
33786 if ( !items || !items.length ) {
33791 ['xs', 'xs', 'xs', 'tall'],
33792 ['xs', 'xs', 'tall'],
33793 ['xs', 'xs', 'sm'],
33794 ['xs', 'xs', 'xs'],
33800 ['sm', 'xs', 'xs'],
33804 ['tall', 'xs', 'xs', 'xs'],
33805 ['tall', 'xs', 'xs'],
33817 Roo.each(items, function(item, k){
33819 switch (item.size) {
33820 // these layouts take up a full box,
33831 boxes.push([item]);
33854 var filterPattern = function(box, length)
33862 var pattern = box.slice(0, length);
33866 Roo.each(pattern, function(i){
33867 format.push(i.size);
33870 Roo.each(standard, function(s){
33872 if(String(s) != String(format)){
33881 if(!match && length == 1){
33886 filterPattern(box, length - 1);
33890 queue.push(pattern);
33892 box = box.slice(length, box.length);
33894 filterPattern(box, 4);
33900 Roo.each(boxes, function(box, k){
33906 if(box.length == 1){
33911 filterPattern(box, 4);
33915 this._processVerticalLayoutQueue( queue, isInstant );
33919 // _verticalAlternativeLayoutItems : function( items , isInstant )
33921 // if ( !items || !items.length ) {
33925 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33929 _horizontalLayoutItems : function ( items , isInstant)
33931 if ( !items || !items.length || items.length < 3) {
33937 var eItems = items.slice(0, 3);
33939 items = items.slice(3, items.length);
33942 ['xs', 'xs', 'xs', 'wide'],
33943 ['xs', 'xs', 'wide'],
33944 ['xs', 'xs', 'sm'],
33945 ['xs', 'xs', 'xs'],
33951 ['sm', 'xs', 'xs'],
33955 ['wide', 'xs', 'xs', 'xs'],
33956 ['wide', 'xs', 'xs'],
33969 Roo.each(items, function(item, k){
33971 switch (item.size) {
33982 boxes.push([item]);
34006 var filterPattern = function(box, length)
34014 var pattern = box.slice(0, length);
34018 Roo.each(pattern, function(i){
34019 format.push(i.size);
34022 Roo.each(standard, function(s){
34024 if(String(s) != String(format)){
34033 if(!match && length == 1){
34038 filterPattern(box, length - 1);
34042 queue.push(pattern);
34044 box = box.slice(length, box.length);
34046 filterPattern(box, 4);
34052 Roo.each(boxes, function(box, k){
34058 if(box.length == 1){
34063 filterPattern(box, 4);
34070 var pos = this.el.getBox(true);
34074 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34076 var hit_end = false;
34078 Roo.each(queue, function(box){
34082 Roo.each(box, function(b){
34084 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34094 Roo.each(box, function(b){
34096 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34099 mx = Math.max(mx, b.x);
34103 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34107 Roo.each(box, function(b){
34109 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34123 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34126 /** Sets position of item in DOM
34127 * @param {Element} item
34128 * @param {Number} x - horizontal position
34129 * @param {Number} y - vertical position
34130 * @param {Boolean} isInstant - disables transitions
34132 _processVerticalLayoutQueue : function( queue, isInstant )
34134 var pos = this.el.getBox(true);
34139 for (var i = 0; i < this.cols; i++){
34143 Roo.each(queue, function(box, k){
34145 var col = k % this.cols;
34147 Roo.each(box, function(b,kk){
34149 b.el.position('absolute');
34151 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34152 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34154 if(b.size == 'md-left' || b.size == 'md-right'){
34155 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34156 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34159 b.el.setWidth(width);
34160 b.el.setHeight(height);
34162 b.el.select('iframe',true).setSize(width,height);
34166 for (var i = 0; i < this.cols; i++){
34168 if(maxY[i] < maxY[col]){
34173 col = Math.min(col, i);
34177 x = pos.x + col * (this.colWidth + this.padWidth);
34181 var positions = [];
34183 switch (box.length){
34185 positions = this.getVerticalOneBoxColPositions(x, y, box);
34188 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34191 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34194 positions = this.getVerticalFourBoxColPositions(x, y, box);
34200 Roo.each(box, function(b,kk){
34202 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34204 var sz = b.el.getSize();
34206 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34214 for (var i = 0; i < this.cols; i++){
34215 mY = Math.max(mY, maxY[i]);
34218 this.el.setHeight(mY - pos.y);
34222 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34224 // var pos = this.el.getBox(true);
34227 // var maxX = pos.right;
34229 // var maxHeight = 0;
34231 // Roo.each(items, function(item, k){
34235 // item.el.position('absolute');
34237 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34239 // item.el.setWidth(width);
34241 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34243 // item.el.setHeight(height);
34246 // item.el.setXY([x, y], isInstant ? false : true);
34248 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34251 // y = y + height + this.alternativePadWidth;
34253 // maxHeight = maxHeight + height + this.alternativePadWidth;
34257 // this.el.setHeight(maxHeight);
34261 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34263 var pos = this.el.getBox(true);
34268 var maxX = pos.right;
34270 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34272 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34274 Roo.each(queue, function(box, k){
34276 Roo.each(box, function(b, kk){
34278 b.el.position('absolute');
34280 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34281 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34283 if(b.size == 'md-left' || b.size == 'md-right'){
34284 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34285 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34288 b.el.setWidth(width);
34289 b.el.setHeight(height);
34297 var positions = [];
34299 switch (box.length){
34301 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34304 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34307 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34310 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34316 Roo.each(box, function(b,kk){
34318 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34320 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34328 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34330 Roo.each(eItems, function(b,k){
34332 b.size = (k == 0) ? 'sm' : 'xs';
34333 b.x = (k == 0) ? 2 : 1;
34334 b.y = (k == 0) ? 2 : 1;
34336 b.el.position('absolute');
34338 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34340 b.el.setWidth(width);
34342 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34344 b.el.setHeight(height);
34348 var positions = [];
34351 x : maxX - this.unitWidth * 2 - this.gutter,
34356 x : maxX - this.unitWidth,
34357 y : minY + (this.unitWidth + this.gutter) * 2
34361 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34365 Roo.each(eItems, function(b,k){
34367 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34373 getVerticalOneBoxColPositions : function(x, y, box)
34377 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34379 if(box[0].size == 'md-left'){
34383 if(box[0].size == 'md-right'){
34388 x : x + (this.unitWidth + this.gutter) * rand,
34395 getVerticalTwoBoxColPositions : function(x, y, box)
34399 if(box[0].size == 'xs'){
34403 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34407 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34421 x : x + (this.unitWidth + this.gutter) * 2,
34422 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34429 getVerticalThreeBoxColPositions : function(x, y, box)
34433 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34441 x : x + (this.unitWidth + this.gutter) * 1,
34446 x : x + (this.unitWidth + this.gutter) * 2,
34454 if(box[0].size == 'xs' && box[1].size == 'xs'){
34463 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34467 x : x + (this.unitWidth + this.gutter) * 1,
34481 x : x + (this.unitWidth + this.gutter) * 2,
34486 x : x + (this.unitWidth + this.gutter) * 2,
34487 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34494 getVerticalFourBoxColPositions : function(x, y, box)
34498 if(box[0].size == 'xs'){
34507 y : y + (this.unitHeight + this.gutter) * 1
34512 y : y + (this.unitHeight + this.gutter) * 2
34516 x : x + (this.unitWidth + this.gutter) * 1,
34530 x : x + (this.unitWidth + this.gutter) * 2,
34535 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34536 y : y + (this.unitHeight + this.gutter) * 1
34540 x : x + (this.unitWidth + this.gutter) * 2,
34541 y : y + (this.unitWidth + this.gutter) * 2
34548 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34552 if(box[0].size == 'md-left'){
34554 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34561 if(box[0].size == 'md-right'){
34563 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34564 y : minY + (this.unitWidth + this.gutter) * 1
34570 var rand = Math.floor(Math.random() * (4 - box[0].y));
34573 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34574 y : minY + (this.unitWidth + this.gutter) * rand
34581 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34585 if(box[0].size == 'xs'){
34588 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34593 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34594 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34602 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34607 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34608 y : minY + (this.unitWidth + this.gutter) * 2
34615 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34619 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34622 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34627 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34628 y : minY + (this.unitWidth + this.gutter) * 1
34632 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34633 y : minY + (this.unitWidth + this.gutter) * 2
34640 if(box[0].size == 'xs' && box[1].size == 'xs'){
34643 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34648 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34653 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34654 y : minY + (this.unitWidth + this.gutter) * 1
34662 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34667 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34668 y : minY + (this.unitWidth + this.gutter) * 2
34672 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34673 y : minY + (this.unitWidth + this.gutter) * 2
34680 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34684 if(box[0].size == 'xs'){
34687 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34692 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34697 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),
34702 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34703 y : minY + (this.unitWidth + this.gutter) * 1
34711 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34716 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34717 y : minY + (this.unitWidth + this.gutter) * 2
34721 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34722 y : minY + (this.unitWidth + this.gutter) * 2
34726 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),
34727 y : minY + (this.unitWidth + this.gutter) * 2
34735 * remove a Masonry Brick
34736 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34738 removeBrick : function(brick_id)
34744 for (var i = 0; i<this.bricks.length; i++) {
34745 if (this.bricks[i].id == brick_id) {
34746 this.bricks.splice(i,1);
34747 this.el.dom.removeChild(Roo.get(brick_id).dom);
34754 * adds a Masonry Brick
34755 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34757 addBrick : function(cfg)
34759 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34760 //this.register(cn);
34761 cn.parentId = this.id;
34762 cn.render(this.el);
34767 * register a Masonry Brick
34768 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34771 register : function(brick)
34773 this.bricks.push(brick);
34774 brick.masonryId = this.id;
34778 * clear all the Masonry Brick
34780 clearAll : function()
34783 //this.getChildContainer().dom.innerHTML = "";
34784 this.el.dom.innerHTML = '';
34787 getSelected : function()
34789 if (!this.selectedBrick) {
34793 return this.selectedBrick;
34797 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34801 * register a Masonry Layout
34802 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34805 register : function(layout)
34807 this.groups[layout.id] = layout;
34810 * fetch a Masonry Layout based on the masonry layout ID
34811 * @param {string} the masonry layout to add
34812 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34815 get: function(layout_id) {
34816 if (typeof(this.groups[layout_id]) == 'undefined') {
34819 return this.groups[layout_id] ;
34831 * http://masonry.desandro.com
34833 * The idea is to render all the bricks based on vertical width...
34835 * The original code extends 'outlayer' - we might need to use that....
34841 * @class Roo.bootstrap.LayoutMasonryAuto
34842 * @extends Roo.bootstrap.Component
34843 * Bootstrap Layout Masonry class
34846 * Create a new Element
34847 * @param {Object} config The config object
34850 Roo.bootstrap.LayoutMasonryAuto = function(config){
34851 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34854 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34857 * @cfg {Boolean} isFitWidth - resize the width..
34859 isFitWidth : false, // options..
34861 * @cfg {Boolean} isOriginLeft = left align?
34863 isOriginLeft : true,
34865 * @cfg {Boolean} isOriginTop = top align?
34867 isOriginTop : false,
34869 * @cfg {Boolean} isLayoutInstant = no animation?
34871 isLayoutInstant : false, // needed?
34873 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34875 isResizingContainer : true,
34877 * @cfg {Number} columnWidth width of the columns
34883 * @cfg {Number} maxCols maximum number of columns
34888 * @cfg {Number} padHeight padding below box..
34894 * @cfg {Boolean} isAutoInitial defalut true
34897 isAutoInitial : true,
34903 initialColumnWidth : 0,
34904 currentSize : null,
34906 colYs : null, // array.
34913 bricks: null, //CompositeElement
34914 cols : 0, // array?
34915 // element : null, // wrapped now this.el
34916 _isLayoutInited : null,
34919 getAutoCreate : function(){
34923 cls: 'blog-masonary-wrapper ' + this.cls,
34925 cls : 'mas-boxes masonary'
34932 getChildContainer: function( )
34934 if (this.boxesEl) {
34935 return this.boxesEl;
34938 this.boxesEl = this.el.select('.mas-boxes').first();
34940 return this.boxesEl;
34944 initEvents : function()
34948 if(this.isAutoInitial){
34949 Roo.log('hook children rendered');
34950 this.on('childrenrendered', function() {
34951 Roo.log('children rendered');
34958 initial : function()
34960 this.reloadItems();
34962 this.currentSize = this.el.getBox(true);
34964 /// was window resize... - let's see if this works..
34965 Roo.EventManager.onWindowResize(this.resize, this);
34967 if(!this.isAutoInitial){
34972 this.layout.defer(500,this);
34975 reloadItems: function()
34977 this.bricks = this.el.select('.masonry-brick', true);
34979 this.bricks.each(function(b) {
34980 //Roo.log(b.getSize());
34981 if (!b.attr('originalwidth')) {
34982 b.attr('originalwidth', b.getSize().width);
34987 Roo.log(this.bricks.elements.length);
34990 resize : function()
34993 var cs = this.el.getBox(true);
34995 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34996 Roo.log("no change in with or X");
34999 this.currentSize = cs;
35003 layout : function()
35006 this._resetLayout();
35007 //this._manageStamps();
35009 // don't animate first layout
35010 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35011 this.layoutItems( isInstant );
35013 // flag for initalized
35014 this._isLayoutInited = true;
35017 layoutItems : function( isInstant )
35019 //var items = this._getItemsForLayout( this.items );
35020 // original code supports filtering layout items.. we just ignore it..
35022 this._layoutItems( this.bricks , isInstant );
35024 this._postLayout();
35026 _layoutItems : function ( items , isInstant)
35028 //this.fireEvent( 'layout', this, items );
35031 if ( !items || !items.elements.length ) {
35032 // no items, emit event with empty array
35037 items.each(function(item) {
35038 Roo.log("layout item");
35040 // get x/y object from method
35041 var position = this._getItemLayoutPosition( item );
35043 position.item = item;
35044 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35045 queue.push( position );
35048 this._processLayoutQueue( queue );
35050 /** Sets position of item in DOM
35051 * @param {Element} item
35052 * @param {Number} x - horizontal position
35053 * @param {Number} y - vertical position
35054 * @param {Boolean} isInstant - disables transitions
35056 _processLayoutQueue : function( queue )
35058 for ( var i=0, len = queue.length; i < len; i++ ) {
35059 var obj = queue[i];
35060 obj.item.position('absolute');
35061 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35067 * Any logic you want to do after each layout,
35068 * i.e. size the container
35070 _postLayout : function()
35072 this.resizeContainer();
35075 resizeContainer : function()
35077 if ( !this.isResizingContainer ) {
35080 var size = this._getContainerSize();
35082 this.el.setSize(size.width,size.height);
35083 this.boxesEl.setSize(size.width,size.height);
35089 _resetLayout : function()
35091 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35092 this.colWidth = this.el.getWidth();
35093 //this.gutter = this.el.getWidth();
35095 this.measureColumns();
35101 this.colYs.push( 0 );
35107 measureColumns : function()
35109 this.getContainerWidth();
35110 // if columnWidth is 0, default to outerWidth of first item
35111 if ( !this.columnWidth ) {
35112 var firstItem = this.bricks.first();
35113 Roo.log(firstItem);
35114 this.columnWidth = this.containerWidth;
35115 if (firstItem && firstItem.attr('originalwidth') ) {
35116 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35118 // columnWidth fall back to item of first element
35119 Roo.log("set column width?");
35120 this.initialColumnWidth = this.columnWidth ;
35122 // if first elem has no width, default to size of container
35127 if (this.initialColumnWidth) {
35128 this.columnWidth = this.initialColumnWidth;
35133 // column width is fixed at the top - however if container width get's smaller we should
35136 // this bit calcs how man columns..
35138 var columnWidth = this.columnWidth += this.gutter;
35140 // calculate columns
35141 var containerWidth = this.containerWidth + this.gutter;
35143 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35144 // fix rounding errors, typically with gutters
35145 var excess = columnWidth - containerWidth % columnWidth;
35148 // if overshoot is less than a pixel, round up, otherwise floor it
35149 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35150 cols = Math[ mathMethod ]( cols );
35151 this.cols = Math.max( cols, 1 );
35152 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35154 // padding positioning..
35155 var totalColWidth = this.cols * this.columnWidth;
35156 var padavail = this.containerWidth - totalColWidth;
35157 // so for 2 columns - we need 3 'pads'
35159 var padNeeded = (1+this.cols) * this.padWidth;
35161 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35163 this.columnWidth += padExtra
35164 //this.padWidth = Math.floor(padavail / ( this.cols));
35166 // adjust colum width so that padding is fixed??
35168 // we have 3 columns ... total = width * 3
35169 // we have X left over... that should be used by
35171 //if (this.expandC) {
35179 getContainerWidth : function()
35181 /* // container is parent if fit width
35182 var container = this.isFitWidth ? this.element.parentNode : this.element;
35183 // check that this.size and size are there
35184 // IE8 triggers resize on body size change, so they might not be
35186 var size = getSize( container ); //FIXME
35187 this.containerWidth = size && size.innerWidth; //FIXME
35190 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35194 _getItemLayoutPosition : function( item ) // what is item?
35196 // we resize the item to our columnWidth..
35198 item.setWidth(this.columnWidth);
35199 item.autoBoxAdjust = false;
35201 var sz = item.getSize();
35203 // how many columns does this brick span
35204 var remainder = this.containerWidth % this.columnWidth;
35206 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35207 // round if off by 1 pixel, otherwise use ceil
35208 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35209 colSpan = Math.min( colSpan, this.cols );
35211 // normally this should be '1' as we dont' currently allow multi width columns..
35213 var colGroup = this._getColGroup( colSpan );
35214 // get the minimum Y value from the columns
35215 var minimumY = Math.min.apply( Math, colGroup );
35216 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35218 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35220 // position the brick
35222 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35223 y: this.currentSize.y + minimumY + this.padHeight
35227 // apply setHeight to necessary columns
35228 var setHeight = minimumY + sz.height + this.padHeight;
35229 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35231 var setSpan = this.cols + 1 - colGroup.length;
35232 for ( var i = 0; i < setSpan; i++ ) {
35233 this.colYs[ shortColIndex + i ] = setHeight ;
35240 * @param {Number} colSpan - number of columns the element spans
35241 * @returns {Array} colGroup
35243 _getColGroup : function( colSpan )
35245 if ( colSpan < 2 ) {
35246 // if brick spans only one column, use all the column Ys
35251 // how many different places could this brick fit horizontally
35252 var groupCount = this.cols + 1 - colSpan;
35253 // for each group potential horizontal position
35254 for ( var i = 0; i < groupCount; i++ ) {
35255 // make an array of colY values for that one group
35256 var groupColYs = this.colYs.slice( i, i + colSpan );
35257 // and get the max value of the array
35258 colGroup[i] = Math.max.apply( Math, groupColYs );
35263 _manageStamp : function( stamp )
35265 var stampSize = stamp.getSize();
35266 var offset = stamp.getBox();
35267 // get the columns that this stamp affects
35268 var firstX = this.isOriginLeft ? offset.x : offset.right;
35269 var lastX = firstX + stampSize.width;
35270 var firstCol = Math.floor( firstX / this.columnWidth );
35271 firstCol = Math.max( 0, firstCol );
35273 var lastCol = Math.floor( lastX / this.columnWidth );
35274 // lastCol should not go over if multiple of columnWidth #425
35275 lastCol -= lastX % this.columnWidth ? 0 : 1;
35276 lastCol = Math.min( this.cols - 1, lastCol );
35278 // set colYs to bottom of the stamp
35279 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35282 for ( var i = firstCol; i <= lastCol; i++ ) {
35283 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35288 _getContainerSize : function()
35290 this.maxY = Math.max.apply( Math, this.colYs );
35295 if ( this.isFitWidth ) {
35296 size.width = this._getContainerFitWidth();
35302 _getContainerFitWidth : function()
35304 var unusedCols = 0;
35305 // count unused columns
35308 if ( this.colYs[i] !== 0 ) {
35313 // fit container to columns that have been used
35314 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35317 needsResizeLayout : function()
35319 var previousWidth = this.containerWidth;
35320 this.getContainerWidth();
35321 return previousWidth !== this.containerWidth;
35336 * @class Roo.bootstrap.MasonryBrick
35337 * @extends Roo.bootstrap.Component
35338 * Bootstrap MasonryBrick class
35341 * Create a new MasonryBrick
35342 * @param {Object} config The config object
35345 Roo.bootstrap.MasonryBrick = function(config){
35347 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35349 Roo.bootstrap.MasonryBrick.register(this);
35355 * When a MasonryBrick is clcik
35356 * @param {Roo.bootstrap.MasonryBrick} this
35357 * @param {Roo.EventObject} e
35363 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35366 * @cfg {String} title
35370 * @cfg {String} html
35374 * @cfg {String} bgimage
35378 * @cfg {String} videourl
35382 * @cfg {String} cls
35386 * @cfg {String} href
35390 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35395 * @cfg {String} placetitle (center|bottom)
35400 * @cfg {Boolean} isFitContainer defalut true
35402 isFitContainer : true,
35405 * @cfg {Boolean} preventDefault defalut false
35407 preventDefault : false,
35410 * @cfg {Boolean} inverse defalut false
35412 maskInverse : false,
35414 getAutoCreate : function()
35416 if(!this.isFitContainer){
35417 return this.getSplitAutoCreate();
35420 var cls = 'masonry-brick masonry-brick-full';
35422 if(this.href.length){
35423 cls += ' masonry-brick-link';
35426 if(this.bgimage.length){
35427 cls += ' masonry-brick-image';
35430 if(this.maskInverse){
35431 cls += ' mask-inverse';
35434 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35435 cls += ' enable-mask';
35439 cls += ' masonry-' + this.size + '-brick';
35442 if(this.placetitle.length){
35444 switch (this.placetitle) {
35446 cls += ' masonry-center-title';
35449 cls += ' masonry-bottom-title';
35456 if(!this.html.length && !this.bgimage.length){
35457 cls += ' masonry-center-title';
35460 if(!this.html.length && this.bgimage.length){
35461 cls += ' masonry-bottom-title';
35466 cls += ' ' + this.cls;
35470 tag: (this.href.length) ? 'a' : 'div',
35475 cls: 'masonry-brick-mask'
35479 cls: 'masonry-brick-paragraph',
35485 if(this.href.length){
35486 cfg.href = this.href;
35489 var cn = cfg.cn[1].cn;
35491 if(this.title.length){
35494 cls: 'masonry-brick-title',
35499 if(this.html.length){
35502 cls: 'masonry-brick-text',
35507 if (!this.title.length && !this.html.length) {
35508 cfg.cn[1].cls += ' hide';
35511 if(this.bgimage.length){
35514 cls: 'masonry-brick-image-view',
35519 if(this.videourl.length){
35520 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35521 // youtube support only?
35524 cls: 'masonry-brick-image-view',
35527 allowfullscreen : true
35535 getSplitAutoCreate : function()
35537 var cls = 'masonry-brick masonry-brick-split';
35539 if(this.href.length){
35540 cls += ' masonry-brick-link';
35543 if(this.bgimage.length){
35544 cls += ' masonry-brick-image';
35548 cls += ' masonry-' + this.size + '-brick';
35551 switch (this.placetitle) {
35553 cls += ' masonry-center-title';
35556 cls += ' masonry-bottom-title';
35559 if(!this.bgimage.length){
35560 cls += ' masonry-center-title';
35563 if(this.bgimage.length){
35564 cls += ' masonry-bottom-title';
35570 cls += ' ' + this.cls;
35574 tag: (this.href.length) ? 'a' : 'div',
35579 cls: 'masonry-brick-split-head',
35583 cls: 'masonry-brick-paragraph',
35590 cls: 'masonry-brick-split-body',
35596 if(this.href.length){
35597 cfg.href = this.href;
35600 if(this.title.length){
35601 cfg.cn[0].cn[0].cn.push({
35603 cls: 'masonry-brick-title',
35608 if(this.html.length){
35609 cfg.cn[1].cn.push({
35611 cls: 'masonry-brick-text',
35616 if(this.bgimage.length){
35617 cfg.cn[0].cn.push({
35619 cls: 'masonry-brick-image-view',
35624 if(this.videourl.length){
35625 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35626 // youtube support only?
35627 cfg.cn[0].cn.cn.push({
35629 cls: 'masonry-brick-image-view',
35632 allowfullscreen : true
35639 initEvents: function()
35641 switch (this.size) {
35674 this.el.on('touchstart', this.onTouchStart, this);
35675 this.el.on('touchmove', this.onTouchMove, this);
35676 this.el.on('touchend', this.onTouchEnd, this);
35677 this.el.on('contextmenu', this.onContextMenu, this);
35679 this.el.on('mouseenter' ,this.enter, this);
35680 this.el.on('mouseleave', this.leave, this);
35681 this.el.on('click', this.onClick, this);
35684 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35685 this.parent().bricks.push(this);
35690 onClick: function(e, el)
35692 var time = this.endTimer - this.startTimer;
35693 // Roo.log(e.preventDefault());
35696 e.preventDefault();
35701 if(!this.preventDefault){
35705 e.preventDefault();
35707 if (this.activeClass != '') {
35708 this.selectBrick();
35711 this.fireEvent('click', this, e);
35714 enter: function(e, el)
35716 e.preventDefault();
35718 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35722 if(this.bgimage.length && this.html.length){
35723 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35727 leave: function(e, el)
35729 e.preventDefault();
35731 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35735 if(this.bgimage.length && this.html.length){
35736 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35740 onTouchStart: function(e, el)
35742 // e.preventDefault();
35744 this.touchmoved = false;
35746 if(!this.isFitContainer){
35750 if(!this.bgimage.length || !this.html.length){
35754 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35756 this.timer = new Date().getTime();
35760 onTouchMove: function(e, el)
35762 this.touchmoved = true;
35765 onContextMenu : function(e,el)
35767 e.preventDefault();
35768 e.stopPropagation();
35772 onTouchEnd: function(e, el)
35774 // e.preventDefault();
35776 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35783 if(!this.bgimage.length || !this.html.length){
35785 if(this.href.length){
35786 window.location.href = this.href;
35792 if(!this.isFitContainer){
35796 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35798 window.location.href = this.href;
35801 //selection on single brick only
35802 selectBrick : function() {
35804 if (!this.parentId) {
35808 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35809 var index = m.selectedBrick.indexOf(this.id);
35812 m.selectedBrick.splice(index,1);
35813 this.el.removeClass(this.activeClass);
35817 for(var i = 0; i < m.selectedBrick.length; i++) {
35818 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35819 b.el.removeClass(b.activeClass);
35822 m.selectedBrick = [];
35824 m.selectedBrick.push(this.id);
35825 this.el.addClass(this.activeClass);
35829 isSelected : function(){
35830 return this.el.hasClass(this.activeClass);
35835 Roo.apply(Roo.bootstrap.MasonryBrick, {
35838 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35840 * register a Masonry Brick
35841 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35844 register : function(brick)
35846 //this.groups[brick.id] = brick;
35847 this.groups.add(brick.id, brick);
35850 * fetch a masonry brick based on the masonry brick ID
35851 * @param {string} the masonry brick to add
35852 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35855 get: function(brick_id)
35857 // if (typeof(this.groups[brick_id]) == 'undefined') {
35860 // return this.groups[brick_id] ;
35862 if(this.groups.key(brick_id)) {
35863 return this.groups.key(brick_id);
35881 * @class Roo.bootstrap.Brick
35882 * @extends Roo.bootstrap.Component
35883 * Bootstrap Brick class
35886 * Create a new Brick
35887 * @param {Object} config The config object
35890 Roo.bootstrap.Brick = function(config){
35891 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35897 * When a Brick is click
35898 * @param {Roo.bootstrap.Brick} this
35899 * @param {Roo.EventObject} e
35905 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35908 * @cfg {String} title
35912 * @cfg {String} html
35916 * @cfg {String} bgimage
35920 * @cfg {String} cls
35924 * @cfg {String} href
35928 * @cfg {String} video
35932 * @cfg {Boolean} square
35936 getAutoCreate : function()
35938 var cls = 'roo-brick';
35940 if(this.href.length){
35941 cls += ' roo-brick-link';
35944 if(this.bgimage.length){
35945 cls += ' roo-brick-image';
35948 if(!this.html.length && !this.bgimage.length){
35949 cls += ' roo-brick-center-title';
35952 if(!this.html.length && this.bgimage.length){
35953 cls += ' roo-brick-bottom-title';
35957 cls += ' ' + this.cls;
35961 tag: (this.href.length) ? 'a' : 'div',
35966 cls: 'roo-brick-paragraph',
35972 if(this.href.length){
35973 cfg.href = this.href;
35976 var cn = cfg.cn[0].cn;
35978 if(this.title.length){
35981 cls: 'roo-brick-title',
35986 if(this.html.length){
35989 cls: 'roo-brick-text',
35996 if(this.bgimage.length){
35999 cls: 'roo-brick-image-view',
36007 initEvents: function()
36009 if(this.title.length || this.html.length){
36010 this.el.on('mouseenter' ,this.enter, this);
36011 this.el.on('mouseleave', this.leave, this);
36014 Roo.EventManager.onWindowResize(this.resize, this);
36016 if(this.bgimage.length){
36017 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36018 this.imageEl.on('load', this.onImageLoad, this);
36025 onImageLoad : function()
36030 resize : function()
36032 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36034 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36036 if(this.bgimage.length){
36037 var image = this.el.select('.roo-brick-image-view', true).first();
36039 image.setWidth(paragraph.getWidth());
36042 image.setHeight(paragraph.getWidth());
36045 this.el.setHeight(image.getHeight());
36046 paragraph.setHeight(image.getHeight());
36052 enter: function(e, el)
36054 e.preventDefault();
36056 if(this.bgimage.length){
36057 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36058 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36062 leave: function(e, el)
36064 e.preventDefault();
36066 if(this.bgimage.length){
36067 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36068 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36083 * @class Roo.bootstrap.NumberField
36084 * @extends Roo.bootstrap.Input
36085 * Bootstrap NumberField class
36091 * Create a new NumberField
36092 * @param {Object} config The config object
36095 Roo.bootstrap.NumberField = function(config){
36096 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36099 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36102 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36104 allowDecimals : true,
36106 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36108 decimalSeparator : ".",
36110 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36112 decimalPrecision : 2,
36114 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36116 allowNegative : true,
36119 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36123 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36125 minValue : Number.NEGATIVE_INFINITY,
36127 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36129 maxValue : Number.MAX_VALUE,
36131 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36133 minText : "The minimum value for this field is {0}",
36135 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36137 maxText : "The maximum value for this field is {0}",
36139 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36140 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36142 nanText : "{0} is not a valid number",
36144 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36146 thousandsDelimiter : false,
36148 * @cfg {String} valueAlign alignment of value
36150 valueAlign : "left",
36152 getAutoCreate : function()
36154 var hiddenInput = {
36158 cls: 'hidden-number-input'
36162 hiddenInput.name = this.name;
36167 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36169 this.name = hiddenInput.name;
36171 if(cfg.cn.length > 0) {
36172 cfg.cn.push(hiddenInput);
36179 initEvents : function()
36181 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36183 var allowed = "0123456789";
36185 if(this.allowDecimals){
36186 allowed += this.decimalSeparator;
36189 if(this.allowNegative){
36193 if(this.thousandsDelimiter) {
36197 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36199 var keyPress = function(e){
36201 var k = e.getKey();
36203 var c = e.getCharCode();
36206 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36207 allowed.indexOf(String.fromCharCode(c)) === -1
36213 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36217 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36222 this.el.on("keypress", keyPress, this);
36225 validateValue : function(value)
36228 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36232 var num = this.parseValue(value);
36235 this.markInvalid(String.format(this.nanText, value));
36239 if(num < this.minValue){
36240 this.markInvalid(String.format(this.minText, this.minValue));
36244 if(num > this.maxValue){
36245 this.markInvalid(String.format(this.maxText, this.maxValue));
36252 getValue : function()
36254 var v = this.hiddenEl().getValue();
36256 return this.fixPrecision(this.parseValue(v));
36259 parseValue : function(value)
36261 if(this.thousandsDelimiter) {
36263 r = new RegExp(",", "g");
36264 value = value.replace(r, "");
36267 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36268 return isNaN(value) ? '' : value;
36271 fixPrecision : function(value)
36273 if(this.thousandsDelimiter) {
36275 r = new RegExp(",", "g");
36276 value = value.replace(r, "");
36279 var nan = isNaN(value);
36281 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36282 return nan ? '' : value;
36284 return parseFloat(value).toFixed(this.decimalPrecision);
36287 setValue : function(v)
36289 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36295 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36297 this.inputEl().dom.value = (v == '') ? '' :
36298 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36300 if(!this.allowZero && v === '0') {
36301 this.hiddenEl().dom.value = '';
36302 this.inputEl().dom.value = '';
36309 decimalPrecisionFcn : function(v)
36311 return Math.floor(v);
36314 beforeBlur : function()
36316 var v = this.parseValue(this.getRawValue());
36318 if(v || v === 0 || v === ''){
36323 hiddenEl : function()
36325 return this.el.select('input.hidden-number-input',true).first();
36337 * @class Roo.bootstrap.DocumentSlider
36338 * @extends Roo.bootstrap.Component
36339 * Bootstrap DocumentSlider class
36342 * Create a new DocumentViewer
36343 * @param {Object} config The config object
36346 Roo.bootstrap.DocumentSlider = function(config){
36347 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36354 * Fire after initEvent
36355 * @param {Roo.bootstrap.DocumentSlider} this
36360 * Fire after update
36361 * @param {Roo.bootstrap.DocumentSlider} this
36367 * @param {Roo.bootstrap.DocumentSlider} this
36373 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36379 getAutoCreate : function()
36383 cls : 'roo-document-slider',
36387 cls : 'roo-document-slider-header',
36391 cls : 'roo-document-slider-header-title'
36397 cls : 'roo-document-slider-body',
36401 cls : 'roo-document-slider-prev',
36405 cls : 'fa fa-chevron-left'
36411 cls : 'roo-document-slider-thumb',
36415 cls : 'roo-document-slider-image'
36421 cls : 'roo-document-slider-next',
36425 cls : 'fa fa-chevron-right'
36437 initEvents : function()
36439 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36440 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36442 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36443 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36445 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36446 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36448 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36449 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36451 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36452 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36454 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36455 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36457 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36458 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36460 this.thumbEl.on('click', this.onClick, this);
36462 this.prevIndicator.on('click', this.prev, this);
36464 this.nextIndicator.on('click', this.next, this);
36468 initial : function()
36470 if(this.files.length){
36471 this.indicator = 1;
36475 this.fireEvent('initial', this);
36478 update : function()
36480 this.imageEl.attr('src', this.files[this.indicator - 1]);
36482 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36484 this.prevIndicator.show();
36486 if(this.indicator == 1){
36487 this.prevIndicator.hide();
36490 this.nextIndicator.show();
36492 if(this.indicator == this.files.length){
36493 this.nextIndicator.hide();
36496 this.thumbEl.scrollTo('top');
36498 this.fireEvent('update', this);
36501 onClick : function(e)
36503 e.preventDefault();
36505 this.fireEvent('click', this);
36510 e.preventDefault();
36512 this.indicator = Math.max(1, this.indicator - 1);
36519 e.preventDefault();
36521 this.indicator = Math.min(this.files.length, this.indicator + 1);
36535 * @class Roo.bootstrap.RadioSet
36536 * @extends Roo.bootstrap.Input
36537 * Bootstrap RadioSet class
36538 * @cfg {String} indicatorpos (left|right) default left
36539 * @cfg {Boolean} inline (true|false) inline the element (default true)
36540 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36542 * Create a new RadioSet
36543 * @param {Object} config The config object
36546 Roo.bootstrap.RadioSet = function(config){
36548 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36552 Roo.bootstrap.RadioSet.register(this);
36557 * Fires when the element is checked or unchecked.
36558 * @param {Roo.bootstrap.RadioSet} this This radio
36559 * @param {Roo.bootstrap.Radio} item The checked item
36564 * Fires when the element is click.
36565 * @param {Roo.bootstrap.RadioSet} this This radio set
36566 * @param {Roo.bootstrap.Radio} item The checked item
36567 * @param {Roo.EventObject} e The event object
36574 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36582 indicatorpos : 'left',
36584 getAutoCreate : function()
36588 cls : 'roo-radio-set-label',
36592 html : this.fieldLabel
36596 if (Roo.bootstrap.version == 3) {
36599 if(this.indicatorpos == 'left'){
36602 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36603 tooltip : 'This field is required'
36608 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36609 tooltip : 'This field is required'
36615 cls : 'roo-radio-set-items'
36618 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36620 if (align === 'left' && this.fieldLabel.length) {
36623 cls : "roo-radio-set-right",
36629 if(this.labelWidth > 12){
36630 label.style = "width: " + this.labelWidth + 'px';
36633 if(this.labelWidth < 13 && this.labelmd == 0){
36634 this.labelmd = this.labelWidth;
36637 if(this.labellg > 0){
36638 label.cls += ' col-lg-' + this.labellg;
36639 items.cls += ' col-lg-' + (12 - this.labellg);
36642 if(this.labelmd > 0){
36643 label.cls += ' col-md-' + this.labelmd;
36644 items.cls += ' col-md-' + (12 - this.labelmd);
36647 if(this.labelsm > 0){
36648 label.cls += ' col-sm-' + this.labelsm;
36649 items.cls += ' col-sm-' + (12 - this.labelsm);
36652 if(this.labelxs > 0){
36653 label.cls += ' col-xs-' + this.labelxs;
36654 items.cls += ' col-xs-' + (12 - this.labelxs);
36660 cls : 'roo-radio-set',
36664 cls : 'roo-radio-set-input',
36667 value : this.value ? this.value : ''
36674 if(this.weight.length){
36675 cfg.cls += ' roo-radio-' + this.weight;
36679 cfg.cls += ' roo-radio-set-inline';
36683 ['xs','sm','md','lg'].map(function(size){
36684 if (settings[size]) {
36685 cfg.cls += ' col-' + size + '-' + settings[size];
36693 initEvents : function()
36695 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36696 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36698 if(!this.fieldLabel.length){
36699 this.labelEl.hide();
36702 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36703 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36705 this.indicator = this.indicatorEl();
36707 if(this.indicator){
36708 this.indicator.addClass('invisible');
36711 this.originalValue = this.getValue();
36715 inputEl: function ()
36717 return this.el.select('.roo-radio-set-input', true).first();
36720 getChildContainer : function()
36722 return this.itemsEl;
36725 register : function(item)
36727 this.radioes.push(item);
36731 validate : function()
36733 if(this.getVisibilityEl().hasClass('hidden')){
36739 Roo.each(this.radioes, function(i){
36748 if(this.allowBlank) {
36752 if(this.disabled || valid){
36757 this.markInvalid();
36762 markValid : function()
36764 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36765 this.indicatorEl().removeClass('visible');
36766 this.indicatorEl().addClass('invisible');
36770 if (Roo.bootstrap.version == 3) {
36771 this.el.removeClass([this.invalidClass, this.validClass]);
36772 this.el.addClass(this.validClass);
36774 this.el.removeClass(['is-invalid','is-valid']);
36775 this.el.addClass(['is-valid']);
36777 this.fireEvent('valid', this);
36780 markInvalid : function(msg)
36782 if(this.allowBlank || this.disabled){
36786 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36787 this.indicatorEl().removeClass('invisible');
36788 this.indicatorEl().addClass('visible');
36790 if (Roo.bootstrap.version == 3) {
36791 this.el.removeClass([this.invalidClass, this.validClass]);
36792 this.el.addClass(this.invalidClass);
36794 this.el.removeClass(['is-invalid','is-valid']);
36795 this.el.addClass(['is-invalid']);
36798 this.fireEvent('invalid', this, msg);
36802 setValue : function(v, suppressEvent)
36804 if(this.value === v){
36811 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36814 Roo.each(this.radioes, function(i){
36816 i.el.removeClass('checked');
36819 Roo.each(this.radioes, function(i){
36821 if(i.value === v || i.value.toString() === v.toString()){
36823 i.el.addClass('checked');
36825 if(suppressEvent !== true){
36826 this.fireEvent('check', this, i);
36837 clearInvalid : function(){
36839 if(!this.el || this.preventMark){
36843 this.el.removeClass([this.invalidClass]);
36845 this.fireEvent('valid', this);
36850 Roo.apply(Roo.bootstrap.RadioSet, {
36854 register : function(set)
36856 this.groups[set.name] = set;
36859 get: function(name)
36861 if (typeof(this.groups[name]) == 'undefined') {
36865 return this.groups[name] ;
36871 * Ext JS Library 1.1.1
36872 * Copyright(c) 2006-2007, Ext JS, LLC.
36874 * Originally Released Under LGPL - original licence link has changed is not relivant.
36877 * <script type="text/javascript">
36882 * @class Roo.bootstrap.SplitBar
36883 * @extends Roo.util.Observable
36884 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36888 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36889 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36890 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36891 split.minSize = 100;
36892 split.maxSize = 600;
36893 split.animate = true;
36894 split.on('moved', splitterMoved);
36897 * Create a new SplitBar
36898 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36899 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36900 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36901 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36902 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36903 position of the SplitBar).
36905 Roo.bootstrap.SplitBar = function(cfg){
36910 // dragElement : elm
36911 // resizingElement: el,
36913 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36914 // placement : Roo.bootstrap.SplitBar.LEFT ,
36915 // existingProxy ???
36918 this.el = Roo.get(cfg.dragElement, true);
36919 this.el.dom.unselectable = "on";
36921 this.resizingEl = Roo.get(cfg.resizingElement, true);
36925 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36926 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36929 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36932 * The minimum size of the resizing element. (Defaults to 0)
36938 * The maximum size of the resizing element. (Defaults to 2000)
36941 this.maxSize = 2000;
36944 * Whether to animate the transition to the new size
36947 this.animate = false;
36950 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36953 this.useShim = false;
36958 if(!cfg.existingProxy){
36960 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36962 this.proxy = Roo.get(cfg.existingProxy).dom;
36965 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36968 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36971 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36974 this.dragSpecs = {};
36977 * @private The adapter to use to positon and resize elements
36979 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36980 this.adapter.init(this);
36982 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36984 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36985 this.el.addClass("roo-splitbar-h");
36988 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36989 this.el.addClass("roo-splitbar-v");
36995 * Fires when the splitter is moved (alias for {@link #event-moved})
36996 * @param {Roo.bootstrap.SplitBar} this
36997 * @param {Number} newSize the new width or height
37002 * Fires when the splitter is moved
37003 * @param {Roo.bootstrap.SplitBar} this
37004 * @param {Number} newSize the new width or height
37008 * @event beforeresize
37009 * Fires before the splitter is dragged
37010 * @param {Roo.bootstrap.SplitBar} this
37012 "beforeresize" : true,
37014 "beforeapply" : true
37017 Roo.util.Observable.call(this);
37020 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37021 onStartProxyDrag : function(x, y){
37022 this.fireEvent("beforeresize", this);
37024 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37026 o.enableDisplayMode("block");
37027 // all splitbars share the same overlay
37028 Roo.bootstrap.SplitBar.prototype.overlay = o;
37030 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37031 this.overlay.show();
37032 Roo.get(this.proxy).setDisplayed("block");
37033 var size = this.adapter.getElementSize(this);
37034 this.activeMinSize = this.getMinimumSize();;
37035 this.activeMaxSize = this.getMaximumSize();;
37036 var c1 = size - this.activeMinSize;
37037 var c2 = Math.max(this.activeMaxSize - size, 0);
37038 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37039 this.dd.resetConstraints();
37040 this.dd.setXConstraint(
37041 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37042 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37044 this.dd.setYConstraint(0, 0);
37046 this.dd.resetConstraints();
37047 this.dd.setXConstraint(0, 0);
37048 this.dd.setYConstraint(
37049 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37050 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37053 this.dragSpecs.startSize = size;
37054 this.dragSpecs.startPoint = [x, y];
37055 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37059 * @private Called after the drag operation by the DDProxy
37061 onEndProxyDrag : function(e){
37062 Roo.get(this.proxy).setDisplayed(false);
37063 var endPoint = Roo.lib.Event.getXY(e);
37065 this.overlay.hide();
37068 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37069 newSize = this.dragSpecs.startSize +
37070 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37071 endPoint[0] - this.dragSpecs.startPoint[0] :
37072 this.dragSpecs.startPoint[0] - endPoint[0]
37075 newSize = this.dragSpecs.startSize +
37076 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37077 endPoint[1] - this.dragSpecs.startPoint[1] :
37078 this.dragSpecs.startPoint[1] - endPoint[1]
37081 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37082 if(newSize != this.dragSpecs.startSize){
37083 if(this.fireEvent('beforeapply', this, newSize) !== false){
37084 this.adapter.setElementSize(this, newSize);
37085 this.fireEvent("moved", this, newSize);
37086 this.fireEvent("resize", this, newSize);
37092 * Get the adapter this SplitBar uses
37093 * @return The adapter object
37095 getAdapter : function(){
37096 return this.adapter;
37100 * Set the adapter this SplitBar uses
37101 * @param {Object} adapter A SplitBar adapter object
37103 setAdapter : function(adapter){
37104 this.adapter = adapter;
37105 this.adapter.init(this);
37109 * Gets the minimum size for the resizing element
37110 * @return {Number} The minimum size
37112 getMinimumSize : function(){
37113 return this.minSize;
37117 * Sets the minimum size for the resizing element
37118 * @param {Number} minSize The minimum size
37120 setMinimumSize : function(minSize){
37121 this.minSize = minSize;
37125 * Gets the maximum size for the resizing element
37126 * @return {Number} The maximum size
37128 getMaximumSize : function(){
37129 return this.maxSize;
37133 * Sets the maximum size for the resizing element
37134 * @param {Number} maxSize The maximum size
37136 setMaximumSize : function(maxSize){
37137 this.maxSize = maxSize;
37141 * Sets the initialize size for the resizing element
37142 * @param {Number} size The initial size
37144 setCurrentSize : function(size){
37145 var oldAnimate = this.animate;
37146 this.animate = false;
37147 this.adapter.setElementSize(this, size);
37148 this.animate = oldAnimate;
37152 * Destroy this splitbar.
37153 * @param {Boolean} removeEl True to remove the element
37155 destroy : function(removeEl){
37157 this.shim.remove();
37160 this.proxy.parentNode.removeChild(this.proxy);
37168 * @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.
37170 Roo.bootstrap.SplitBar.createProxy = function(dir){
37171 var proxy = new Roo.Element(document.createElement("div"));
37172 proxy.unselectable();
37173 var cls = 'roo-splitbar-proxy';
37174 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37175 document.body.appendChild(proxy.dom);
37180 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37181 * Default Adapter. It assumes the splitter and resizing element are not positioned
37182 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37184 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37187 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37188 // do nothing for now
37189 init : function(s){
37193 * Called before drag operations to get the current size of the resizing element.
37194 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37196 getElementSize : function(s){
37197 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37198 return s.resizingEl.getWidth();
37200 return s.resizingEl.getHeight();
37205 * Called after drag operations to set the size of the resizing element.
37206 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37207 * @param {Number} newSize The new size to set
37208 * @param {Function} onComplete A function to be invoked when resizing is complete
37210 setElementSize : function(s, newSize, onComplete){
37211 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37213 s.resizingEl.setWidth(newSize);
37215 onComplete(s, newSize);
37218 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37223 s.resizingEl.setHeight(newSize);
37225 onComplete(s, newSize);
37228 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37235 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37236 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37237 * Adapter that moves the splitter element to align with the resized sizing element.
37238 * Used with an absolute positioned SplitBar.
37239 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37240 * document.body, make sure you assign an id to the body element.
37242 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37243 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37244 this.container = Roo.get(container);
37247 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37248 init : function(s){
37249 this.basic.init(s);
37252 getElementSize : function(s){
37253 return this.basic.getElementSize(s);
37256 setElementSize : function(s, newSize, onComplete){
37257 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37260 moveSplitter : function(s){
37261 var yes = Roo.bootstrap.SplitBar;
37262 switch(s.placement){
37264 s.el.setX(s.resizingEl.getRight());
37267 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37270 s.el.setY(s.resizingEl.getBottom());
37273 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37280 * Orientation constant - Create a vertical SplitBar
37284 Roo.bootstrap.SplitBar.VERTICAL = 1;
37287 * Orientation constant - Create a horizontal SplitBar
37291 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37294 * Placement constant - The resizing element is to the left of the splitter element
37298 Roo.bootstrap.SplitBar.LEFT = 1;
37301 * Placement constant - The resizing element is to the right of the splitter element
37305 Roo.bootstrap.SplitBar.RIGHT = 2;
37308 * Placement constant - The resizing element is positioned above the splitter element
37312 Roo.bootstrap.SplitBar.TOP = 3;
37315 * Placement constant - The resizing element is positioned under splitter element
37319 Roo.bootstrap.SplitBar.BOTTOM = 4;
37320 Roo.namespace("Roo.bootstrap.layout");/*
37322 * Ext JS Library 1.1.1
37323 * Copyright(c) 2006-2007, Ext JS, LLC.
37325 * Originally Released Under LGPL - original licence link has changed is not relivant.
37328 * <script type="text/javascript">
37332 * @class Roo.bootstrap.layout.Manager
37333 * @extends Roo.bootstrap.Component
37334 * Base class for layout managers.
37336 Roo.bootstrap.layout.Manager = function(config)
37338 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37344 /** false to disable window resize monitoring @type Boolean */
37345 this.monitorWindowResize = true;
37350 * Fires when a layout is performed.
37351 * @param {Roo.LayoutManager} this
37355 * @event regionresized
37356 * Fires when the user resizes a region.
37357 * @param {Roo.LayoutRegion} region The resized region
37358 * @param {Number} newSize The new size (width for east/west, height for north/south)
37360 "regionresized" : true,
37362 * @event regioncollapsed
37363 * Fires when a region is collapsed.
37364 * @param {Roo.LayoutRegion} region The collapsed region
37366 "regioncollapsed" : true,
37368 * @event regionexpanded
37369 * Fires when a region is expanded.
37370 * @param {Roo.LayoutRegion} region The expanded region
37372 "regionexpanded" : true
37374 this.updating = false;
37377 this.el = Roo.get(config.el);
37383 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37388 monitorWindowResize : true,
37394 onRender : function(ct, position)
37397 this.el = Roo.get(ct);
37400 //this.fireEvent('render',this);
37404 initEvents: function()
37408 // ie scrollbar fix
37409 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37410 document.body.scroll = "no";
37411 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37412 this.el.position('relative');
37414 this.id = this.el.id;
37415 this.el.addClass("roo-layout-container");
37416 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37417 if(this.el.dom != document.body ) {
37418 this.el.on('resize', this.layout,this);
37419 this.el.on('show', this.layout,this);
37425 * Returns true if this layout is currently being updated
37426 * @return {Boolean}
37428 isUpdating : function(){
37429 return this.updating;
37433 * Suspend the LayoutManager from doing auto-layouts while
37434 * making multiple add or remove calls
37436 beginUpdate : function(){
37437 this.updating = true;
37441 * Restore auto-layouts and optionally disable the manager from performing a layout
37442 * @param {Boolean} noLayout true to disable a layout update
37444 endUpdate : function(noLayout){
37445 this.updating = false;
37451 layout: function(){
37455 onRegionResized : function(region, newSize){
37456 this.fireEvent("regionresized", region, newSize);
37460 onRegionCollapsed : function(region){
37461 this.fireEvent("regioncollapsed", region);
37464 onRegionExpanded : function(region){
37465 this.fireEvent("regionexpanded", region);
37469 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37470 * performs box-model adjustments.
37471 * @return {Object} The size as an object {width: (the width), height: (the height)}
37473 getViewSize : function()
37476 if(this.el.dom != document.body){
37477 size = this.el.getSize();
37479 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37481 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37482 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37487 * Returns the Element this layout is bound to.
37488 * @return {Roo.Element}
37490 getEl : function(){
37495 * Returns the specified region.
37496 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37497 * @return {Roo.LayoutRegion}
37499 getRegion : function(target){
37500 return this.regions[target.toLowerCase()];
37503 onWindowResize : function(){
37504 if(this.monitorWindowResize){
37511 * Ext JS Library 1.1.1
37512 * Copyright(c) 2006-2007, Ext JS, LLC.
37514 * Originally Released Under LGPL - original licence link has changed is not relivant.
37517 * <script type="text/javascript">
37520 * @class Roo.bootstrap.layout.Border
37521 * @extends Roo.bootstrap.layout.Manager
37522 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37523 * please see: examples/bootstrap/nested.html<br><br>
37525 <b>The container the layout is rendered into can be either the body element or any other element.
37526 If it is not the body element, the container needs to either be an absolute positioned element,
37527 or you will need to add "position:relative" to the css of the container. You will also need to specify
37528 the container size if it is not the body element.</b>
37531 * Create a new Border
37532 * @param {Object} config Configuration options
37534 Roo.bootstrap.layout.Border = function(config){
37535 config = config || {};
37536 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37540 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37541 if(config[region]){
37542 config[region].region = region;
37543 this.addRegion(config[region]);
37549 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37551 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37553 parent : false, // this might point to a 'nest' or a ???
37556 * Creates and adds a new region if it doesn't already exist.
37557 * @param {String} target The target region key (north, south, east, west or center).
37558 * @param {Object} config The regions config object
37559 * @return {BorderLayoutRegion} The new region
37561 addRegion : function(config)
37563 if(!this.regions[config.region]){
37564 var r = this.factory(config);
37565 this.bindRegion(r);
37567 return this.regions[config.region];
37571 bindRegion : function(r){
37572 this.regions[r.config.region] = r;
37574 r.on("visibilitychange", this.layout, this);
37575 r.on("paneladded", this.layout, this);
37576 r.on("panelremoved", this.layout, this);
37577 r.on("invalidated", this.layout, this);
37578 r.on("resized", this.onRegionResized, this);
37579 r.on("collapsed", this.onRegionCollapsed, this);
37580 r.on("expanded", this.onRegionExpanded, this);
37584 * Performs a layout update.
37586 layout : function()
37588 if(this.updating) {
37592 // render all the rebions if they have not been done alreayd?
37593 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37594 if(this.regions[region] && !this.regions[region].bodyEl){
37595 this.regions[region].onRender(this.el)
37599 var size = this.getViewSize();
37600 var w = size.width;
37601 var h = size.height;
37606 //var x = 0, y = 0;
37608 var rs = this.regions;
37609 var north = rs["north"];
37610 var south = rs["south"];
37611 var west = rs["west"];
37612 var east = rs["east"];
37613 var center = rs["center"];
37614 //if(this.hideOnLayout){ // not supported anymore
37615 //c.el.setStyle("display", "none");
37617 if(north && north.isVisible()){
37618 var b = north.getBox();
37619 var m = north.getMargins();
37620 b.width = w - (m.left+m.right);
37623 centerY = b.height + b.y + m.bottom;
37624 centerH -= centerY;
37625 north.updateBox(this.safeBox(b));
37627 if(south && south.isVisible()){
37628 var b = south.getBox();
37629 var m = south.getMargins();
37630 b.width = w - (m.left+m.right);
37632 var totalHeight = (b.height + m.top + m.bottom);
37633 b.y = h - totalHeight + m.top;
37634 centerH -= totalHeight;
37635 south.updateBox(this.safeBox(b));
37637 if(west && west.isVisible()){
37638 var b = west.getBox();
37639 var m = west.getMargins();
37640 b.height = centerH - (m.top+m.bottom);
37642 b.y = centerY + m.top;
37643 var totalWidth = (b.width + m.left + m.right);
37644 centerX += totalWidth;
37645 centerW -= totalWidth;
37646 west.updateBox(this.safeBox(b));
37648 if(east && east.isVisible()){
37649 var b = east.getBox();
37650 var m = east.getMargins();
37651 b.height = centerH - (m.top+m.bottom);
37652 var totalWidth = (b.width + m.left + m.right);
37653 b.x = w - totalWidth + m.left;
37654 b.y = centerY + m.top;
37655 centerW -= totalWidth;
37656 east.updateBox(this.safeBox(b));
37659 var m = center.getMargins();
37661 x: centerX + m.left,
37662 y: centerY + m.top,
37663 width: centerW - (m.left+m.right),
37664 height: centerH - (m.top+m.bottom)
37666 //if(this.hideOnLayout){
37667 //center.el.setStyle("display", "block");
37669 center.updateBox(this.safeBox(centerBox));
37672 this.fireEvent("layout", this);
37676 safeBox : function(box){
37677 box.width = Math.max(0, box.width);
37678 box.height = Math.max(0, box.height);
37683 * Adds a ContentPanel (or subclass) to this layout.
37684 * @param {String} target The target region key (north, south, east, west or center).
37685 * @param {Roo.ContentPanel} panel The panel to add
37686 * @return {Roo.ContentPanel} The added panel
37688 add : function(target, panel){
37690 target = target.toLowerCase();
37691 return this.regions[target].add(panel);
37695 * Remove a ContentPanel (or subclass) to this layout.
37696 * @param {String} target The target region key (north, south, east, west or center).
37697 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37698 * @return {Roo.ContentPanel} The removed panel
37700 remove : function(target, panel){
37701 target = target.toLowerCase();
37702 return this.regions[target].remove(panel);
37706 * Searches all regions for a panel with the specified id
37707 * @param {String} panelId
37708 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37710 findPanel : function(panelId){
37711 var rs = this.regions;
37712 for(var target in rs){
37713 if(typeof rs[target] != "function"){
37714 var p = rs[target].getPanel(panelId);
37724 * Searches all regions for a panel with the specified id and activates (shows) it.
37725 * @param {String/ContentPanel} panelId The panels id or the panel itself
37726 * @return {Roo.ContentPanel} The shown panel or null
37728 showPanel : function(panelId) {
37729 var rs = this.regions;
37730 for(var target in rs){
37731 var r = rs[target];
37732 if(typeof r != "function"){
37733 if(r.hasPanel(panelId)){
37734 return r.showPanel(panelId);
37742 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37743 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37746 restoreState : function(provider){
37748 provider = Roo.state.Manager;
37750 var sm = new Roo.LayoutStateManager();
37751 sm.init(this, provider);
37757 * Adds a xtype elements to the layout.
37761 xtype : 'ContentPanel',
37768 xtype : 'NestedLayoutPanel',
37774 items : [ ... list of content panels or nested layout panels.. ]
37778 * @param {Object} cfg Xtype definition of item to add.
37780 addxtype : function(cfg)
37782 // basically accepts a pannel...
37783 // can accept a layout region..!?!?
37784 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37787 // theory? children can only be panels??
37789 //if (!cfg.xtype.match(/Panel$/)) {
37794 if (typeof(cfg.region) == 'undefined') {
37795 Roo.log("Failed to add Panel, region was not set");
37799 var region = cfg.region;
37805 xitems = cfg.items;
37810 if ( region == 'center') {
37811 Roo.log("Center: " + cfg.title);
37817 case 'Content': // ContentPanel (el, cfg)
37818 case 'Scroll': // ContentPanel (el, cfg)
37820 cfg.autoCreate = cfg.autoCreate || true;
37821 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37823 // var el = this.el.createChild();
37824 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37827 this.add(region, ret);
37831 case 'TreePanel': // our new panel!
37832 cfg.el = this.el.createChild();
37833 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37834 this.add(region, ret);
37839 // create a new Layout (which is a Border Layout...
37841 var clayout = cfg.layout;
37842 clayout.el = this.el.createChild();
37843 clayout.items = clayout.items || [];
37847 // replace this exitems with the clayout ones..
37848 xitems = clayout.items;
37850 // force background off if it's in center...
37851 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37852 cfg.background = false;
37854 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37857 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37858 //console.log('adding nested layout panel ' + cfg.toSource());
37859 this.add(region, ret);
37860 nb = {}; /// find first...
37865 // needs grid and region
37867 //var el = this.getRegion(region).el.createChild();
37869 *var el = this.el.createChild();
37870 // create the grid first...
37871 cfg.grid.container = el;
37872 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37875 if (region == 'center' && this.active ) {
37876 cfg.background = false;
37879 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37881 this.add(region, ret);
37883 if (cfg.background) {
37884 // render grid on panel activation (if panel background)
37885 ret.on('activate', function(gp) {
37886 if (!gp.grid.rendered) {
37887 // gp.grid.render(el);
37891 // cfg.grid.render(el);
37897 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37898 // it was the old xcomponent building that caused this before.
37899 // espeically if border is the top element in the tree.
37909 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37911 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37912 this.add(region, ret);
37916 throw "Can not add '" + cfg.xtype + "' to Border";
37922 this.beginUpdate();
37926 Roo.each(xitems, function(i) {
37927 region = nb && i.region ? i.region : false;
37929 var add = ret.addxtype(i);
37932 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37933 if (!i.background) {
37934 abn[region] = nb[region] ;
37941 // make the last non-background panel active..
37942 //if (nb) { Roo.log(abn); }
37945 for(var r in abn) {
37946 region = this.getRegion(r);
37948 // tried using nb[r], but it does not work..
37950 region.showPanel(abn[r]);
37961 factory : function(cfg)
37964 var validRegions = Roo.bootstrap.layout.Border.regions;
37966 var target = cfg.region;
37969 var r = Roo.bootstrap.layout;
37973 return new r.North(cfg);
37975 return new r.South(cfg);
37977 return new r.East(cfg);
37979 return new r.West(cfg);
37981 return new r.Center(cfg);
37983 throw 'Layout region "'+target+'" not supported.';
37990 * Ext JS Library 1.1.1
37991 * Copyright(c) 2006-2007, Ext JS, LLC.
37993 * Originally Released Under LGPL - original licence link has changed is not relivant.
37996 * <script type="text/javascript">
38000 * @class Roo.bootstrap.layout.Basic
38001 * @extends Roo.util.Observable
38002 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38003 * and does not have a titlebar, tabs or any other features. All it does is size and position
38004 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38005 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38006 * @cfg {string} region the region that it inhabits..
38007 * @cfg {bool} skipConfig skip config?
38011 Roo.bootstrap.layout.Basic = function(config){
38013 this.mgr = config.mgr;
38015 this.position = config.region;
38017 var skipConfig = config.skipConfig;
38021 * @scope Roo.BasicLayoutRegion
38025 * @event beforeremove
38026 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38027 * @param {Roo.LayoutRegion} this
38028 * @param {Roo.ContentPanel} panel The panel
38029 * @param {Object} e The cancel event object
38031 "beforeremove" : true,
38033 * @event invalidated
38034 * Fires when the layout for this region is changed.
38035 * @param {Roo.LayoutRegion} this
38037 "invalidated" : true,
38039 * @event visibilitychange
38040 * Fires when this region is shown or hidden
38041 * @param {Roo.LayoutRegion} this
38042 * @param {Boolean} visibility true or false
38044 "visibilitychange" : true,
38046 * @event paneladded
38047 * Fires when a panel is added.
38048 * @param {Roo.LayoutRegion} this
38049 * @param {Roo.ContentPanel} panel The panel
38051 "paneladded" : true,
38053 * @event panelremoved
38054 * Fires when a panel is removed.
38055 * @param {Roo.LayoutRegion} this
38056 * @param {Roo.ContentPanel} panel The panel
38058 "panelremoved" : true,
38060 * @event beforecollapse
38061 * Fires when this region before collapse.
38062 * @param {Roo.LayoutRegion} this
38064 "beforecollapse" : true,
38067 * Fires when this region is collapsed.
38068 * @param {Roo.LayoutRegion} this
38070 "collapsed" : true,
38073 * Fires when this region is expanded.
38074 * @param {Roo.LayoutRegion} this
38079 * Fires when this region is slid into view.
38080 * @param {Roo.LayoutRegion} this
38082 "slideshow" : true,
38085 * Fires when this region slides out of view.
38086 * @param {Roo.LayoutRegion} this
38088 "slidehide" : true,
38090 * @event panelactivated
38091 * Fires when a panel is activated.
38092 * @param {Roo.LayoutRegion} this
38093 * @param {Roo.ContentPanel} panel The activated panel
38095 "panelactivated" : true,
38098 * Fires when the user resizes this region.
38099 * @param {Roo.LayoutRegion} this
38100 * @param {Number} newSize The new size (width for east/west, height for north/south)
38104 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38105 this.panels = new Roo.util.MixedCollection();
38106 this.panels.getKey = this.getPanelId.createDelegate(this);
38108 this.activePanel = null;
38109 // ensure listeners are added...
38111 if (config.listeners || config.events) {
38112 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38113 listeners : config.listeners || {},
38114 events : config.events || {}
38118 if(skipConfig !== true){
38119 this.applyConfig(config);
38123 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38125 getPanelId : function(p){
38129 applyConfig : function(config){
38130 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38131 this.config = config;
38136 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38137 * the width, for horizontal (north, south) the height.
38138 * @param {Number} newSize The new width or height
38140 resizeTo : function(newSize){
38141 var el = this.el ? this.el :
38142 (this.activePanel ? this.activePanel.getEl() : null);
38144 switch(this.position){
38147 el.setWidth(newSize);
38148 this.fireEvent("resized", this, newSize);
38152 el.setHeight(newSize);
38153 this.fireEvent("resized", this, newSize);
38159 getBox : function(){
38160 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38163 getMargins : function(){
38164 return this.margins;
38167 updateBox : function(box){
38169 var el = this.activePanel.getEl();
38170 el.dom.style.left = box.x + "px";
38171 el.dom.style.top = box.y + "px";
38172 this.activePanel.setSize(box.width, box.height);
38176 * Returns the container element for this region.
38177 * @return {Roo.Element}
38179 getEl : function(){
38180 return this.activePanel;
38184 * Returns true if this region is currently visible.
38185 * @return {Boolean}
38187 isVisible : function(){
38188 return this.activePanel ? true : false;
38191 setActivePanel : function(panel){
38192 panel = this.getPanel(panel);
38193 if(this.activePanel && this.activePanel != panel){
38194 this.activePanel.setActiveState(false);
38195 this.activePanel.getEl().setLeftTop(-10000,-10000);
38197 this.activePanel = panel;
38198 panel.setActiveState(true);
38200 panel.setSize(this.box.width, this.box.height);
38202 this.fireEvent("panelactivated", this, panel);
38203 this.fireEvent("invalidated");
38207 * Show the specified panel.
38208 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38209 * @return {Roo.ContentPanel} The shown panel or null
38211 showPanel : function(panel){
38212 panel = this.getPanel(panel);
38214 this.setActivePanel(panel);
38220 * Get the active panel for this region.
38221 * @return {Roo.ContentPanel} The active panel or null
38223 getActivePanel : function(){
38224 return this.activePanel;
38228 * Add the passed ContentPanel(s)
38229 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38230 * @return {Roo.ContentPanel} The panel added (if only one was added)
38232 add : function(panel){
38233 if(arguments.length > 1){
38234 for(var i = 0, len = arguments.length; i < len; i++) {
38235 this.add(arguments[i]);
38239 if(this.hasPanel(panel)){
38240 this.showPanel(panel);
38243 var el = panel.getEl();
38244 if(el.dom.parentNode != this.mgr.el.dom){
38245 this.mgr.el.dom.appendChild(el.dom);
38247 if(panel.setRegion){
38248 panel.setRegion(this);
38250 this.panels.add(panel);
38251 el.setStyle("position", "absolute");
38252 if(!panel.background){
38253 this.setActivePanel(panel);
38254 if(this.config.initialSize && this.panels.getCount()==1){
38255 this.resizeTo(this.config.initialSize);
38258 this.fireEvent("paneladded", this, panel);
38263 * Returns true if the panel is in this region.
38264 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38265 * @return {Boolean}
38267 hasPanel : function(panel){
38268 if(typeof panel == "object"){ // must be panel obj
38269 panel = panel.getId();
38271 return this.getPanel(panel) ? true : false;
38275 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38276 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38277 * @param {Boolean} preservePanel Overrides the config preservePanel option
38278 * @return {Roo.ContentPanel} The panel that was removed
38280 remove : function(panel, preservePanel){
38281 panel = this.getPanel(panel);
38286 this.fireEvent("beforeremove", this, panel, e);
38287 if(e.cancel === true){
38290 var panelId = panel.getId();
38291 this.panels.removeKey(panelId);
38296 * Returns the panel specified or null if it's not in this region.
38297 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38298 * @return {Roo.ContentPanel}
38300 getPanel : function(id){
38301 if(typeof id == "object"){ // must be panel obj
38304 return this.panels.get(id);
38308 * Returns this regions position (north/south/east/west/center).
38311 getPosition: function(){
38312 return this.position;
38316 * Ext JS Library 1.1.1
38317 * Copyright(c) 2006-2007, Ext JS, LLC.
38319 * Originally Released Under LGPL - original licence link has changed is not relivant.
38322 * <script type="text/javascript">
38326 * @class Roo.bootstrap.layout.Region
38327 * @extends Roo.bootstrap.layout.Basic
38328 * This class represents a region in a layout manager.
38330 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38331 * @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})
38332 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38333 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38334 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38335 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38336 * @cfg {String} title The title for the region (overrides panel titles)
38337 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38338 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38339 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38340 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38341 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38342 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38343 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38344 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38345 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38346 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38348 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38349 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38350 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38351 * @cfg {Number} width For East/West panels
38352 * @cfg {Number} height For North/South panels
38353 * @cfg {Boolean} split To show the splitter
38354 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38356 * @cfg {string} cls Extra CSS classes to add to region
38358 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38359 * @cfg {string} region the region that it inhabits..
38362 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38363 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38365 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38366 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38367 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38369 Roo.bootstrap.layout.Region = function(config)
38371 this.applyConfig(config);
38373 var mgr = config.mgr;
38374 var pos = config.region;
38375 config.skipConfig = true;
38376 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38379 this.onRender(mgr.el);
38382 this.visible = true;
38383 this.collapsed = false;
38384 this.unrendered_panels = [];
38387 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38389 position: '', // set by wrapper (eg. north/south etc..)
38390 unrendered_panels : null, // unrendered panels.
38392 tabPosition : false,
38394 mgr: false, // points to 'Border'
38397 createBody : function(){
38398 /** This region's body element
38399 * @type Roo.Element */
38400 this.bodyEl = this.el.createChild({
38402 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38406 onRender: function(ctr, pos)
38408 var dh = Roo.DomHelper;
38409 /** This region's container element
38410 * @type Roo.Element */
38411 this.el = dh.append(ctr.dom, {
38413 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38415 /** This region's title element
38416 * @type Roo.Element */
38418 this.titleEl = dh.append(this.el.dom, {
38420 unselectable: "on",
38421 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38423 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38424 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38428 this.titleEl.enableDisplayMode();
38429 /** This region's title text element
38430 * @type HTMLElement */
38431 this.titleTextEl = this.titleEl.dom.firstChild;
38432 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38434 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38435 this.closeBtn.enableDisplayMode();
38436 this.closeBtn.on("click", this.closeClicked, this);
38437 this.closeBtn.hide();
38439 this.createBody(this.config);
38440 if(this.config.hideWhenEmpty){
38442 this.on("paneladded", this.validateVisibility, this);
38443 this.on("panelremoved", this.validateVisibility, this);
38445 if(this.autoScroll){
38446 this.bodyEl.setStyle("overflow", "auto");
38448 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38450 //if(c.titlebar !== false){
38451 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38452 this.titleEl.hide();
38454 this.titleEl.show();
38455 if(this.config.title){
38456 this.titleTextEl.innerHTML = this.config.title;
38460 if(this.config.collapsed){
38461 this.collapse(true);
38463 if(this.config.hidden){
38467 if (this.unrendered_panels && this.unrendered_panels.length) {
38468 for (var i =0;i< this.unrendered_panels.length; i++) {
38469 this.add(this.unrendered_panels[i]);
38471 this.unrendered_panels = null;
38477 applyConfig : function(c)
38480 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38481 var dh = Roo.DomHelper;
38482 if(c.titlebar !== false){
38483 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38484 this.collapseBtn.on("click", this.collapse, this);
38485 this.collapseBtn.enableDisplayMode();
38487 if(c.showPin === true || this.showPin){
38488 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38489 this.stickBtn.enableDisplayMode();
38490 this.stickBtn.on("click", this.expand, this);
38491 this.stickBtn.hide();
38496 /** This region's collapsed element
38497 * @type Roo.Element */
38500 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38501 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38504 if(c.floatable !== false){
38505 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38506 this.collapsedEl.on("click", this.collapseClick, this);
38509 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38510 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38511 id: "message", unselectable: "on", style:{"float":"left"}});
38512 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38514 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38515 this.expandBtn.on("click", this.expand, this);
38519 if(this.collapseBtn){
38520 this.collapseBtn.setVisible(c.collapsible == true);
38523 this.cmargins = c.cmargins || this.cmargins ||
38524 (this.position == "west" || this.position == "east" ?
38525 {top: 0, left: 2, right:2, bottom: 0} :
38526 {top: 2, left: 0, right:0, bottom: 2});
38528 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38531 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38533 this.autoScroll = c.autoScroll || false;
38538 this.duration = c.duration || .30;
38539 this.slideDuration = c.slideDuration || .45;
38544 * Returns true if this region is currently visible.
38545 * @return {Boolean}
38547 isVisible : function(){
38548 return this.visible;
38552 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38553 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38555 //setCollapsedTitle : function(title){
38556 // title = title || " ";
38557 // if(this.collapsedTitleTextEl){
38558 // this.collapsedTitleTextEl.innerHTML = title;
38562 getBox : function(){
38564 // if(!this.collapsed){
38565 b = this.el.getBox(false, true);
38567 // b = this.collapsedEl.getBox(false, true);
38572 getMargins : function(){
38573 return this.margins;
38574 //return this.collapsed ? this.cmargins : this.margins;
38577 highlight : function(){
38578 this.el.addClass("x-layout-panel-dragover");
38581 unhighlight : function(){
38582 this.el.removeClass("x-layout-panel-dragover");
38585 updateBox : function(box)
38587 if (!this.bodyEl) {
38588 return; // not rendered yet..
38592 if(!this.collapsed){
38593 this.el.dom.style.left = box.x + "px";
38594 this.el.dom.style.top = box.y + "px";
38595 this.updateBody(box.width, box.height);
38597 this.collapsedEl.dom.style.left = box.x + "px";
38598 this.collapsedEl.dom.style.top = box.y + "px";
38599 this.collapsedEl.setSize(box.width, box.height);
38602 this.tabs.autoSizeTabs();
38606 updateBody : function(w, h)
38609 this.el.setWidth(w);
38610 w -= this.el.getBorderWidth("rl");
38611 if(this.config.adjustments){
38612 w += this.config.adjustments[0];
38615 if(h !== null && h > 0){
38616 this.el.setHeight(h);
38617 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38618 h -= this.el.getBorderWidth("tb");
38619 if(this.config.adjustments){
38620 h += this.config.adjustments[1];
38622 this.bodyEl.setHeight(h);
38624 h = this.tabs.syncHeight(h);
38627 if(this.panelSize){
38628 w = w !== null ? w : this.panelSize.width;
38629 h = h !== null ? h : this.panelSize.height;
38631 if(this.activePanel){
38632 var el = this.activePanel.getEl();
38633 w = w !== null ? w : el.getWidth();
38634 h = h !== null ? h : el.getHeight();
38635 this.panelSize = {width: w, height: h};
38636 this.activePanel.setSize(w, h);
38638 if(Roo.isIE && this.tabs){
38639 this.tabs.el.repaint();
38644 * Returns the container element for this region.
38645 * @return {Roo.Element}
38647 getEl : function(){
38652 * Hides this region.
38655 //if(!this.collapsed){
38656 this.el.dom.style.left = "-2000px";
38659 // this.collapsedEl.dom.style.left = "-2000px";
38660 // this.collapsedEl.hide();
38662 this.visible = false;
38663 this.fireEvent("visibilitychange", this, false);
38667 * Shows this region if it was previously hidden.
38670 //if(!this.collapsed){
38673 // this.collapsedEl.show();
38675 this.visible = true;
38676 this.fireEvent("visibilitychange", this, true);
38679 closeClicked : function(){
38680 if(this.activePanel){
38681 this.remove(this.activePanel);
38685 collapseClick : function(e){
38687 e.stopPropagation();
38690 e.stopPropagation();
38696 * Collapses this region.
38697 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38700 collapse : function(skipAnim, skipCheck = false){
38701 if(this.collapsed) {
38705 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38707 this.collapsed = true;
38709 this.split.el.hide();
38711 if(this.config.animate && skipAnim !== true){
38712 this.fireEvent("invalidated", this);
38713 this.animateCollapse();
38715 this.el.setLocation(-20000,-20000);
38717 this.collapsedEl.show();
38718 this.fireEvent("collapsed", this);
38719 this.fireEvent("invalidated", this);
38725 animateCollapse : function(){
38730 * Expands this region if it was previously collapsed.
38731 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38732 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38735 expand : function(e, skipAnim){
38737 e.stopPropagation();
38739 if(!this.collapsed || this.el.hasActiveFx()) {
38743 this.afterSlideIn();
38746 this.collapsed = false;
38747 if(this.config.animate && skipAnim !== true){
38748 this.animateExpand();
38752 this.split.el.show();
38754 this.collapsedEl.setLocation(-2000,-2000);
38755 this.collapsedEl.hide();
38756 this.fireEvent("invalidated", this);
38757 this.fireEvent("expanded", this);
38761 animateExpand : function(){
38765 initTabs : function()
38767 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38769 var ts = new Roo.bootstrap.panel.Tabs({
38770 el: this.bodyEl.dom,
38772 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38773 disableTooltips: this.config.disableTabTips,
38774 toolbar : this.config.toolbar
38777 if(this.config.hideTabs){
38778 ts.stripWrap.setDisplayed(false);
38781 ts.resizeTabs = this.config.resizeTabs === true;
38782 ts.minTabWidth = this.config.minTabWidth || 40;
38783 ts.maxTabWidth = this.config.maxTabWidth || 250;
38784 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38785 ts.monitorResize = false;
38786 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38787 ts.bodyEl.addClass('roo-layout-tabs-body');
38788 this.panels.each(this.initPanelAsTab, this);
38791 initPanelAsTab : function(panel){
38792 var ti = this.tabs.addTab(
38796 this.config.closeOnTab && panel.isClosable(),
38799 if(panel.tabTip !== undefined){
38800 ti.setTooltip(panel.tabTip);
38802 ti.on("activate", function(){
38803 this.setActivePanel(panel);
38806 if(this.config.closeOnTab){
38807 ti.on("beforeclose", function(t, e){
38809 this.remove(panel);
38813 panel.tabItem = ti;
38818 updatePanelTitle : function(panel, title)
38820 if(this.activePanel == panel){
38821 this.updateTitle(title);
38824 var ti = this.tabs.getTab(panel.getEl().id);
38826 if(panel.tabTip !== undefined){
38827 ti.setTooltip(panel.tabTip);
38832 updateTitle : function(title){
38833 if(this.titleTextEl && !this.config.title){
38834 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38838 setActivePanel : function(panel)
38840 panel = this.getPanel(panel);
38841 if(this.activePanel && this.activePanel != panel){
38842 if(this.activePanel.setActiveState(false) === false){
38846 this.activePanel = panel;
38847 panel.setActiveState(true);
38848 if(this.panelSize){
38849 panel.setSize(this.panelSize.width, this.panelSize.height);
38852 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38854 this.updateTitle(panel.getTitle());
38856 this.fireEvent("invalidated", this);
38858 this.fireEvent("panelactivated", this, panel);
38862 * Shows the specified panel.
38863 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38864 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38866 showPanel : function(panel)
38868 panel = this.getPanel(panel);
38871 var tab = this.tabs.getTab(panel.getEl().id);
38872 if(tab.isHidden()){
38873 this.tabs.unhideTab(tab.id);
38877 this.setActivePanel(panel);
38884 * Get the active panel for this region.
38885 * @return {Roo.ContentPanel} The active panel or null
38887 getActivePanel : function(){
38888 return this.activePanel;
38891 validateVisibility : function(){
38892 if(this.panels.getCount() < 1){
38893 this.updateTitle(" ");
38894 this.closeBtn.hide();
38897 if(!this.isVisible()){
38904 * Adds the passed ContentPanel(s) to this region.
38905 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38906 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38908 add : function(panel)
38910 if(arguments.length > 1){
38911 for(var i = 0, len = arguments.length; i < len; i++) {
38912 this.add(arguments[i]);
38917 // if we have not been rendered yet, then we can not really do much of this..
38918 if (!this.bodyEl) {
38919 this.unrendered_panels.push(panel);
38926 if(this.hasPanel(panel)){
38927 this.showPanel(panel);
38930 panel.setRegion(this);
38931 this.panels.add(panel);
38932 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38933 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38934 // and hide them... ???
38935 this.bodyEl.dom.appendChild(panel.getEl().dom);
38936 if(panel.background !== true){
38937 this.setActivePanel(panel);
38939 this.fireEvent("paneladded", this, panel);
38946 this.initPanelAsTab(panel);
38950 if(panel.background !== true){
38951 this.tabs.activate(panel.getEl().id);
38953 this.fireEvent("paneladded", this, panel);
38958 * Hides the tab for the specified panel.
38959 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38961 hidePanel : function(panel){
38962 if(this.tabs && (panel = this.getPanel(panel))){
38963 this.tabs.hideTab(panel.getEl().id);
38968 * Unhides the tab for a previously hidden panel.
38969 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38971 unhidePanel : function(panel){
38972 if(this.tabs && (panel = this.getPanel(panel))){
38973 this.tabs.unhideTab(panel.getEl().id);
38977 clearPanels : function(){
38978 while(this.panels.getCount() > 0){
38979 this.remove(this.panels.first());
38984 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38985 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38986 * @param {Boolean} preservePanel Overrides the config preservePanel option
38987 * @return {Roo.ContentPanel} The panel that was removed
38989 remove : function(panel, preservePanel)
38991 panel = this.getPanel(panel);
38996 this.fireEvent("beforeremove", this, panel, e);
38997 if(e.cancel === true){
39000 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39001 var panelId = panel.getId();
39002 this.panels.removeKey(panelId);
39004 document.body.appendChild(panel.getEl().dom);
39007 this.tabs.removeTab(panel.getEl().id);
39008 }else if (!preservePanel){
39009 this.bodyEl.dom.removeChild(panel.getEl().dom);
39011 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39012 var p = this.panels.first();
39013 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39014 tempEl.appendChild(p.getEl().dom);
39015 this.bodyEl.update("");
39016 this.bodyEl.dom.appendChild(p.getEl().dom);
39018 this.updateTitle(p.getTitle());
39020 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39021 this.setActivePanel(p);
39023 panel.setRegion(null);
39024 if(this.activePanel == panel){
39025 this.activePanel = null;
39027 if(this.config.autoDestroy !== false && preservePanel !== true){
39028 try{panel.destroy();}catch(e){}
39030 this.fireEvent("panelremoved", this, panel);
39035 * Returns the TabPanel component used by this region
39036 * @return {Roo.TabPanel}
39038 getTabs : function(){
39042 createTool : function(parentEl, className){
39043 var btn = Roo.DomHelper.append(parentEl, {
39045 cls: "x-layout-tools-button",
39048 cls: "roo-layout-tools-button-inner " + className,
39052 btn.addClassOnOver("roo-layout-tools-button-over");
39057 * Ext JS Library 1.1.1
39058 * Copyright(c) 2006-2007, Ext JS, LLC.
39060 * Originally Released Under LGPL - original licence link has changed is not relivant.
39063 * <script type="text/javascript">
39069 * @class Roo.SplitLayoutRegion
39070 * @extends Roo.LayoutRegion
39071 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39073 Roo.bootstrap.layout.Split = function(config){
39074 this.cursor = config.cursor;
39075 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39078 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39080 splitTip : "Drag to resize.",
39081 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39082 useSplitTips : false,
39084 applyConfig : function(config){
39085 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39088 onRender : function(ctr,pos) {
39090 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39091 if(!this.config.split){
39096 var splitEl = Roo.DomHelper.append(ctr.dom, {
39098 id: this.el.id + "-split",
39099 cls: "roo-layout-split roo-layout-split-"+this.position,
39102 /** The SplitBar for this region
39103 * @type Roo.SplitBar */
39104 // does not exist yet...
39105 Roo.log([this.position, this.orientation]);
39107 this.split = new Roo.bootstrap.SplitBar({
39108 dragElement : splitEl,
39109 resizingElement: this.el,
39110 orientation : this.orientation
39113 this.split.on("moved", this.onSplitMove, this);
39114 this.split.useShim = this.config.useShim === true;
39115 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39116 if(this.useSplitTips){
39117 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39119 //if(config.collapsible){
39120 // this.split.el.on("dblclick", this.collapse, this);
39123 if(typeof this.config.minSize != "undefined"){
39124 this.split.minSize = this.config.minSize;
39126 if(typeof this.config.maxSize != "undefined"){
39127 this.split.maxSize = this.config.maxSize;
39129 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39130 this.hideSplitter();
39135 getHMaxSize : function(){
39136 var cmax = this.config.maxSize || 10000;
39137 var center = this.mgr.getRegion("center");
39138 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39141 getVMaxSize : function(){
39142 var cmax = this.config.maxSize || 10000;
39143 var center = this.mgr.getRegion("center");
39144 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39147 onSplitMove : function(split, newSize){
39148 this.fireEvent("resized", this, newSize);
39152 * Returns the {@link Roo.SplitBar} for this region.
39153 * @return {Roo.SplitBar}
39155 getSplitBar : function(){
39160 this.hideSplitter();
39161 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39164 hideSplitter : function(){
39166 this.split.el.setLocation(-2000,-2000);
39167 this.split.el.hide();
39173 this.split.el.show();
39175 Roo.bootstrap.layout.Split.superclass.show.call(this);
39178 beforeSlide: function(){
39179 if(Roo.isGecko){// firefox overflow auto bug workaround
39180 this.bodyEl.clip();
39182 this.tabs.bodyEl.clip();
39184 if(this.activePanel){
39185 this.activePanel.getEl().clip();
39187 if(this.activePanel.beforeSlide){
39188 this.activePanel.beforeSlide();
39194 afterSlide : function(){
39195 if(Roo.isGecko){// firefox overflow auto bug workaround
39196 this.bodyEl.unclip();
39198 this.tabs.bodyEl.unclip();
39200 if(this.activePanel){
39201 this.activePanel.getEl().unclip();
39202 if(this.activePanel.afterSlide){
39203 this.activePanel.afterSlide();
39209 initAutoHide : function(){
39210 if(this.autoHide !== false){
39211 if(!this.autoHideHd){
39212 var st = new Roo.util.DelayedTask(this.slideIn, this);
39213 this.autoHideHd = {
39214 "mouseout": function(e){
39215 if(!e.within(this.el, true)){
39219 "mouseover" : function(e){
39225 this.el.on(this.autoHideHd);
39229 clearAutoHide : function(){
39230 if(this.autoHide !== false){
39231 this.el.un("mouseout", this.autoHideHd.mouseout);
39232 this.el.un("mouseover", this.autoHideHd.mouseover);
39236 clearMonitor : function(){
39237 Roo.get(document).un("click", this.slideInIf, this);
39240 // these names are backwards but not changed for compat
39241 slideOut : function(){
39242 if(this.isSlid || this.el.hasActiveFx()){
39245 this.isSlid = true;
39246 if(this.collapseBtn){
39247 this.collapseBtn.hide();
39249 this.closeBtnState = this.closeBtn.getStyle('display');
39250 this.closeBtn.hide();
39252 this.stickBtn.show();
39255 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39256 this.beforeSlide();
39257 this.el.setStyle("z-index", 10001);
39258 this.el.slideIn(this.getSlideAnchor(), {
39259 callback: function(){
39261 this.initAutoHide();
39262 Roo.get(document).on("click", this.slideInIf, this);
39263 this.fireEvent("slideshow", this);
39270 afterSlideIn : function(){
39271 this.clearAutoHide();
39272 this.isSlid = false;
39273 this.clearMonitor();
39274 this.el.setStyle("z-index", "");
39275 if(this.collapseBtn){
39276 this.collapseBtn.show();
39278 this.closeBtn.setStyle('display', this.closeBtnState);
39280 this.stickBtn.hide();
39282 this.fireEvent("slidehide", this);
39285 slideIn : function(cb){
39286 if(!this.isSlid || this.el.hasActiveFx()){
39290 this.isSlid = false;
39291 this.beforeSlide();
39292 this.el.slideOut(this.getSlideAnchor(), {
39293 callback: function(){
39294 this.el.setLeftTop(-10000, -10000);
39296 this.afterSlideIn();
39304 slideInIf : function(e){
39305 if(!e.within(this.el)){
39310 animateCollapse : function(){
39311 this.beforeSlide();
39312 this.el.setStyle("z-index", 20000);
39313 var anchor = this.getSlideAnchor();
39314 this.el.slideOut(anchor, {
39315 callback : function(){
39316 this.el.setStyle("z-index", "");
39317 this.collapsedEl.slideIn(anchor, {duration:.3});
39319 this.el.setLocation(-10000,-10000);
39321 this.fireEvent("collapsed", this);
39328 animateExpand : function(){
39329 this.beforeSlide();
39330 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39331 this.el.setStyle("z-index", 20000);
39332 this.collapsedEl.hide({
39335 this.el.slideIn(this.getSlideAnchor(), {
39336 callback : function(){
39337 this.el.setStyle("z-index", "");
39340 this.split.el.show();
39342 this.fireEvent("invalidated", this);
39343 this.fireEvent("expanded", this);
39371 getAnchor : function(){
39372 return this.anchors[this.position];
39375 getCollapseAnchor : function(){
39376 return this.canchors[this.position];
39379 getSlideAnchor : function(){
39380 return this.sanchors[this.position];
39383 getAlignAdj : function(){
39384 var cm = this.cmargins;
39385 switch(this.position){
39401 getExpandAdj : function(){
39402 var c = this.collapsedEl, cm = this.cmargins;
39403 switch(this.position){
39405 return [-(cm.right+c.getWidth()+cm.left), 0];
39408 return [cm.right+c.getWidth()+cm.left, 0];
39411 return [0, -(cm.top+cm.bottom+c.getHeight())];
39414 return [0, cm.top+cm.bottom+c.getHeight()];
39420 * Ext JS Library 1.1.1
39421 * Copyright(c) 2006-2007, Ext JS, LLC.
39423 * Originally Released Under LGPL - original licence link has changed is not relivant.
39426 * <script type="text/javascript">
39429 * These classes are private internal classes
39431 Roo.bootstrap.layout.Center = function(config){
39432 config.region = "center";
39433 Roo.bootstrap.layout.Region.call(this, config);
39434 this.visible = true;
39435 this.minWidth = config.minWidth || 20;
39436 this.minHeight = config.minHeight || 20;
39439 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39441 // center panel can't be hidden
39445 // center panel can't be hidden
39448 getMinWidth: function(){
39449 return this.minWidth;
39452 getMinHeight: function(){
39453 return this.minHeight;
39467 Roo.bootstrap.layout.North = function(config)
39469 config.region = 'north';
39470 config.cursor = 'n-resize';
39472 Roo.bootstrap.layout.Split.call(this, config);
39476 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39477 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39478 this.split.el.addClass("roo-layout-split-v");
39480 //var size = config.initialSize || config.height;
39481 //if(this.el && typeof size != "undefined"){
39482 // this.el.setHeight(size);
39485 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39487 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39490 onRender : function(ctr, pos)
39492 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39493 var size = this.config.initialSize || this.config.height;
39494 if(this.el && typeof size != "undefined"){
39495 this.el.setHeight(size);
39500 getBox : function(){
39501 if(this.collapsed){
39502 return this.collapsedEl.getBox();
39504 var box = this.el.getBox();
39506 box.height += this.split.el.getHeight();
39511 updateBox : function(box){
39512 if(this.split && !this.collapsed){
39513 box.height -= this.split.el.getHeight();
39514 this.split.el.setLeft(box.x);
39515 this.split.el.setTop(box.y+box.height);
39516 this.split.el.setWidth(box.width);
39518 if(this.collapsed){
39519 this.updateBody(box.width, null);
39521 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39529 Roo.bootstrap.layout.South = function(config){
39530 config.region = 'south';
39531 config.cursor = 's-resize';
39532 Roo.bootstrap.layout.Split.call(this, config);
39534 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39535 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39536 this.split.el.addClass("roo-layout-split-v");
39541 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39542 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39544 onRender : function(ctr, pos)
39546 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39547 var size = this.config.initialSize || this.config.height;
39548 if(this.el && typeof size != "undefined"){
39549 this.el.setHeight(size);
39554 getBox : function(){
39555 if(this.collapsed){
39556 return this.collapsedEl.getBox();
39558 var box = this.el.getBox();
39560 var sh = this.split.el.getHeight();
39567 updateBox : function(box){
39568 if(this.split && !this.collapsed){
39569 var sh = this.split.el.getHeight();
39572 this.split.el.setLeft(box.x);
39573 this.split.el.setTop(box.y-sh);
39574 this.split.el.setWidth(box.width);
39576 if(this.collapsed){
39577 this.updateBody(box.width, null);
39579 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39583 Roo.bootstrap.layout.East = function(config){
39584 config.region = "east";
39585 config.cursor = "e-resize";
39586 Roo.bootstrap.layout.Split.call(this, config);
39588 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39589 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39590 this.split.el.addClass("roo-layout-split-h");
39594 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39595 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39597 onRender : function(ctr, pos)
39599 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39600 var size = this.config.initialSize || this.config.width;
39601 if(this.el && typeof size != "undefined"){
39602 this.el.setWidth(size);
39607 getBox : function(){
39608 if(this.collapsed){
39609 return this.collapsedEl.getBox();
39611 var box = this.el.getBox();
39613 var sw = this.split.el.getWidth();
39620 updateBox : function(box){
39621 if(this.split && !this.collapsed){
39622 var sw = this.split.el.getWidth();
39624 this.split.el.setLeft(box.x);
39625 this.split.el.setTop(box.y);
39626 this.split.el.setHeight(box.height);
39629 if(this.collapsed){
39630 this.updateBody(null, box.height);
39632 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39636 Roo.bootstrap.layout.West = function(config){
39637 config.region = "west";
39638 config.cursor = "w-resize";
39640 Roo.bootstrap.layout.Split.call(this, config);
39642 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39643 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39644 this.split.el.addClass("roo-layout-split-h");
39648 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39649 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39651 onRender: function(ctr, pos)
39653 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39654 var size = this.config.initialSize || this.config.width;
39655 if(typeof size != "undefined"){
39656 this.el.setWidth(size);
39660 getBox : function(){
39661 if(this.collapsed){
39662 return this.collapsedEl.getBox();
39664 var box = this.el.getBox();
39665 if (box.width == 0) {
39666 box.width = this.config.width; // kludge?
39669 box.width += this.split.el.getWidth();
39674 updateBox : function(box){
39675 if(this.split && !this.collapsed){
39676 var sw = this.split.el.getWidth();
39678 this.split.el.setLeft(box.x+box.width);
39679 this.split.el.setTop(box.y);
39680 this.split.el.setHeight(box.height);
39682 if(this.collapsed){
39683 this.updateBody(null, box.height);
39685 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39687 });Roo.namespace("Roo.bootstrap.panel");/*
39689 * Ext JS Library 1.1.1
39690 * Copyright(c) 2006-2007, Ext JS, LLC.
39692 * Originally Released Under LGPL - original licence link has changed is not relivant.
39695 * <script type="text/javascript">
39698 * @class Roo.ContentPanel
39699 * @extends Roo.util.Observable
39700 * A basic ContentPanel element.
39701 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39702 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39703 * @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
39704 * @cfg {Boolean} closable True if the panel can be closed/removed
39705 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39706 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39707 * @cfg {Toolbar} toolbar A toolbar for this panel
39708 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39709 * @cfg {String} title The title for this panel
39710 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39711 * @cfg {String} url Calls {@link #setUrl} with this value
39712 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39713 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39714 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39715 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39716 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39717 * @cfg {Boolean} badges render the badges
39718 * @cfg {String} cls extra classes to use
39719 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39722 * Create a new ContentPanel.
39723 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39724 * @param {String/Object} config A string to set only the title or a config object
39725 * @param {String} content (optional) Set the HTML content for this panel
39726 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39728 Roo.bootstrap.panel.Content = function( config){
39730 this.tpl = config.tpl || false;
39732 var el = config.el;
39733 var content = config.content;
39735 if(config.autoCreate){ // xtype is available if this is called from factory
39738 this.el = Roo.get(el);
39739 if(!this.el && config && config.autoCreate){
39740 if(typeof config.autoCreate == "object"){
39741 if(!config.autoCreate.id){
39742 config.autoCreate.id = config.id||el;
39744 this.el = Roo.DomHelper.append(document.body,
39745 config.autoCreate, true);
39749 cls: (config.cls || '') +
39750 (config.background ? ' bg-' + config.background : '') +
39751 " roo-layout-inactive-content",
39754 if (config.iframe) {
39758 style : 'border: 0px',
39759 src : 'about:blank'
39765 elcfg.html = config.html;
39769 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39770 if (config.iframe) {
39771 this.iframeEl = this.el.select('iframe',true).first();
39776 this.closable = false;
39777 this.loaded = false;
39778 this.active = false;
39781 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39783 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39785 this.wrapEl = this.el; //this.el.wrap();
39787 if (config.toolbar.items) {
39788 ti = config.toolbar.items ;
39789 delete config.toolbar.items ;
39793 this.toolbar.render(this.wrapEl, 'before');
39794 for(var i =0;i < ti.length;i++) {
39795 // Roo.log(['add child', items[i]]);
39796 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39798 this.toolbar.items = nitems;
39799 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39800 delete config.toolbar;
39804 // xtype created footer. - not sure if will work as we normally have to render first..
39805 if (this.footer && !this.footer.el && this.footer.xtype) {
39806 if (!this.wrapEl) {
39807 this.wrapEl = this.el.wrap();
39810 this.footer.container = this.wrapEl.createChild();
39812 this.footer = Roo.factory(this.footer, Roo);
39817 if(typeof config == "string"){
39818 this.title = config;
39820 Roo.apply(this, config);
39824 this.resizeEl = Roo.get(this.resizeEl, true);
39826 this.resizeEl = this.el;
39828 // handle view.xtype
39836 * Fires when this panel is activated.
39837 * @param {Roo.ContentPanel} this
39841 * @event deactivate
39842 * Fires when this panel is activated.
39843 * @param {Roo.ContentPanel} this
39845 "deactivate" : true,
39849 * Fires when this panel is resized if fitToFrame is true.
39850 * @param {Roo.ContentPanel} this
39851 * @param {Number} width The width after any component adjustments
39852 * @param {Number} height The height after any component adjustments
39858 * Fires when this tab is created
39859 * @param {Roo.ContentPanel} this
39870 if(this.autoScroll && !this.iframe){
39871 this.resizeEl.setStyle("overflow", "auto");
39873 // fix randome scrolling
39874 //this.el.on('scroll', function() {
39875 // Roo.log('fix random scolling');
39876 // this.scrollTo('top',0);
39879 content = content || this.content;
39881 this.setContent(content);
39883 if(config && config.url){
39884 this.setUrl(this.url, this.params, this.loadOnce);
39889 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39891 if (this.view && typeof(this.view.xtype) != 'undefined') {
39892 this.view.el = this.el.appendChild(document.createElement("div"));
39893 this.view = Roo.factory(this.view);
39894 this.view.render && this.view.render(false, '');
39898 this.fireEvent('render', this);
39901 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39911 setRegion : function(region){
39912 this.region = region;
39913 this.setActiveClass(region && !this.background);
39917 setActiveClass: function(state)
39920 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39921 this.el.setStyle('position','relative');
39923 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39924 this.el.setStyle('position', 'absolute');
39929 * Returns the toolbar for this Panel if one was configured.
39930 * @return {Roo.Toolbar}
39932 getToolbar : function(){
39933 return this.toolbar;
39936 setActiveState : function(active)
39938 this.active = active;
39939 this.setActiveClass(active);
39941 if(this.fireEvent("deactivate", this) === false){
39946 this.fireEvent("activate", this);
39950 * Updates this panel's element (not for iframe)
39951 * @param {String} content The new content
39952 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39954 setContent : function(content, loadScripts){
39959 this.el.update(content, loadScripts);
39962 ignoreResize : function(w, h){
39963 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39966 this.lastSize = {width: w, height: h};
39971 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39972 * @return {Roo.UpdateManager} The UpdateManager
39974 getUpdateManager : function(){
39978 return this.el.getUpdateManager();
39981 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39982 * Does not work with IFRAME contents
39983 * @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:
39986 url: "your-url.php",
39987 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39988 callback: yourFunction,
39989 scope: yourObject, //(optional scope)
39992 text: "Loading...",
39998 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39999 * 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.
40000 * @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}
40001 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40002 * @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.
40003 * @return {Roo.ContentPanel} this
40011 var um = this.el.getUpdateManager();
40012 um.update.apply(um, arguments);
40018 * 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.
40019 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40020 * @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)
40021 * @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)
40022 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40024 setUrl : function(url, params, loadOnce){
40026 this.iframeEl.dom.src = url;
40030 if(this.refreshDelegate){
40031 this.removeListener("activate", this.refreshDelegate);
40033 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40034 this.on("activate", this.refreshDelegate);
40035 return this.el.getUpdateManager();
40038 _handleRefresh : function(url, params, loadOnce){
40039 if(!loadOnce || !this.loaded){
40040 var updater = this.el.getUpdateManager();
40041 updater.update(url, params, this._setLoaded.createDelegate(this));
40045 _setLoaded : function(){
40046 this.loaded = true;
40050 * Returns this panel's id
40053 getId : function(){
40058 * Returns this panel's element - used by regiosn to add.
40059 * @return {Roo.Element}
40061 getEl : function(){
40062 return this.wrapEl || this.el;
40067 adjustForComponents : function(width, height)
40069 //Roo.log('adjustForComponents ');
40070 if(this.resizeEl != this.el){
40071 width -= this.el.getFrameWidth('lr');
40072 height -= this.el.getFrameWidth('tb');
40075 var te = this.toolbar.getEl();
40076 te.setWidth(width);
40077 height -= te.getHeight();
40080 var te = this.footer.getEl();
40081 te.setWidth(width);
40082 height -= te.getHeight();
40086 if(this.adjustments){
40087 width += this.adjustments[0];
40088 height += this.adjustments[1];
40090 return {"width": width, "height": height};
40093 setSize : function(width, height){
40094 if(this.fitToFrame && !this.ignoreResize(width, height)){
40095 if(this.fitContainer && this.resizeEl != this.el){
40096 this.el.setSize(width, height);
40098 var size = this.adjustForComponents(width, height);
40100 this.iframeEl.setSize(width,height);
40103 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40104 this.fireEvent('resize', this, size.width, size.height);
40111 * Returns this panel's title
40114 getTitle : function(){
40116 if (typeof(this.title) != 'object') {
40121 for (var k in this.title) {
40122 if (!this.title.hasOwnProperty(k)) {
40126 if (k.indexOf('-') >= 0) {
40127 var s = k.split('-');
40128 for (var i = 0; i<s.length; i++) {
40129 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40132 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40139 * Set this panel's title
40140 * @param {String} title
40142 setTitle : function(title){
40143 this.title = title;
40145 this.region.updatePanelTitle(this, title);
40150 * Returns true is this panel was configured to be closable
40151 * @return {Boolean}
40153 isClosable : function(){
40154 return this.closable;
40157 beforeSlide : function(){
40159 this.resizeEl.clip();
40162 afterSlide : function(){
40164 this.resizeEl.unclip();
40168 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40169 * Will fail silently if the {@link #setUrl} method has not been called.
40170 * This does not activate the panel, just updates its content.
40172 refresh : function(){
40173 if(this.refreshDelegate){
40174 this.loaded = false;
40175 this.refreshDelegate();
40180 * Destroys this panel
40182 destroy : function(){
40183 this.el.removeAllListeners();
40184 var tempEl = document.createElement("span");
40185 tempEl.appendChild(this.el.dom);
40186 tempEl.innerHTML = "";
40192 * form - if the content panel contains a form - this is a reference to it.
40193 * @type {Roo.form.Form}
40197 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40198 * This contains a reference to it.
40204 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40214 * @param {Object} cfg Xtype definition of item to add.
40218 getChildContainer: function () {
40219 return this.getEl();
40224 var ret = new Roo.factory(cfg);
40229 if (cfg.xtype.match(/^Form$/)) {
40232 //if (this.footer) {
40233 // el = this.footer.container.insertSibling(false, 'before');
40235 el = this.el.createChild();
40238 this.form = new Roo.form.Form(cfg);
40241 if ( this.form.allItems.length) {
40242 this.form.render(el.dom);
40246 // should only have one of theses..
40247 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40248 // views.. should not be just added - used named prop 'view''
40250 cfg.el = this.el.appendChild(document.createElement("div"));
40253 var ret = new Roo.factory(cfg);
40255 ret.render && ret.render(false, ''); // render blank..
40265 * @class Roo.bootstrap.panel.Grid
40266 * @extends Roo.bootstrap.panel.Content
40268 * Create a new GridPanel.
40269 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40270 * @param {Object} config A the config object
40276 Roo.bootstrap.panel.Grid = function(config)
40280 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40281 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40283 config.el = this.wrapper;
40284 //this.el = this.wrapper;
40286 if (config.container) {
40287 // ctor'ed from a Border/panel.grid
40290 this.wrapper.setStyle("overflow", "hidden");
40291 this.wrapper.addClass('roo-grid-container');
40296 if(config.toolbar){
40297 var tool_el = this.wrapper.createChild();
40298 this.toolbar = Roo.factory(config.toolbar);
40300 if (config.toolbar.items) {
40301 ti = config.toolbar.items ;
40302 delete config.toolbar.items ;
40306 this.toolbar.render(tool_el);
40307 for(var i =0;i < ti.length;i++) {
40308 // Roo.log(['add child', items[i]]);
40309 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40311 this.toolbar.items = nitems;
40313 delete config.toolbar;
40316 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40317 config.grid.scrollBody = true;;
40318 config.grid.monitorWindowResize = false; // turn off autosizing
40319 config.grid.autoHeight = false;
40320 config.grid.autoWidth = false;
40322 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40324 if (config.background) {
40325 // render grid on panel activation (if panel background)
40326 this.on('activate', function(gp) {
40327 if (!gp.grid.rendered) {
40328 gp.grid.render(this.wrapper);
40329 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40334 this.grid.render(this.wrapper);
40335 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40338 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40339 // ??? needed ??? config.el = this.wrapper;
40344 // xtype created footer. - not sure if will work as we normally have to render first..
40345 if (this.footer && !this.footer.el && this.footer.xtype) {
40347 var ctr = this.grid.getView().getFooterPanel(true);
40348 this.footer.dataSource = this.grid.dataSource;
40349 this.footer = Roo.factory(this.footer, Roo);
40350 this.footer.render(ctr);
40360 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40361 getId : function(){
40362 return this.grid.id;
40366 * Returns the grid for this panel
40367 * @return {Roo.bootstrap.Table}
40369 getGrid : function(){
40373 setSize : function(width, height){
40374 if(!this.ignoreResize(width, height)){
40375 var grid = this.grid;
40376 var size = this.adjustForComponents(width, height);
40377 // tfoot is not a footer?
40380 var gridel = grid.getGridEl();
40381 gridel.setSize(size.width, size.height);
40383 var tbd = grid.getGridEl().select('tbody', true).first();
40384 var thd = grid.getGridEl().select('thead',true).first();
40385 var tbf= grid.getGridEl().select('tfoot', true).first();
40388 size.height -= tbf.getHeight();
40391 size.height -= thd.getHeight();
40394 tbd.setSize(size.width, size.height );
40395 // this is for the account management tab -seems to work there.
40396 var thd = grid.getGridEl().select('thead',true).first();
40398 // tbd.setSize(size.width, size.height - thd.getHeight());
40407 beforeSlide : function(){
40408 this.grid.getView().scroller.clip();
40411 afterSlide : function(){
40412 this.grid.getView().scroller.unclip();
40415 destroy : function(){
40416 this.grid.destroy();
40418 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40423 * @class Roo.bootstrap.panel.Nest
40424 * @extends Roo.bootstrap.panel.Content
40426 * Create a new Panel, that can contain a layout.Border.
40429 * @param {Roo.BorderLayout} layout The layout for this panel
40430 * @param {String/Object} config A string to set only the title or a config object
40432 Roo.bootstrap.panel.Nest = function(config)
40434 // construct with only one argument..
40435 /* FIXME - implement nicer consturctors
40436 if (layout.layout) {
40438 layout = config.layout;
40439 delete config.layout;
40441 if (layout.xtype && !layout.getEl) {
40442 // then layout needs constructing..
40443 layout = Roo.factory(layout, Roo);
40447 config.el = config.layout.getEl();
40449 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40451 config.layout.monitorWindowResize = false; // turn off autosizing
40452 this.layout = config.layout;
40453 this.layout.getEl().addClass("roo-layout-nested-layout");
40454 this.layout.parent = this;
40461 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40463 setSize : function(width, height){
40464 if(!this.ignoreResize(width, height)){
40465 var size = this.adjustForComponents(width, height);
40466 var el = this.layout.getEl();
40467 if (size.height < 1) {
40468 el.setWidth(size.width);
40470 el.setSize(size.width, size.height);
40472 var touch = el.dom.offsetWidth;
40473 this.layout.layout();
40474 // ie requires a double layout on the first pass
40475 if(Roo.isIE && !this.initialized){
40476 this.initialized = true;
40477 this.layout.layout();
40482 // activate all subpanels if not currently active..
40484 setActiveState : function(active){
40485 this.active = active;
40486 this.setActiveClass(active);
40489 this.fireEvent("deactivate", this);
40493 this.fireEvent("activate", this);
40494 // not sure if this should happen before or after..
40495 if (!this.layout) {
40496 return; // should not happen..
40499 for (var r in this.layout.regions) {
40500 reg = this.layout.getRegion(r);
40501 if (reg.getActivePanel()) {
40502 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40503 reg.setActivePanel(reg.getActivePanel());
40506 if (!reg.panels.length) {
40509 reg.showPanel(reg.getPanel(0));
40518 * Returns the nested BorderLayout for this panel
40519 * @return {Roo.BorderLayout}
40521 getLayout : function(){
40522 return this.layout;
40526 * Adds a xtype elements to the layout of the nested panel
40530 xtype : 'ContentPanel',
40537 xtype : 'NestedLayoutPanel',
40543 items : [ ... list of content panels or nested layout panels.. ]
40547 * @param {Object} cfg Xtype definition of item to add.
40549 addxtype : function(cfg) {
40550 return this.layout.addxtype(cfg);
40555 * Ext JS Library 1.1.1
40556 * Copyright(c) 2006-2007, Ext JS, LLC.
40558 * Originally Released Under LGPL - original licence link has changed is not relivant.
40561 * <script type="text/javascript">
40564 * @class Roo.TabPanel
40565 * @extends Roo.util.Observable
40566 * A lightweight tab container.
40570 // basic tabs 1, built from existing content
40571 var tabs = new Roo.TabPanel("tabs1");
40572 tabs.addTab("script", "View Script");
40573 tabs.addTab("markup", "View Markup");
40574 tabs.activate("script");
40576 // more advanced tabs, built from javascript
40577 var jtabs = new Roo.TabPanel("jtabs");
40578 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40580 // set up the UpdateManager
40581 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40582 var updater = tab2.getUpdateManager();
40583 updater.setDefaultUrl("ajax1.htm");
40584 tab2.on('activate', updater.refresh, updater, true);
40586 // Use setUrl for Ajax loading
40587 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40588 tab3.setUrl("ajax2.htm", null, true);
40591 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40594 jtabs.activate("jtabs-1");
40597 * Create a new TabPanel.
40598 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40599 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40601 Roo.bootstrap.panel.Tabs = function(config){
40603 * The container element for this TabPanel.
40604 * @type Roo.Element
40606 this.el = Roo.get(config.el);
40609 if(typeof config == "boolean"){
40610 this.tabPosition = config ? "bottom" : "top";
40612 Roo.apply(this, config);
40616 if(this.tabPosition == "bottom"){
40617 // if tabs are at the bottom = create the body first.
40618 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40619 this.el.addClass("roo-tabs-bottom");
40621 // next create the tabs holders
40623 if (this.tabPosition == "west"){
40625 var reg = this.region; // fake it..
40627 if (!reg.mgr.parent) {
40630 reg = reg.mgr.parent.region;
40632 Roo.log("got nest?");
40634 if (reg.mgr.getRegion('west')) {
40635 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40636 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40637 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40638 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40639 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40647 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40648 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40649 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40650 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40655 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40658 // finally - if tabs are at the top, then create the body last..
40659 if(this.tabPosition != "bottom"){
40660 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40661 * @type Roo.Element
40663 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40664 this.el.addClass("roo-tabs-top");
40668 this.bodyEl.setStyle("position", "relative");
40670 this.active = null;
40671 this.activateDelegate = this.activate.createDelegate(this);
40676 * Fires when the active tab changes
40677 * @param {Roo.TabPanel} this
40678 * @param {Roo.TabPanelItem} activePanel The new active tab
40682 * @event beforetabchange
40683 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40684 * @param {Roo.TabPanel} this
40685 * @param {Object} e Set cancel to true on this object to cancel the tab change
40686 * @param {Roo.TabPanelItem} tab The tab being changed to
40688 "beforetabchange" : true
40691 Roo.EventManager.onWindowResize(this.onResize, this);
40692 this.cpad = this.el.getPadding("lr");
40693 this.hiddenCount = 0;
40696 // toolbar on the tabbar support...
40697 if (this.toolbar) {
40698 alert("no toolbar support yet");
40699 this.toolbar = false;
40701 var tcfg = this.toolbar;
40702 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40703 this.toolbar = new Roo.Toolbar(tcfg);
40704 if (Roo.isSafari) {
40705 var tbl = tcfg.container.child('table', true);
40706 tbl.setAttribute('width', '100%');
40714 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40717 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40719 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40721 tabPosition : "top",
40723 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40725 currentTabWidth : 0,
40727 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40731 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40735 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40737 preferredTabWidth : 175,
40739 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40741 resizeTabs : false,
40743 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40745 monitorResize : true,
40747 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40749 toolbar : false, // set by caller..
40751 region : false, /// set by caller
40753 disableTooltips : true, // not used yet...
40756 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40757 * @param {String} id The id of the div to use <b>or create</b>
40758 * @param {String} text The text for the tab
40759 * @param {String} content (optional) Content to put in the TabPanelItem body
40760 * @param {Boolean} closable (optional) True to create a close icon on the tab
40761 * @return {Roo.TabPanelItem} The created TabPanelItem
40763 addTab : function(id, text, content, closable, tpl)
40765 var item = new Roo.bootstrap.panel.TabItem({
40769 closable : closable,
40772 this.addTabItem(item);
40774 item.setContent(content);
40780 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40781 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40782 * @return {Roo.TabPanelItem}
40784 getTab : function(id){
40785 return this.items[id];
40789 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40790 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40792 hideTab : function(id){
40793 var t = this.items[id];
40796 this.hiddenCount++;
40797 this.autoSizeTabs();
40802 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40803 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40805 unhideTab : function(id){
40806 var t = this.items[id];
40808 t.setHidden(false);
40809 this.hiddenCount--;
40810 this.autoSizeTabs();
40815 * Adds an existing {@link Roo.TabPanelItem}.
40816 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40818 addTabItem : function(item)
40820 this.items[item.id] = item;
40821 this.items.push(item);
40822 this.autoSizeTabs();
40823 // if(this.resizeTabs){
40824 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40825 // this.autoSizeTabs();
40827 // item.autoSize();
40832 * Removes a {@link Roo.TabPanelItem}.
40833 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40835 removeTab : function(id){
40836 var items = this.items;
40837 var tab = items[id];
40838 if(!tab) { return; }
40839 var index = items.indexOf(tab);
40840 if(this.active == tab && items.length > 1){
40841 var newTab = this.getNextAvailable(index);
40846 this.stripEl.dom.removeChild(tab.pnode.dom);
40847 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40848 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40850 items.splice(index, 1);
40851 delete this.items[tab.id];
40852 tab.fireEvent("close", tab);
40853 tab.purgeListeners();
40854 this.autoSizeTabs();
40857 getNextAvailable : function(start){
40858 var items = this.items;
40860 // look for a next tab that will slide over to
40861 // replace the one being removed
40862 while(index < items.length){
40863 var item = items[++index];
40864 if(item && !item.isHidden()){
40868 // if one isn't found select the previous tab (on the left)
40871 var item = items[--index];
40872 if(item && !item.isHidden()){
40880 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40881 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40883 disableTab : function(id){
40884 var tab = this.items[id];
40885 if(tab && this.active != tab){
40891 * Enables a {@link Roo.TabPanelItem} that is disabled.
40892 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40894 enableTab : function(id){
40895 var tab = this.items[id];
40900 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40901 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40902 * @return {Roo.TabPanelItem} The TabPanelItem.
40904 activate : function(id)
40906 //Roo.log('activite:' + id);
40908 var tab = this.items[id];
40912 if(tab == this.active || tab.disabled){
40916 this.fireEvent("beforetabchange", this, e, tab);
40917 if(e.cancel !== true && !tab.disabled){
40919 this.active.hide();
40921 this.active = this.items[id];
40922 this.active.show();
40923 this.fireEvent("tabchange", this, this.active);
40929 * Gets the active {@link Roo.TabPanelItem}.
40930 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40932 getActiveTab : function(){
40933 return this.active;
40937 * Updates the tab body element to fit the height of the container element
40938 * for overflow scrolling
40939 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40941 syncHeight : function(targetHeight){
40942 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40943 var bm = this.bodyEl.getMargins();
40944 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40945 this.bodyEl.setHeight(newHeight);
40949 onResize : function(){
40950 if(this.monitorResize){
40951 this.autoSizeTabs();
40956 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40958 beginUpdate : function(){
40959 this.updating = true;
40963 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40965 endUpdate : function(){
40966 this.updating = false;
40967 this.autoSizeTabs();
40971 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40973 autoSizeTabs : function()
40975 var count = this.items.length;
40976 var vcount = count - this.hiddenCount;
40979 this.stripEl.hide();
40981 this.stripEl.show();
40984 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40989 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40990 var availWidth = Math.floor(w / vcount);
40991 var b = this.stripBody;
40992 if(b.getWidth() > w){
40993 var tabs = this.items;
40994 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40995 if(availWidth < this.minTabWidth){
40996 /*if(!this.sleft){ // incomplete scrolling code
40997 this.createScrollButtons();
41000 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41003 if(this.currentTabWidth < this.preferredTabWidth){
41004 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41010 * Returns the number of tabs in this TabPanel.
41013 getCount : function(){
41014 return this.items.length;
41018 * Resizes all the tabs to the passed width
41019 * @param {Number} The new width
41021 setTabWidth : function(width){
41022 this.currentTabWidth = width;
41023 for(var i = 0, len = this.items.length; i < len; i++) {
41024 if(!this.items[i].isHidden()) {
41025 this.items[i].setWidth(width);
41031 * Destroys this TabPanel
41032 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41034 destroy : function(removeEl){
41035 Roo.EventManager.removeResizeListener(this.onResize, this);
41036 for(var i = 0, len = this.items.length; i < len; i++){
41037 this.items[i].purgeListeners();
41039 if(removeEl === true){
41040 this.el.update("");
41045 createStrip : function(container)
41047 var strip = document.createElement("nav");
41048 strip.className = Roo.bootstrap.version == 4 ?
41049 "navbar-light bg-light" :
41050 "navbar navbar-default"; //"x-tabs-wrap";
41051 container.appendChild(strip);
41055 createStripList : function(strip)
41057 // div wrapper for retard IE
41058 // returns the "tr" element.
41059 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41060 //'<div class="x-tabs-strip-wrap">'+
41061 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41062 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41063 return strip.firstChild; //.firstChild.firstChild.firstChild;
41065 createBody : function(container)
41067 var body = document.createElement("div");
41068 Roo.id(body, "tab-body");
41069 //Roo.fly(body).addClass("x-tabs-body");
41070 Roo.fly(body).addClass("tab-content");
41071 container.appendChild(body);
41074 createItemBody :function(bodyEl, id){
41075 var body = Roo.getDom(id);
41077 body = document.createElement("div");
41080 //Roo.fly(body).addClass("x-tabs-item-body");
41081 Roo.fly(body).addClass("tab-pane");
41082 bodyEl.insertBefore(body, bodyEl.firstChild);
41086 createStripElements : function(stripEl, text, closable, tpl)
41088 var td = document.createElement("li"); // was td..
41089 td.className = 'nav-item';
41091 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41094 stripEl.appendChild(td);
41096 td.className = "x-tabs-closable";
41097 if(!this.closeTpl){
41098 this.closeTpl = new Roo.Template(
41099 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41100 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41101 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41104 var el = this.closeTpl.overwrite(td, {"text": text});
41105 var close = el.getElementsByTagName("div")[0];
41106 var inner = el.getElementsByTagName("em")[0];
41107 return {"el": el, "close": close, "inner": inner};
41110 // not sure what this is..
41111 // if(!this.tabTpl){
41112 //this.tabTpl = new Roo.Template(
41113 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41114 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41116 // this.tabTpl = new Roo.Template(
41117 // '<a href="#">' +
41118 // '<span unselectable="on"' +
41119 // (this.disableTooltips ? '' : ' title="{text}"') +
41120 // ' >{text}</span></a>'
41126 var template = tpl || this.tabTpl || false;
41129 template = new Roo.Template(
41130 Roo.bootstrap.version == 4 ?
41132 '<a class="nav-link" href="#" unselectable="on"' +
41133 (this.disableTooltips ? '' : ' title="{text}"') +
41136 '<a class="nav-link" href="#">' +
41137 '<span unselectable="on"' +
41138 (this.disableTooltips ? '' : ' title="{text}"') +
41139 ' >{text}</span></a>'
41144 switch (typeof(template)) {
41148 template = new Roo.Template(template);
41154 var el = template.overwrite(td, {"text": text});
41156 var inner = el.getElementsByTagName("span")[0];
41158 return {"el": el, "inner": inner};
41166 * @class Roo.TabPanelItem
41167 * @extends Roo.util.Observable
41168 * Represents an individual item (tab plus body) in a TabPanel.
41169 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41170 * @param {String} id The id of this TabPanelItem
41171 * @param {String} text The text for the tab of this TabPanelItem
41172 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41174 Roo.bootstrap.panel.TabItem = function(config){
41176 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41177 * @type Roo.TabPanel
41179 this.tabPanel = config.panel;
41181 * The id for this TabPanelItem
41184 this.id = config.id;
41186 this.disabled = false;
41188 this.text = config.text;
41190 this.loaded = false;
41191 this.closable = config.closable;
41194 * The body element for this TabPanelItem.
41195 * @type Roo.Element
41197 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41198 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41199 this.bodyEl.setStyle("display", "block");
41200 this.bodyEl.setStyle("zoom", "1");
41201 //this.hideAction();
41203 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41205 this.el = Roo.get(els.el);
41206 this.inner = Roo.get(els.inner, true);
41207 this.textEl = Roo.bootstrap.version == 4 ?
41208 this.el : Roo.get(this.el.dom.firstChild, true);
41210 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41211 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41214 // this.el.on("mousedown", this.onTabMouseDown, this);
41215 this.el.on("click", this.onTabClick, this);
41217 if(config.closable){
41218 var c = Roo.get(els.close, true);
41219 c.dom.title = this.closeText;
41220 c.addClassOnOver("close-over");
41221 c.on("click", this.closeClick, this);
41227 * Fires when this tab becomes the active tab.
41228 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41229 * @param {Roo.TabPanelItem} this
41233 * @event beforeclose
41234 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41235 * @param {Roo.TabPanelItem} this
41236 * @param {Object} e Set cancel to true on this object to cancel the close.
41238 "beforeclose": true,
41241 * Fires when this tab is closed.
41242 * @param {Roo.TabPanelItem} this
41246 * @event deactivate
41247 * Fires when this tab is no longer the active tab.
41248 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41249 * @param {Roo.TabPanelItem} this
41251 "deactivate" : true
41253 this.hidden = false;
41255 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41258 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41260 purgeListeners : function(){
41261 Roo.util.Observable.prototype.purgeListeners.call(this);
41262 this.el.removeAllListeners();
41265 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41268 this.status_node.addClass("active");
41271 this.tabPanel.stripWrap.repaint();
41273 this.fireEvent("activate", this.tabPanel, this);
41277 * Returns true if this tab is the active tab.
41278 * @return {Boolean}
41280 isActive : function(){
41281 return this.tabPanel.getActiveTab() == this;
41285 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41288 this.status_node.removeClass("active");
41290 this.fireEvent("deactivate", this.tabPanel, this);
41293 hideAction : function(){
41294 this.bodyEl.hide();
41295 this.bodyEl.setStyle("position", "absolute");
41296 this.bodyEl.setLeft("-20000px");
41297 this.bodyEl.setTop("-20000px");
41300 showAction : function(){
41301 this.bodyEl.setStyle("position", "relative");
41302 this.bodyEl.setTop("");
41303 this.bodyEl.setLeft("");
41304 this.bodyEl.show();
41308 * Set the tooltip for the tab.
41309 * @param {String} tooltip The tab's tooltip
41311 setTooltip : function(text){
41312 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41313 this.textEl.dom.qtip = text;
41314 this.textEl.dom.removeAttribute('title');
41316 this.textEl.dom.title = text;
41320 onTabClick : function(e){
41321 e.preventDefault();
41322 this.tabPanel.activate(this.id);
41325 onTabMouseDown : function(e){
41326 e.preventDefault();
41327 this.tabPanel.activate(this.id);
41330 getWidth : function(){
41331 return this.inner.getWidth();
41334 setWidth : function(width){
41335 var iwidth = width - this.linode.getPadding("lr");
41336 this.inner.setWidth(iwidth);
41337 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41338 this.linode.setWidth(width);
41342 * Show or hide the tab
41343 * @param {Boolean} hidden True to hide or false to show.
41345 setHidden : function(hidden){
41346 this.hidden = hidden;
41347 this.linode.setStyle("display", hidden ? "none" : "");
41351 * Returns true if this tab is "hidden"
41352 * @return {Boolean}
41354 isHidden : function(){
41355 return this.hidden;
41359 * Returns the text for this tab
41362 getText : function(){
41366 autoSize : function(){
41367 //this.el.beginMeasure();
41368 this.textEl.setWidth(1);
41370 * #2804 [new] Tabs in Roojs
41371 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41373 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41374 //this.el.endMeasure();
41378 * Sets the text for the tab (Note: this also sets the tooltip text)
41379 * @param {String} text The tab's text and tooltip
41381 setText : function(text){
41383 this.textEl.update(text);
41384 this.setTooltip(text);
41385 //if(!this.tabPanel.resizeTabs){
41386 // this.autoSize();
41390 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41392 activate : function(){
41393 this.tabPanel.activate(this.id);
41397 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41399 disable : function(){
41400 if(this.tabPanel.active != this){
41401 this.disabled = true;
41402 this.status_node.addClass("disabled");
41407 * Enables this TabPanelItem if it was previously disabled.
41409 enable : function(){
41410 this.disabled = false;
41411 this.status_node.removeClass("disabled");
41415 * Sets the content for this TabPanelItem.
41416 * @param {String} content The content
41417 * @param {Boolean} loadScripts true to look for and load scripts
41419 setContent : function(content, loadScripts){
41420 this.bodyEl.update(content, loadScripts);
41424 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41425 * @return {Roo.UpdateManager} The UpdateManager
41427 getUpdateManager : function(){
41428 return this.bodyEl.getUpdateManager();
41432 * Set a URL to be used to load the content for this TabPanelItem.
41433 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41434 * @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)
41435 * @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)
41436 * @return {Roo.UpdateManager} The UpdateManager
41438 setUrl : function(url, params, loadOnce){
41439 if(this.refreshDelegate){
41440 this.un('activate', this.refreshDelegate);
41442 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41443 this.on("activate", this.refreshDelegate);
41444 return this.bodyEl.getUpdateManager();
41448 _handleRefresh : function(url, params, loadOnce){
41449 if(!loadOnce || !this.loaded){
41450 var updater = this.bodyEl.getUpdateManager();
41451 updater.update(url, params, this._setLoaded.createDelegate(this));
41456 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41457 * Will fail silently if the setUrl method has not been called.
41458 * This does not activate the panel, just updates its content.
41460 refresh : function(){
41461 if(this.refreshDelegate){
41462 this.loaded = false;
41463 this.refreshDelegate();
41468 _setLoaded : function(){
41469 this.loaded = true;
41473 closeClick : function(e){
41476 this.fireEvent("beforeclose", this, o);
41477 if(o.cancel !== true){
41478 this.tabPanel.removeTab(this.id);
41482 * The text displayed in the tooltip for the close icon.
41485 closeText : "Close this tab"
41488 * This script refer to:
41489 * Title: International Telephone Input
41490 * Author: Jack O'Connor
41491 * Code version: v12.1.12
41492 * Availability: https://github.com/jackocnr/intl-tel-input.git
41495 Roo.bootstrap.PhoneInputData = function() {
41498 "Afghanistan (افغانستان)",
41503 "Albania (Shqipëri)",
41508 "Algeria (الجزائر)",
41533 "Antigua and Barbuda",
41543 "Armenia (Հայաստան)",
41559 "Austria (Österreich)",
41564 "Azerbaijan (Azərbaycan)",
41574 "Bahrain (البحرين)",
41579 "Bangladesh (বাংলাদেশ)",
41589 "Belarus (Беларусь)",
41594 "Belgium (België)",
41624 "Bosnia and Herzegovina (Босна и Херцеговина)",
41639 "British Indian Ocean Territory",
41644 "British Virgin Islands",
41654 "Bulgaria (България)",
41664 "Burundi (Uburundi)",
41669 "Cambodia (កម្ពុជា)",
41674 "Cameroon (Cameroun)",
41683 ["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"]
41686 "Cape Verde (Kabu Verdi)",
41691 "Caribbean Netherlands",
41702 "Central African Republic (République centrafricaine)",
41722 "Christmas Island",
41728 "Cocos (Keeling) Islands",
41739 "Comoros (جزر القمر)",
41744 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41749 "Congo (Republic) (Congo-Brazzaville)",
41769 "Croatia (Hrvatska)",
41790 "Czech Republic (Česká republika)",
41795 "Denmark (Danmark)",
41810 "Dominican Republic (República Dominicana)",
41814 ["809", "829", "849"]
41832 "Equatorial Guinea (Guinea Ecuatorial)",
41852 "Falkland Islands (Islas Malvinas)",
41857 "Faroe Islands (Føroyar)",
41878 "French Guiana (Guyane française)",
41883 "French Polynesia (Polynésie française)",
41898 "Georgia (საქართველო)",
41903 "Germany (Deutschland)",
41923 "Greenland (Kalaallit Nunaat)",
41960 "Guinea-Bissau (Guiné Bissau)",
41985 "Hungary (Magyarország)",
41990 "Iceland (Ísland)",
42010 "Iraq (العراق)",
42026 "Israel (ישראל)",
42053 "Jordan (الأردن)",
42058 "Kazakhstan (Казахстан)",
42079 "Kuwait (الكويت)",
42084 "Kyrgyzstan (Кыргызстан)",
42094 "Latvia (Latvija)",
42099 "Lebanon (لبنان)",
42114 "Libya (ليبيا)",
42124 "Lithuania (Lietuva)",
42139 "Macedonia (FYROM) (Македонија)",
42144 "Madagascar (Madagasikara)",
42174 "Marshall Islands",
42184 "Mauritania (موريتانيا)",
42189 "Mauritius (Moris)",
42210 "Moldova (Republica Moldova)",
42220 "Mongolia (Монгол)",
42225 "Montenegro (Crna Gora)",
42235 "Morocco (المغرب)",
42241 "Mozambique (Moçambique)",
42246 "Myanmar (Burma) (မြန်မာ)",
42251 "Namibia (Namibië)",
42266 "Netherlands (Nederland)",
42271 "New Caledonia (Nouvelle-Calédonie)",
42306 "North Korea (조선 민주주의 인민 공화국)",
42311 "Northern Mariana Islands",
42327 "Pakistan (پاکستان)",
42337 "Palestine (فلسطين)",
42347 "Papua New Guinea",
42389 "Réunion (La Réunion)",
42395 "Romania (România)",
42411 "Saint Barthélemy",
42422 "Saint Kitts and Nevis",
42432 "Saint Martin (Saint-Martin (partie française))",
42438 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42443 "Saint Vincent and the Grenadines",
42458 "São Tomé and Príncipe (São Tomé e Príncipe)",
42463 "Saudi Arabia (المملكة العربية السعودية)",
42468 "Senegal (Sénégal)",
42498 "Slovakia (Slovensko)",
42503 "Slovenia (Slovenija)",
42513 "Somalia (Soomaaliya)",
42523 "South Korea (대한민국)",
42528 "South Sudan (جنوب السودان)",
42538 "Sri Lanka (ශ්රී ලංකාව)",
42543 "Sudan (السودان)",
42553 "Svalbard and Jan Mayen",
42564 "Sweden (Sverige)",
42569 "Switzerland (Schweiz)",
42574 "Syria (سوريا)",
42619 "Trinidad and Tobago",
42624 "Tunisia (تونس)",
42629 "Turkey (Türkiye)",
42639 "Turks and Caicos Islands",
42649 "U.S. Virgin Islands",
42659 "Ukraine (Україна)",
42664 "United Arab Emirates (الإمارات العربية المتحدة)",
42686 "Uzbekistan (Oʻzbekiston)",
42696 "Vatican City (Città del Vaticano)",
42707 "Vietnam (Việt Nam)",
42712 "Wallis and Futuna (Wallis-et-Futuna)",
42717 "Western Sahara (الصحراء الغربية)",
42723 "Yemen (اليمن)",
42747 * This script refer to:
42748 * Title: International Telephone Input
42749 * Author: Jack O'Connor
42750 * Code version: v12.1.12
42751 * Availability: https://github.com/jackocnr/intl-tel-input.git
42755 * @class Roo.bootstrap.PhoneInput
42756 * @extends Roo.bootstrap.TriggerField
42757 * An input with International dial-code selection
42759 * @cfg {String} defaultDialCode default '+852'
42760 * @cfg {Array} preferedCountries default []
42763 * Create a new PhoneInput.
42764 * @param {Object} config Configuration options
42767 Roo.bootstrap.PhoneInput = function(config) {
42768 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42771 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42773 listWidth: undefined,
42775 selectedClass: 'active',
42777 invalidClass : "has-warning",
42779 validClass: 'has-success',
42781 allowed: '0123456789',
42786 * @cfg {String} defaultDialCode The default dial code when initializing the input
42788 defaultDialCode: '+852',
42791 * @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
42793 preferedCountries: false,
42795 getAutoCreate : function()
42797 var data = Roo.bootstrap.PhoneInputData();
42798 var align = this.labelAlign || this.parentLabelAlign();
42801 this.allCountries = [];
42802 this.dialCodeMapping = [];
42804 for (var i = 0; i < data.length; i++) {
42806 this.allCountries[i] = {
42810 priority: c[3] || 0,
42811 areaCodes: c[4] || null
42813 this.dialCodeMapping[c[2]] = {
42816 priority: c[3] || 0,
42817 areaCodes: c[4] || null
42829 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42830 maxlength: this.max_length,
42831 cls : 'form-control tel-input',
42832 autocomplete: 'new-password'
42835 var hiddenInput = {
42838 cls: 'hidden-tel-input'
42842 hiddenInput.name = this.name;
42845 if (this.disabled) {
42846 input.disabled = true;
42849 var flag_container = {
42866 cls: this.hasFeedback ? 'has-feedback' : '',
42872 cls: 'dial-code-holder',
42879 cls: 'roo-select2-container input-group',
42886 if (this.fieldLabel.length) {
42889 tooltip: 'This field is required'
42895 cls: 'control-label',
42901 html: this.fieldLabel
42904 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42910 if(this.indicatorpos == 'right') {
42911 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42918 if(align == 'left') {
42926 if(this.labelWidth > 12){
42927 label.style = "width: " + this.labelWidth + 'px';
42929 if(this.labelWidth < 13 && this.labelmd == 0){
42930 this.labelmd = this.labelWidth;
42932 if(this.labellg > 0){
42933 label.cls += ' col-lg-' + this.labellg;
42934 input.cls += ' col-lg-' + (12 - this.labellg);
42936 if(this.labelmd > 0){
42937 label.cls += ' col-md-' + this.labelmd;
42938 container.cls += ' col-md-' + (12 - this.labelmd);
42940 if(this.labelsm > 0){
42941 label.cls += ' col-sm-' + this.labelsm;
42942 container.cls += ' col-sm-' + (12 - this.labelsm);
42944 if(this.labelxs > 0){
42945 label.cls += ' col-xs-' + this.labelxs;
42946 container.cls += ' col-xs-' + (12 - this.labelxs);
42956 var settings = this;
42958 ['xs','sm','md','lg'].map(function(size){
42959 if (settings[size]) {
42960 cfg.cls += ' col-' + size + '-' + settings[size];
42964 this.store = new Roo.data.Store({
42965 proxy : new Roo.data.MemoryProxy({}),
42966 reader : new Roo.data.JsonReader({
42977 'name' : 'dialCode',
42981 'name' : 'priority',
42985 'name' : 'areaCodes',
42992 if(!this.preferedCountries) {
42993 this.preferedCountries = [
43000 var p = this.preferedCountries.reverse();
43003 for (var i = 0; i < p.length; i++) {
43004 for (var j = 0; j < this.allCountries.length; j++) {
43005 if(this.allCountries[j].iso2 == p[i]) {
43006 var t = this.allCountries[j];
43007 this.allCountries.splice(j,1);
43008 this.allCountries.unshift(t);
43014 this.store.proxy.data = {
43016 data: this.allCountries
43022 initEvents : function()
43025 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43027 this.indicator = this.indicatorEl();
43028 this.flag = this.flagEl();
43029 this.dialCodeHolder = this.dialCodeHolderEl();
43031 this.trigger = this.el.select('div.flag-box',true).first();
43032 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43037 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43038 _this.list.setWidth(lw);
43041 this.list.on('mouseover', this.onViewOver, this);
43042 this.list.on('mousemove', this.onViewMove, this);
43043 this.inputEl().on("keyup", this.onKeyUp, this);
43044 this.inputEl().on("keypress", this.onKeyPress, this);
43046 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43048 this.view = new Roo.View(this.list, this.tpl, {
43049 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43052 this.view.on('click', this.onViewClick, this);
43053 this.setValue(this.defaultDialCode);
43056 onTriggerClick : function(e)
43058 Roo.log('trigger click');
43063 if(this.isExpanded()){
43065 this.hasFocus = false;
43067 this.store.load({});
43068 this.hasFocus = true;
43073 isExpanded : function()
43075 return this.list.isVisible();
43078 collapse : function()
43080 if(!this.isExpanded()){
43084 Roo.get(document).un('mousedown', this.collapseIf, this);
43085 Roo.get(document).un('mousewheel', this.collapseIf, this);
43086 this.fireEvent('collapse', this);
43090 expand : function()
43094 if(this.isExpanded() || !this.hasFocus){
43098 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43099 this.list.setWidth(lw);
43102 this.restrictHeight();
43104 Roo.get(document).on('mousedown', this.collapseIf, this);
43105 Roo.get(document).on('mousewheel', this.collapseIf, this);
43107 this.fireEvent('expand', this);
43110 restrictHeight : function()
43112 this.list.alignTo(this.inputEl(), this.listAlign);
43113 this.list.alignTo(this.inputEl(), this.listAlign);
43116 onViewOver : function(e, t)
43118 if(this.inKeyMode){
43121 var item = this.view.findItemFromChild(t);
43124 var index = this.view.indexOf(item);
43125 this.select(index, false);
43130 onViewClick : function(view, doFocus, el, e)
43132 var index = this.view.getSelectedIndexes()[0];
43134 var r = this.store.getAt(index);
43137 this.onSelect(r, index);
43139 if(doFocus !== false && !this.blockFocus){
43140 this.inputEl().focus();
43144 onViewMove : function(e, t)
43146 this.inKeyMode = false;
43149 select : function(index, scrollIntoView)
43151 this.selectedIndex = index;
43152 this.view.select(index);
43153 if(scrollIntoView !== false){
43154 var el = this.view.getNode(index);
43156 this.list.scrollChildIntoView(el, false);
43161 createList : function()
43163 this.list = Roo.get(document.body).createChild({
43165 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43166 style: 'display:none'
43169 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43172 collapseIf : function(e)
43174 var in_combo = e.within(this.el);
43175 var in_list = e.within(this.list);
43176 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43178 if (in_combo || in_list || is_list) {
43184 onSelect : function(record, index)
43186 if(this.fireEvent('beforeselect', this, record, index) !== false){
43188 this.setFlagClass(record.data.iso2);
43189 this.setDialCode(record.data.dialCode);
43190 this.hasFocus = false;
43192 this.fireEvent('select', this, record, index);
43196 flagEl : function()
43198 var flag = this.el.select('div.flag',true).first();
43205 dialCodeHolderEl : function()
43207 var d = this.el.select('input.dial-code-holder',true).first();
43214 setDialCode : function(v)
43216 this.dialCodeHolder.dom.value = '+'+v;
43219 setFlagClass : function(n)
43221 this.flag.dom.className = 'flag '+n;
43224 getValue : function()
43226 var v = this.inputEl().getValue();
43227 if(this.dialCodeHolder) {
43228 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43233 setValue : function(v)
43235 var d = this.getDialCode(v);
43237 //invalid dial code
43238 if(v.length == 0 || !d || d.length == 0) {
43240 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43241 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43247 this.setFlagClass(this.dialCodeMapping[d].iso2);
43248 this.setDialCode(d);
43249 this.inputEl().dom.value = v.replace('+'+d,'');
43250 this.hiddenEl().dom.value = this.getValue();
43255 getDialCode : function(v)
43259 if (v.length == 0) {
43260 return this.dialCodeHolder.dom.value;
43264 if (v.charAt(0) != "+") {
43267 var numericChars = "";
43268 for (var i = 1; i < v.length; i++) {
43269 var c = v.charAt(i);
43272 if (this.dialCodeMapping[numericChars]) {
43273 dialCode = v.substr(1, i);
43275 if (numericChars.length == 4) {
43285 this.setValue(this.defaultDialCode);
43289 hiddenEl : function()
43291 return this.el.select('input.hidden-tel-input',true).first();
43294 // after setting val
43295 onKeyUp : function(e){
43296 this.setValue(this.getValue());
43299 onKeyPress : function(e){
43300 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43307 * @class Roo.bootstrap.MoneyField
43308 * @extends Roo.bootstrap.ComboBox
43309 * Bootstrap MoneyField class
43312 * Create a new MoneyField.
43313 * @param {Object} config Configuration options
43316 Roo.bootstrap.MoneyField = function(config) {
43318 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43322 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43325 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43327 allowDecimals : true,
43329 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43331 decimalSeparator : ".",
43333 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43335 decimalPrecision : 0,
43337 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43339 allowNegative : true,
43341 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43345 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43347 minValue : Number.NEGATIVE_INFINITY,
43349 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43351 maxValue : Number.MAX_VALUE,
43353 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43355 minText : "The minimum value for this field is {0}",
43357 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43359 maxText : "The maximum value for this field is {0}",
43361 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43362 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43364 nanText : "{0} is not a valid number",
43366 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43370 * @cfg {String} defaults currency of the MoneyField
43371 * value should be in lkey
43373 defaultCurrency : false,
43375 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43377 thousandsDelimiter : false,
43379 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43390 getAutoCreate : function()
43392 var align = this.labelAlign || this.parentLabelAlign();
43404 cls : 'form-control roo-money-amount-input',
43405 autocomplete: 'new-password'
43408 var hiddenInput = {
43412 cls: 'hidden-number-input'
43415 if(this.max_length) {
43416 input.maxlength = this.max_length;
43420 hiddenInput.name = this.name;
43423 if (this.disabled) {
43424 input.disabled = true;
43427 var clg = 12 - this.inputlg;
43428 var cmd = 12 - this.inputmd;
43429 var csm = 12 - this.inputsm;
43430 var cxs = 12 - this.inputxs;
43434 cls : 'row roo-money-field',
43438 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43442 cls: 'roo-select2-container input-group',
43446 cls : 'form-control roo-money-currency-input',
43447 autocomplete: 'new-password',
43449 name : this.currencyName
43453 cls : 'input-group-addon',
43467 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43471 cls: this.hasFeedback ? 'has-feedback' : '',
43482 if (this.fieldLabel.length) {
43485 tooltip: 'This field is required'
43491 cls: 'control-label',
43497 html: this.fieldLabel
43500 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43506 if(this.indicatorpos == 'right') {
43507 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43514 if(align == 'left') {
43522 if(this.labelWidth > 12){
43523 label.style = "width: " + this.labelWidth + 'px';
43525 if(this.labelWidth < 13 && this.labelmd == 0){
43526 this.labelmd = this.labelWidth;
43528 if(this.labellg > 0){
43529 label.cls += ' col-lg-' + this.labellg;
43530 input.cls += ' col-lg-' + (12 - this.labellg);
43532 if(this.labelmd > 0){
43533 label.cls += ' col-md-' + this.labelmd;
43534 container.cls += ' col-md-' + (12 - this.labelmd);
43536 if(this.labelsm > 0){
43537 label.cls += ' col-sm-' + this.labelsm;
43538 container.cls += ' col-sm-' + (12 - this.labelsm);
43540 if(this.labelxs > 0){
43541 label.cls += ' col-xs-' + this.labelxs;
43542 container.cls += ' col-xs-' + (12 - this.labelxs);
43553 var settings = this;
43555 ['xs','sm','md','lg'].map(function(size){
43556 if (settings[size]) {
43557 cfg.cls += ' col-' + size + '-' + settings[size];
43564 initEvents : function()
43566 this.indicator = this.indicatorEl();
43568 this.initCurrencyEvent();
43570 this.initNumberEvent();
43573 initCurrencyEvent : function()
43576 throw "can not find store for combo";
43579 this.store = Roo.factory(this.store, Roo.data);
43580 this.store.parent = this;
43584 this.triggerEl = this.el.select('.input-group-addon', true).first();
43586 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43591 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43592 _this.list.setWidth(lw);
43595 this.list.on('mouseover', this.onViewOver, this);
43596 this.list.on('mousemove', this.onViewMove, this);
43597 this.list.on('scroll', this.onViewScroll, this);
43600 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43603 this.view = new Roo.View(this.list, this.tpl, {
43604 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43607 this.view.on('click', this.onViewClick, this);
43609 this.store.on('beforeload', this.onBeforeLoad, this);
43610 this.store.on('load', this.onLoad, this);
43611 this.store.on('loadexception', this.onLoadException, this);
43613 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43614 "up" : function(e){
43615 this.inKeyMode = true;
43619 "down" : function(e){
43620 if(!this.isExpanded()){
43621 this.onTriggerClick();
43623 this.inKeyMode = true;
43628 "enter" : function(e){
43631 if(this.fireEvent("specialkey", this, e)){
43632 this.onViewClick(false);
43638 "esc" : function(e){
43642 "tab" : function(e){
43645 if(this.fireEvent("specialkey", this, e)){
43646 this.onViewClick(false);
43654 doRelay : function(foo, bar, hname){
43655 if(hname == 'down' || this.scope.isExpanded()){
43656 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43664 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43668 initNumberEvent : function(e)
43670 this.inputEl().on("keydown" , this.fireKey, this);
43671 this.inputEl().on("focus", this.onFocus, this);
43672 this.inputEl().on("blur", this.onBlur, this);
43674 this.inputEl().relayEvent('keyup', this);
43676 if(this.indicator){
43677 this.indicator.addClass('invisible');
43680 this.originalValue = this.getValue();
43682 if(this.validationEvent == 'keyup'){
43683 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43684 this.inputEl().on('keyup', this.filterValidation, this);
43686 else if(this.validationEvent !== false){
43687 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43690 if(this.selectOnFocus){
43691 this.on("focus", this.preFocus, this);
43694 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43695 this.inputEl().on("keypress", this.filterKeys, this);
43697 this.inputEl().relayEvent('keypress', this);
43700 var allowed = "0123456789";
43702 if(this.allowDecimals){
43703 allowed += this.decimalSeparator;
43706 if(this.allowNegative){
43710 if(this.thousandsDelimiter) {
43714 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43716 var keyPress = function(e){
43718 var k = e.getKey();
43720 var c = e.getCharCode();
43723 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43724 allowed.indexOf(String.fromCharCode(c)) === -1
43730 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43734 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43739 this.inputEl().on("keypress", keyPress, this);
43743 onTriggerClick : function(e)
43750 this.loadNext = false;
43752 if(this.isExpanded()){
43757 this.hasFocus = true;
43759 if(this.triggerAction == 'all') {
43760 this.doQuery(this.allQuery, true);
43764 this.doQuery(this.getRawValue());
43767 getCurrency : function()
43769 var v = this.currencyEl().getValue();
43774 restrictHeight : function()
43776 this.list.alignTo(this.currencyEl(), this.listAlign);
43777 this.list.alignTo(this.currencyEl(), this.listAlign);
43780 onViewClick : function(view, doFocus, el, e)
43782 var index = this.view.getSelectedIndexes()[0];
43784 var r = this.store.getAt(index);
43787 this.onSelect(r, index);
43791 onSelect : function(record, index){
43793 if(this.fireEvent('beforeselect', this, record, index) !== false){
43795 this.setFromCurrencyData(index > -1 ? record.data : false);
43799 this.fireEvent('select', this, record, index);
43803 setFromCurrencyData : function(o)
43807 this.lastCurrency = o;
43809 if (this.currencyField) {
43810 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43812 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43815 this.lastSelectionText = currency;
43817 //setting default currency
43818 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43819 this.setCurrency(this.defaultCurrency);
43823 this.setCurrency(currency);
43826 setFromData : function(o)
43830 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43832 this.setFromCurrencyData(c);
43837 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43839 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43842 this.setValue(value);
43846 setCurrency : function(v)
43848 this.currencyValue = v;
43851 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43856 setValue : function(v)
43858 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43864 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43866 this.inputEl().dom.value = (v == '') ? '' :
43867 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43869 if(!this.allowZero && v === '0') {
43870 this.hiddenEl().dom.value = '';
43871 this.inputEl().dom.value = '';
43878 getRawValue : function()
43880 var v = this.inputEl().getValue();
43885 getValue : function()
43887 return this.fixPrecision(this.parseValue(this.getRawValue()));
43890 parseValue : function(value)
43892 if(this.thousandsDelimiter) {
43894 r = new RegExp(",", "g");
43895 value = value.replace(r, "");
43898 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43899 return isNaN(value) ? '' : value;
43903 fixPrecision : function(value)
43905 if(this.thousandsDelimiter) {
43907 r = new RegExp(",", "g");
43908 value = value.replace(r, "");
43911 var nan = isNaN(value);
43913 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43914 return nan ? '' : value;
43916 return parseFloat(value).toFixed(this.decimalPrecision);
43919 decimalPrecisionFcn : function(v)
43921 return Math.floor(v);
43924 validateValue : function(value)
43926 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43930 var num = this.parseValue(value);
43933 this.markInvalid(String.format(this.nanText, value));
43937 if(num < this.minValue){
43938 this.markInvalid(String.format(this.minText, this.minValue));
43942 if(num > this.maxValue){
43943 this.markInvalid(String.format(this.maxText, this.maxValue));
43950 validate : function()
43952 if(this.disabled || this.allowBlank){
43957 var currency = this.getCurrency();
43959 if(this.validateValue(this.getRawValue()) && currency.length){
43964 this.markInvalid();
43968 getName: function()
43973 beforeBlur : function()
43979 var v = this.parseValue(this.getRawValue());
43986 onBlur : function()
43990 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43991 //this.el.removeClass(this.focusClass);
43994 this.hasFocus = false;
43996 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44000 var v = this.getValue();
44002 if(String(v) !== String(this.startValue)){
44003 this.fireEvent('change', this, v, this.startValue);
44006 this.fireEvent("blur", this);
44009 inputEl : function()
44011 return this.el.select('.roo-money-amount-input', true).first();
44014 currencyEl : function()
44016 return this.el.select('.roo-money-currency-input', true).first();
44019 hiddenEl : function()
44021 return this.el.select('input.hidden-number-input',true).first();
44025 * @class Roo.bootstrap.BezierSignature
44026 * @extends Roo.bootstrap.Component
44027 * Bootstrap BezierSignature class
44028 * This script refer to:
44029 * Title: Signature Pad
44031 * Availability: https://github.com/szimek/signature_pad
44034 * Create a new BezierSignature
44035 * @param {Object} config The config object
44038 Roo.bootstrap.BezierSignature = function(config){
44039 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44045 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44052 mouse_btn_down: true,
44055 * @cfg {int} canvas height
44057 canvas_height: '200px',
44060 * @cfg {float|function} Radius of a single dot.
44065 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44070 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44075 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44080 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44085 * @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.
44087 bg_color: 'rgba(0, 0, 0, 0)',
44090 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44092 dot_color: 'black',
44095 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44097 velocity_filter_weight: 0.7,
44100 * @cfg {function} Callback when stroke begin.
44105 * @cfg {function} Callback when stroke end.
44109 getAutoCreate : function()
44111 var cls = 'roo-signature column';
44114 cls += ' ' + this.cls;
44124 for(var i = 0; i < col_sizes.length; i++) {
44125 if(this[col_sizes[i]]) {
44126 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44136 cls: 'roo-signature-body',
44140 cls: 'roo-signature-body-canvas',
44141 height: this.canvas_height,
44142 width: this.canvas_width
44149 style: 'display: none'
44157 initEvents: function()
44159 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44161 var canvas = this.canvasEl();
44163 // mouse && touch event swapping...
44164 canvas.dom.style.touchAction = 'none';
44165 canvas.dom.style.msTouchAction = 'none';
44167 this.mouse_btn_down = false;
44168 canvas.on('mousedown', this._handleMouseDown, this);
44169 canvas.on('mousemove', this._handleMouseMove, this);
44170 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44172 if (window.PointerEvent) {
44173 canvas.on('pointerdown', this._handleMouseDown, this);
44174 canvas.on('pointermove', this._handleMouseMove, this);
44175 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44178 if ('ontouchstart' in window) {
44179 canvas.on('touchstart', this._handleTouchStart, this);
44180 canvas.on('touchmove', this._handleTouchMove, this);
44181 canvas.on('touchend', this._handleTouchEnd, this);
44184 Roo.EventManager.onWindowResize(this.resize, this, true);
44186 // file input event
44187 this.fileEl().on('change', this.uploadImage, this);
44194 resize: function(){
44196 var canvas = this.canvasEl().dom;
44197 var ctx = this.canvasElCtx();
44198 var img_data = false;
44200 if(canvas.width > 0) {
44201 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44203 // setting canvas width will clean img data
44206 var style = window.getComputedStyle ?
44207 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44209 var padding_left = parseInt(style.paddingLeft) || 0;
44210 var padding_right = parseInt(style.paddingRight) || 0;
44212 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44215 ctx.putImageData(img_data, 0, 0);
44219 _handleMouseDown: function(e)
44221 if (e.browserEvent.which === 1) {
44222 this.mouse_btn_down = true;
44223 this.strokeBegin(e);
44227 _handleMouseMove: function (e)
44229 if (this.mouse_btn_down) {
44230 this.strokeMoveUpdate(e);
44234 _handleMouseUp: function (e)
44236 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44237 this.mouse_btn_down = false;
44242 _handleTouchStart: function (e) {
44244 e.preventDefault();
44245 if (e.browserEvent.targetTouches.length === 1) {
44246 // var touch = e.browserEvent.changedTouches[0];
44247 // this.strokeBegin(touch);
44249 this.strokeBegin(e); // assume e catching the correct xy...
44253 _handleTouchMove: function (e) {
44254 e.preventDefault();
44255 // var touch = event.targetTouches[0];
44256 // _this._strokeMoveUpdate(touch);
44257 this.strokeMoveUpdate(e);
44260 _handleTouchEnd: function (e) {
44261 var wasCanvasTouched = e.target === this.canvasEl().dom;
44262 if (wasCanvasTouched) {
44263 e.preventDefault();
44264 // var touch = event.changedTouches[0];
44265 // _this._strokeEnd(touch);
44270 reset: function () {
44271 this._lastPoints = [];
44272 this._lastVelocity = 0;
44273 this._lastWidth = (this.min_width + this.max_width) / 2;
44274 this.canvasElCtx().fillStyle = this.dot_color;
44277 strokeMoveUpdate: function(e)
44279 this.strokeUpdate(e);
44281 if (this.throttle) {
44282 this.throttleStroke(this.strokeUpdate, this.throttle);
44285 this.strokeUpdate(e);
44289 strokeBegin: function(e)
44291 var newPointGroup = {
44292 color: this.dot_color,
44296 if (typeof this.onBegin === 'function') {
44300 this.curve_data.push(newPointGroup);
44302 this.strokeUpdate(e);
44305 strokeUpdate: function(e)
44307 var rect = this.canvasEl().dom.getBoundingClientRect();
44308 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44309 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44310 var lastPoints = lastPointGroup.points;
44311 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44312 var isLastPointTooClose = lastPoint
44313 ? point.distanceTo(lastPoint) <= this.min_distance
44315 var color = lastPointGroup.color;
44316 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44317 var curve = this.addPoint(point);
44319 this.drawDot({color: color, point: point});
44322 this.drawCurve({color: color, curve: curve});
44332 strokeEnd: function(e)
44334 this.strokeUpdate(e);
44335 if (typeof this.onEnd === 'function') {
44340 addPoint: function (point) {
44341 var _lastPoints = this._lastPoints;
44342 _lastPoints.push(point);
44343 if (_lastPoints.length > 2) {
44344 if (_lastPoints.length === 3) {
44345 _lastPoints.unshift(_lastPoints[0]);
44347 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44348 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44349 _lastPoints.shift();
44355 calculateCurveWidths: function (startPoint, endPoint) {
44356 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44357 (1 - this.velocity_filter_weight) * this._lastVelocity;
44359 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44362 start: this._lastWidth
44365 this._lastVelocity = velocity;
44366 this._lastWidth = newWidth;
44370 drawDot: function (_a) {
44371 var color = _a.color, point = _a.point;
44372 var ctx = this.canvasElCtx();
44373 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44375 this.drawCurveSegment(point.x, point.y, width);
44377 ctx.fillStyle = color;
44381 drawCurve: function (_a) {
44382 var color = _a.color, curve = _a.curve;
44383 var ctx = this.canvasElCtx();
44384 var widthDelta = curve.endWidth - curve.startWidth;
44385 var drawSteps = Math.floor(curve.length()) * 2;
44387 ctx.fillStyle = color;
44388 for (var i = 0; i < drawSteps; i += 1) {
44389 var t = i / drawSteps;
44395 var x = uuu * curve.startPoint.x;
44396 x += 3 * uu * t * curve.control1.x;
44397 x += 3 * u * tt * curve.control2.x;
44398 x += ttt * curve.endPoint.x;
44399 var y = uuu * curve.startPoint.y;
44400 y += 3 * uu * t * curve.control1.y;
44401 y += 3 * u * tt * curve.control2.y;
44402 y += ttt * curve.endPoint.y;
44403 var width = curve.startWidth + ttt * widthDelta;
44404 this.drawCurveSegment(x, y, width);
44410 drawCurveSegment: function (x, y, width) {
44411 var ctx = this.canvasElCtx();
44413 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44414 this.is_empty = false;
44419 var ctx = this.canvasElCtx();
44420 var canvas = this.canvasEl().dom;
44421 ctx.fillStyle = this.bg_color;
44422 ctx.clearRect(0, 0, canvas.width, canvas.height);
44423 ctx.fillRect(0, 0, canvas.width, canvas.height);
44424 this.curve_data = [];
44426 this.is_empty = true;
44431 return this.el.select('input',true).first();
44434 canvasEl: function()
44436 return this.el.select('canvas',true).first();
44439 canvasElCtx: function()
44441 return this.el.select('canvas',true).first().dom.getContext('2d');
44444 getImage: function(type)
44446 if(this.is_empty) {
44451 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44454 drawFromImage: function(img_src)
44456 var img = new Image();
44458 img.onload = function(){
44459 this.canvasElCtx().drawImage(img, 0, 0);
44464 this.is_empty = false;
44467 selectImage: function()
44469 this.fileEl().dom.click();
44472 uploadImage: function(e)
44474 var reader = new FileReader();
44476 reader.onload = function(e){
44477 var img = new Image();
44478 img.onload = function(){
44480 this.canvasElCtx().drawImage(img, 0, 0);
44482 img.src = e.target.result;
44485 reader.readAsDataURL(e.target.files[0]);
44488 // Bezier Point Constructor
44489 Point: (function () {
44490 function Point(x, y, time) {
44493 this.time = time || Date.now();
44495 Point.prototype.distanceTo = function (start) {
44496 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44498 Point.prototype.equals = function (other) {
44499 return this.x === other.x && this.y === other.y && this.time === other.time;
44501 Point.prototype.velocityFrom = function (start) {
44502 return this.time !== start.time
44503 ? this.distanceTo(start) / (this.time - start.time)
44510 // Bezier Constructor
44511 Bezier: (function () {
44512 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44513 this.startPoint = startPoint;
44514 this.control2 = control2;
44515 this.control1 = control1;
44516 this.endPoint = endPoint;
44517 this.startWidth = startWidth;
44518 this.endWidth = endWidth;
44520 Bezier.fromPoints = function (points, widths, scope) {
44521 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44522 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44523 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44525 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44526 var dx1 = s1.x - s2.x;
44527 var dy1 = s1.y - s2.y;
44528 var dx2 = s2.x - s3.x;
44529 var dy2 = s2.y - s3.y;
44530 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44531 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44532 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44533 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44534 var dxm = m1.x - m2.x;
44535 var dym = m1.y - m2.y;
44536 var k = l2 / (l1 + l2);
44537 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44538 var tx = s2.x - cm.x;
44539 var ty = s2.y - cm.y;
44541 c1: new scope.Point(m1.x + tx, m1.y + ty),
44542 c2: new scope.Point(m2.x + tx, m2.y + ty)
44545 Bezier.prototype.length = function () {
44550 for (var i = 0; i <= steps; i += 1) {
44552 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44553 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44555 var xdiff = cx - px;
44556 var ydiff = cy - py;
44557 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44564 Bezier.prototype.point = function (t, start, c1, c2, end) {
44565 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44566 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44567 + (3.0 * c2 * (1.0 - t) * t * t)
44568 + (end * t * t * t);
44573 throttleStroke: function(fn, wait) {
44574 if (wait === void 0) { wait = 250; }
44576 var timeout = null;
44580 var later = function () {
44581 previous = Date.now();
44583 result = fn.apply(storedContext, storedArgs);
44585 storedContext = null;
44589 return function wrapper() {
44591 for (var _i = 0; _i < arguments.length; _i++) {
44592 args[_i] = arguments[_i];
44594 var now = Date.now();
44595 var remaining = wait - (now - previous);
44596 storedContext = this;
44598 if (remaining <= 0 || remaining > wait) {
44600 clearTimeout(timeout);
44604 result = fn.apply(storedContext, storedArgs);
44606 storedContext = null;
44610 else if (!timeout) {
44611 timeout = window.setTimeout(later, remaining);