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,
2098 layoutCls : function()
2102 Roo.log(this.margin_bottom.length);
2103 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2104 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2106 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2107 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2109 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2110 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2114 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2115 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2116 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2120 // more generic support?
2128 // Roo.log("Call onRender: " + this.xtype);
2129 /* We are looking at something like this.
2131 <img src="..." class="card-img-top" alt="...">
2132 <div class="card-body">
2133 <h5 class="card-title">Card title</h5>
2134 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2136 >> this bit is really the body...
2137 <div> << we will ad dthis in hopefully it will not break shit.
2139 ** card text does not actually have any styling...
2141 <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>
2144 <a href="#" class="card-link">Card link</a>
2147 <div class="card-footer">
2148 <small class="text-muted">Last updated 3 mins ago</small>
2152 getAutoCreate : function(){
2160 if (this.weight.length && this.weight != 'light') {
2161 cfg.cls += ' text-white';
2163 cfg.cls += ' text-dark'; // need as it's nested..
2165 if (this.weight.length) {
2166 cfg.cls += ' bg-' + this.weight;
2169 cfg.cls += ' ' + this.layoutCls();
2172 var hdr_ctr = false;
2173 if (this.header.length) {
2175 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2176 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2184 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2190 if (this.collapsable) {
2193 cls : 'd-block user-select-none',
2197 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2202 hdr.cn.push(hdr_ctr);
2207 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2212 if (this.header_image.length) {
2215 cls : 'card-img-top',
2216 src: this.header_image // escape?
2221 cls : 'card-img-top d-none'
2227 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2231 if (this.collapsable || this.rotateable) {
2234 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2241 if (this.title.length) {
2245 src: this.title // escape?
2249 if (this.subtitle.length) {
2253 src: this.subtitle // escape?
2259 cls : 'roo-card-body-ctr'
2262 if (this.html.length) {
2268 // fixme ? handle objects?
2270 if (this.footer.length) {
2273 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2278 cfg.cn.push({cls : 'card-footer d-none'});
2287 getCardHeader : function()
2289 var ret = this.el.select('.card-header',true).first();
2290 if (ret.hasClass('d-none')) {
2291 ret.removeClass('d-none');
2296 getCardFooter : function()
2298 var ret = this.el.select('.card-footer',true).first();
2299 if (ret.hasClass('d-none')) {
2300 ret.removeClass('d-none');
2305 getCardImageTop : function()
2307 var ret = this.header_imageEl;
2308 if (ret.hasClass('d-none')) {
2309 ret.removeClass('d-none');
2315 getChildContainer : function()
2321 return this.el.select('.roo-card-body-ctr',true).first();
2324 initEvents: function()
2326 this.bodyEl = this.el.select('.card-body',true).first();
2327 this.containerEl = this.getChildContainer();
2329 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2330 containerScroll: true,
2331 ddGroup: this.drag_group || 'default_card_drag_group'
2333 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2335 if (this.dropable) {
2336 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2337 containerScroll: true,
2338 ddGroup: this.drop_group || 'default_card_drag_group'
2340 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2341 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2342 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2343 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2344 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2347 if (this.collapsable) {
2348 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2350 if (this.rotateable) {
2351 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2353 this.collapsableEl = this.el.select('.roo-collapsable').first();
2355 this.footerEl = this.el.select('.card-footer').first();
2356 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2357 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2358 this.headerEl = this.el.select('.card-header',true).first();
2361 this.el.addClass('roo-card-rotated');
2362 this.fireEvent('rotate', this, true);
2364 this.header_imageEl = this.el.select('.card-img-top',true).first();
2365 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2368 getDragData : function(e)
2370 var target = this.getEl();
2372 //this.handleSelection(e);
2377 nodes: this.getEl(),
2382 dragData.ddel = target.dom ; // the div element
2383 Roo.log(target.getWidth( ));
2384 dragData.ddel.style.width = target.getWidth() + 'px';
2391 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2392 * whole Element becomes the target, and this causes the drop gesture to append.
2394 * Returns an object:
2397 position : 'below' or 'above'
2398 card : relateive to card OBJECT (or true for no cards listed)
2399 items_n : relative to nth item in list
2400 card_n : relative to nth card in list
2405 getTargetFromEvent : function(e, dragged_card_el)
2407 var target = e.getTarget();
2408 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2409 target = target.parentNode;
2420 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2421 // see if target is one of the 'cards'...
2424 //Roo.log(this.items.length);
2427 var last_card_n = 0;
2429 for (var i = 0;i< this.items.length;i++) {
2431 if (!this.items[i].el.hasClass('card')) {
2434 pos = this.getDropPoint(e, this.items[i].el.dom);
2436 cards_len = ret.cards.length;
2437 //Roo.log(this.items[i].el.dom.id);
2438 ret.cards.push(this.items[i]);
2440 if (ret.card_n < 0 && pos == 'above') {
2441 ret.position = cards_len > 0 ? 'below' : pos;
2442 ret.items_n = i > 0 ? i - 1 : 0;
2443 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2444 ret.card = ret.cards[ret.card_n];
2447 if (!ret.cards.length) {
2449 ret.position = 'below';
2453 // could not find a card.. stick it at the end..
2454 if (ret.card_n < 0) {
2455 ret.card_n = last_card_n;
2456 ret.card = ret.cards[last_card_n];
2457 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2458 ret.position = 'below';
2461 if (this.items[ret.items_n].el == dragged_card_el) {
2465 if (ret.position == 'below') {
2466 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2468 if (card_after && card_after.el == dragged_card_el) {
2475 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2477 if (card_before && card_before.el == dragged_card_el) {
2484 onNodeEnter : function(n, dd, e, data){
2487 onNodeOver : function(n, dd, e, data)
2490 var target_info = this.getTargetFromEvent(e,data.source.el);
2491 if (target_info === false) {
2492 this.dropPlaceHolder('hide');
2495 Roo.log(['getTargetFromEvent', target_info ]);
2498 if (this.fireEvent('cardover', this, [ data ]) === false) {
2502 this.dropPlaceHolder('show', target_info,data);
2506 onNodeOut : function(n, dd, e, data){
2507 this.dropPlaceHolder('hide');
2510 onNodeDrop : function(n, dd, e, data)
2513 // call drop - return false if
2515 // this could actually fail - if the Network drops..
2516 // we will ignore this at present..- client should probably reload
2517 // the whole set of cards if stuff like that fails.
2520 var info = this.getTargetFromEvent(e,data.source.el);
2521 if (info === false) {
2524 this.dropPlaceHolder('hide');
2528 this.acceptCard(data.source, info.position, info.card, info.items_n);
2532 firstChildCard : function()
2534 for (var i = 0;i< this.items.length;i++) {
2536 if (!this.items[i].el.hasClass('card')) {
2539 return this.items[i];
2541 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2546 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2548 acceptCard : function(move_card, position, next_to_card )
2550 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2554 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2556 move_card.parent().removeCard(move_card);
2559 var dom = move_card.el.dom;
2560 dom.style.width = ''; // clear with - which is set by drag.
2562 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2563 var cardel = next_to_card.el.dom;
2565 if (position == 'above' ) {
2566 cardel.parentNode.insertBefore(dom, cardel);
2567 } else if (cardel.nextSibling) {
2568 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2570 cardel.parentNode.append(dom);
2573 // card container???
2574 this.containerEl.dom.append(dom);
2577 //FIXME HANDLE card = true
2579 // add this to the correct place in items.
2581 // remove Card from items.
2584 if (this.items.length) {
2586 //Roo.log([info.items_n, info.position, this.items.length]);
2587 for (var i =0; i < this.items.length; i++) {
2588 if (i == to_items_n && position == 'above') {
2589 nitems.push(move_card);
2591 nitems.push(this.items[i]);
2592 if (i == to_items_n && position == 'below') {
2593 nitems.push(move_card);
2596 this.items = nitems;
2597 Roo.log(this.items);
2599 this.items.push(move_card);
2602 move_card.parentId = this.id;
2608 removeCard : function(c)
2610 this.items = this.items.filter(function(e) { return e != c });
2613 dom.parentNode.removeChild(dom);
2614 dom.style.width = ''; // clear with - which is set by drag.
2619 /** Decide whether to drop above or below a View node. */
2620 getDropPoint : function(e, n, dd)
2625 if (n == this.containerEl.dom) {
2628 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2629 var c = t + (b - t) / 2;
2630 var y = Roo.lib.Event.getPageY(e);
2637 onToggleCollapse : function(e)
2639 if (this.collapsed) {
2640 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2641 this.collapsableEl.addClass('show');
2642 this.collapsed = false;
2645 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2646 this.collapsableEl.removeClass('show');
2647 this.collapsed = true;
2652 onToggleRotate : function(e)
2654 this.collapsableEl.removeClass('show');
2655 this.footerEl.removeClass('d-none');
2656 this.el.removeClass('roo-card-rotated');
2657 this.el.removeClass('d-none');
2660 this.collapsableEl.addClass('show');
2661 this.rotated = false;
2662 this.fireEvent('rotate', this, this.rotated);
2665 this.el.addClass('roo-card-rotated');
2666 this.footerEl.addClass('d-none');
2667 this.el.select('.roo-collapsable').removeClass('show');
2669 this.rotated = true;
2670 this.fireEvent('rotate', this, this.rotated);
2674 dropPlaceHolder: function (action, info, data)
2676 if (this.dropEl === false) {
2677 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2681 this.dropEl.removeClass(['d-none', 'd-block']);
2682 if (action == 'hide') {
2684 this.dropEl.addClass('d-none');
2687 // FIXME - info.card == true!!!
2688 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2690 if (info.card !== true) {
2691 var cardel = info.card.el.dom;
2693 if (info.position == 'above') {
2694 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2695 } else if (cardel.nextSibling) {
2696 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2698 cardel.parentNode.append(this.dropEl.dom);
2701 // card container???
2702 this.containerEl.dom.append(this.dropEl.dom);
2705 this.dropEl.addClass('d-block roo-card-dropzone');
2707 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2714 setHeaderText: function(html)
2717 if (this.headerContainerEl) {
2718 this.headerContainerEl.dom.innerHTML = html;
2721 onHeaderImageLoad : function(ev, he)
2723 if (!this.header_image_fit_square) {
2727 var hw = he.naturalHeight / he.naturalWidth;
2730 //var w = he.dom.naturalWidth;
2733 he.style.position = 'relative';
2735 var nw = (ww * (1/hw));
2736 Roo.get(he).setSize( ww * (1/hw), ww);
2737 he.style.left = ((ww - nw)/ 2) + 'px';
2738 he.style.position = 'relative';
2749 * Card header - holder for the card header elements.
2754 * @class Roo.bootstrap.CardHeader
2755 * @extends Roo.bootstrap.Element
2756 * Bootstrap CardHeader class
2758 * Create a new Card Header - that you can embed children into
2759 * @param {Object} config The config object
2762 Roo.bootstrap.CardHeader = function(config){
2763 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2766 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2769 container_method : 'getCardHeader'
2782 * Card footer - holder for the card footer elements.
2787 * @class Roo.bootstrap.CardFooter
2788 * @extends Roo.bootstrap.Element
2789 * Bootstrap CardFooter class
2791 * Create a new Card Footer - that you can embed children into
2792 * @param {Object} config The config object
2795 Roo.bootstrap.CardFooter = function(config){
2796 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2799 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2802 container_method : 'getCardFooter'
2815 * Card header - holder for the card header elements.
2820 * @class Roo.bootstrap.CardImageTop
2821 * @extends Roo.bootstrap.Element
2822 * Bootstrap CardImageTop class
2824 * Create a new Card Image Top container
2825 * @param {Object} config The config object
2828 Roo.bootstrap.CardImageTop = function(config){
2829 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2832 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2835 container_method : 'getCardImageTop'
2853 * @class Roo.bootstrap.Img
2854 * @extends Roo.bootstrap.Component
2855 * Bootstrap Img class
2856 * @cfg {Boolean} imgResponsive false | true
2857 * @cfg {String} border rounded | circle | thumbnail
2858 * @cfg {String} src image source
2859 * @cfg {String} alt image alternative text
2860 * @cfg {String} href a tag href
2861 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2862 * @cfg {String} xsUrl xs image source
2863 * @cfg {String} smUrl sm image source
2864 * @cfg {String} mdUrl md image source
2865 * @cfg {String} lgUrl lg image source
2868 * Create a new Input
2869 * @param {Object} config The config object
2872 Roo.bootstrap.Img = function(config){
2873 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2879 * The img click event for the img.
2880 * @param {Roo.EventObject} e
2886 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2888 imgResponsive: true,
2898 getAutoCreate : function()
2900 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2901 return this.createSingleImg();
2906 cls: 'roo-image-responsive-group',
2911 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2913 if(!_this[size + 'Url']){
2919 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2920 html: _this.html || cfg.html,
2921 src: _this[size + 'Url']
2924 img.cls += ' roo-image-responsive-' + size;
2926 var s = ['xs', 'sm', 'md', 'lg'];
2928 s.splice(s.indexOf(size), 1);
2930 Roo.each(s, function(ss){
2931 img.cls += ' hidden-' + ss;
2934 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2935 cfg.cls += ' img-' + _this.border;
2939 cfg.alt = _this.alt;
2952 a.target = _this.target;
2956 cfg.cn.push((_this.href) ? a : img);
2963 createSingleImg : function()
2967 cls: (this.imgResponsive) ? 'img-responsive' : '',
2969 src : 'about:blank' // just incase src get's set to undefined?!?
2972 cfg.html = this.html || cfg.html;
2974 cfg.src = this.src || cfg.src;
2976 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2977 cfg.cls += ' img-' + this.border;
2994 a.target = this.target;
2999 return (this.href) ? a : cfg;
3002 initEvents: function()
3005 this.el.on('click', this.onClick, this);
3010 onClick : function(e)
3012 Roo.log('img onclick');
3013 this.fireEvent('click', this, e);
3016 * Sets the url of the image - used to update it
3017 * @param {String} url the url of the image
3020 setSrc : function(url)
3024 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3025 this.el.dom.src = url;
3029 this.el.select('img', true).first().dom.src = url;
3045 * @class Roo.bootstrap.Link
3046 * @extends Roo.bootstrap.Component
3047 * Bootstrap Link Class
3048 * @cfg {String} alt image alternative text
3049 * @cfg {String} href a tag href
3050 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3051 * @cfg {String} html the content of the link.
3052 * @cfg {String} anchor name for the anchor link
3053 * @cfg {String} fa - favicon
3055 * @cfg {Boolean} preventDefault (true | false) default false
3059 * Create a new Input
3060 * @param {Object} config The config object
3063 Roo.bootstrap.Link = function(config){
3064 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3070 * The img click event for the img.
3071 * @param {Roo.EventObject} e
3077 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3081 preventDefault: false,
3087 getAutoCreate : function()
3089 var html = this.html || '';
3091 if (this.fa !== false) {
3092 html = '<i class="fa fa-' + this.fa + '"></i>';
3097 // anchor's do not require html/href...
3098 if (this.anchor === false) {
3100 cfg.href = this.href || '#';
3102 cfg.name = this.anchor;
3103 if (this.html !== false || this.fa !== false) {
3106 if (this.href !== false) {
3107 cfg.href = this.href;
3111 if(this.alt !== false){
3116 if(this.target !== false) {
3117 cfg.target = this.target;
3123 initEvents: function() {
3125 if(!this.href || this.preventDefault){
3126 this.el.on('click', this.onClick, this);
3130 onClick : function(e)
3132 if(this.preventDefault){
3135 //Roo.log('img onclick');
3136 this.fireEvent('click', this, e);
3149 * @class Roo.bootstrap.Header
3150 * @extends Roo.bootstrap.Component
3151 * Bootstrap Header class
3152 * @cfg {String} html content of header
3153 * @cfg {Number} level (1|2|3|4|5|6) default 1
3156 * Create a new Header
3157 * @param {Object} config The config object
3161 Roo.bootstrap.Header = function(config){
3162 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3165 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3173 getAutoCreate : function(){
3178 tag: 'h' + (1 *this.level),
3179 html: this.html || ''
3191 * Ext JS Library 1.1.1
3192 * Copyright(c) 2006-2007, Ext JS, LLC.
3194 * Originally Released Under LGPL - original licence link has changed is not relivant.
3197 * <script type="text/javascript">
3201 * @class Roo.bootstrap.MenuMgr
3202 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3205 Roo.bootstrap.MenuMgr = function(){
3206 var menus, active, groups = {}, attached = false, lastShow = new Date();
3208 // private - called when first menu is created
3211 active = new Roo.util.MixedCollection();
3212 Roo.get(document).addKeyListener(27, function(){
3213 if(active.length > 0){
3221 if(active && active.length > 0){
3222 var c = active.clone();
3232 if(active.length < 1){
3233 Roo.get(document).un("mouseup", onMouseDown);
3241 var last = active.last();
3242 lastShow = new Date();
3245 Roo.get(document).on("mouseup", onMouseDown);
3250 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3251 m.parentMenu.activeChild = m;
3252 }else if(last && last.isVisible()){
3253 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3258 function onBeforeHide(m){
3260 m.activeChild.hide();
3262 if(m.autoHideTimer){
3263 clearTimeout(m.autoHideTimer);
3264 delete m.autoHideTimer;
3269 function onBeforeShow(m){
3270 var pm = m.parentMenu;
3271 if(!pm && !m.allowOtherMenus){
3273 }else if(pm && pm.activeChild && active != m){
3274 pm.activeChild.hide();
3278 // private this should really trigger on mouseup..
3279 function onMouseDown(e){
3280 Roo.log("on Mouse Up");
3282 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3283 Roo.log("MenuManager hideAll");
3292 function onBeforeCheck(mi, state){
3294 var g = groups[mi.group];
3295 for(var i = 0, l = g.length; i < l; i++){
3297 g[i].setChecked(false);
3306 * Hides all menus that are currently visible
3308 hideAll : function(){
3313 register : function(menu){
3317 menus[menu.id] = menu;
3318 menu.on("beforehide", onBeforeHide);
3319 menu.on("hide", onHide);
3320 menu.on("beforeshow", onBeforeShow);
3321 menu.on("show", onShow);
3323 if(g && menu.events["checkchange"]){
3327 groups[g].push(menu);
3328 menu.on("checkchange", onCheck);
3333 * Returns a {@link Roo.menu.Menu} object
3334 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3335 * be used to generate and return a new Menu instance.
3337 get : function(menu){
3338 if(typeof menu == "string"){ // menu id
3340 }else if(menu.events){ // menu instance
3343 /*else if(typeof menu.length == 'number'){ // array of menu items?
3344 return new Roo.bootstrap.Menu({items:menu});
3345 }else{ // otherwise, must be a config
3346 return new Roo.bootstrap.Menu(menu);
3353 unregister : function(menu){
3354 delete menus[menu.id];
3355 menu.un("beforehide", onBeforeHide);
3356 menu.un("hide", onHide);
3357 menu.un("beforeshow", onBeforeShow);
3358 menu.un("show", onShow);
3360 if(g && menu.events["checkchange"]){
3361 groups[g].remove(menu);
3362 menu.un("checkchange", onCheck);
3367 registerCheckable : function(menuItem){
3368 var g = menuItem.group;
3373 groups[g].push(menuItem);
3374 menuItem.on("beforecheckchange", onBeforeCheck);
3379 unregisterCheckable : function(menuItem){
3380 var g = menuItem.group;
3382 groups[g].remove(menuItem);
3383 menuItem.un("beforecheckchange", onBeforeCheck);
3395 * @class Roo.bootstrap.Menu
3396 * @extends Roo.bootstrap.Component
3397 * Bootstrap Menu class - container for MenuItems
3398 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3399 * @cfg {bool} hidden if the menu should be hidden when rendered.
3400 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3401 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3405 * @param {Object} config The config object
3409 Roo.bootstrap.Menu = function(config){
3410 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3411 if (this.registerMenu && this.type != 'treeview') {
3412 Roo.bootstrap.MenuMgr.register(this);
3419 * Fires before this menu is displayed (return false to block)
3420 * @param {Roo.menu.Menu} this
3425 * Fires before this menu is hidden (return false to block)
3426 * @param {Roo.menu.Menu} this
3431 * Fires after this menu is displayed
3432 * @param {Roo.menu.Menu} this
3437 * Fires after this menu is hidden
3438 * @param {Roo.menu.Menu} this
3443 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3444 * @param {Roo.menu.Menu} this
3445 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3446 * @param {Roo.EventObject} e
3451 * Fires when the mouse is hovering over this menu
3452 * @param {Roo.menu.Menu} this
3453 * @param {Roo.EventObject} e
3454 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3459 * Fires when the mouse exits this menu
3460 * @param {Roo.menu.Menu} this
3461 * @param {Roo.EventObject} e
3462 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3467 * Fires when a menu item contained in this menu is clicked
3468 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3469 * @param {Roo.EventObject} e
3473 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3476 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3480 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3483 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3485 registerMenu : true,
3487 menuItems :false, // stores the menu items..
3497 getChildContainer : function() {
3501 getAutoCreate : function(){
3503 //if (['right'].indexOf(this.align)!==-1) {
3504 // cfg.cn[1].cls += ' pull-right'
3510 cls : 'dropdown-menu' ,
3511 style : 'z-index:1000'
3515 if (this.type === 'submenu') {
3516 cfg.cls = 'submenu active';
3518 if (this.type === 'treeview') {
3519 cfg.cls = 'treeview-menu';
3524 initEvents : function() {
3526 // Roo.log("ADD event");
3527 // Roo.log(this.triggerEl.dom);
3529 this.triggerEl.on('click', this.onTriggerClick, this);
3531 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3534 if (this.triggerEl.hasClass('nav-item')) {
3535 // dropdown toggle on the 'a' in BS4?
3536 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3538 this.triggerEl.addClass('dropdown-toggle');
3541 this.el.on('touchstart' , this.onTouch, this);
3543 this.el.on('click' , this.onClick, this);
3545 this.el.on("mouseover", this.onMouseOver, this);
3546 this.el.on("mouseout", this.onMouseOut, this);
3550 findTargetItem : function(e)
3552 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3556 //Roo.log(t); Roo.log(t.id);
3558 //Roo.log(this.menuitems);
3559 return this.menuitems.get(t.id);
3561 //return this.items.get(t.menuItemId);
3567 onTouch : function(e)
3569 Roo.log("menu.onTouch");
3570 //e.stopEvent(); this make the user popdown broken
3574 onClick : function(e)
3576 Roo.log("menu.onClick");
3578 var t = this.findTargetItem(e);
3579 if(!t || t.isContainer){
3584 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3585 if(t == this.activeItem && t.shouldDeactivate(e)){
3586 this.activeItem.deactivate();
3587 delete this.activeItem;
3591 this.setActiveItem(t, true);
3599 Roo.log('pass click event');
3603 this.fireEvent("click", this, t, e);
3607 if(!t.href.length || t.href == '#'){
3608 (function() { _this.hide(); }).defer(100);
3613 onMouseOver : function(e){
3614 var t = this.findTargetItem(e);
3617 // if(t.canActivate && !t.disabled){
3618 // this.setActiveItem(t, true);
3622 this.fireEvent("mouseover", this, e, t);
3624 isVisible : function(){
3625 return !this.hidden;
3627 onMouseOut : function(e){
3628 var t = this.findTargetItem(e);
3631 // if(t == this.activeItem && t.shouldDeactivate(e)){
3632 // this.activeItem.deactivate();
3633 // delete this.activeItem;
3636 this.fireEvent("mouseout", this, e, t);
3641 * Displays this menu relative to another element
3642 * @param {String/HTMLElement/Roo.Element} element The element to align to
3643 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3644 * the element (defaults to this.defaultAlign)
3645 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3647 show : function(el, pos, parentMenu)
3649 if (false === this.fireEvent("beforeshow", this)) {
3650 Roo.log("show canceled");
3653 this.parentMenu = parentMenu;
3658 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3661 * Displays this menu at a specific xy position
3662 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3663 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3665 showAt : function(xy, parentMenu, /* private: */_e){
3666 this.parentMenu = parentMenu;
3671 this.fireEvent("beforeshow", this);
3672 //xy = this.el.adjustForConstraints(xy);
3676 this.hideMenuItems();
3677 this.hidden = false;
3678 this.triggerEl.addClass('open');
3679 this.el.addClass('show');
3681 // reassign x when hitting right
3682 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3683 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3686 // reassign y when hitting bottom
3687 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3688 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3691 // but the list may align on trigger left or trigger top... should it be a properity?
3693 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3698 this.fireEvent("show", this);
3704 this.doFocus.defer(50, this);
3708 doFocus : function(){
3710 this.focusEl.focus();
3715 * Hides this menu and optionally all parent menus
3716 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3718 hide : function(deep)
3720 if (false === this.fireEvent("beforehide", this)) {
3721 Roo.log("hide canceled");
3724 this.hideMenuItems();
3725 if(this.el && this.isVisible()){
3727 if(this.activeItem){
3728 this.activeItem.deactivate();
3729 this.activeItem = null;
3731 this.triggerEl.removeClass('open');;
3732 this.el.removeClass('show');
3734 this.fireEvent("hide", this);
3736 if(deep === true && this.parentMenu){
3737 this.parentMenu.hide(true);
3741 onTriggerClick : function(e)
3743 Roo.log('trigger click');
3745 var target = e.getTarget();
3747 Roo.log(target.nodeName.toLowerCase());
3749 if(target.nodeName.toLowerCase() === 'i'){
3755 onTriggerPress : function(e)
3757 Roo.log('trigger press');
3758 //Roo.log(e.getTarget());
3759 // Roo.log(this.triggerEl.dom);
3761 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3762 var pel = Roo.get(e.getTarget());
3763 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3764 Roo.log('is treeview or dropdown?');
3768 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3772 if (this.isVisible()) {
3777 this.show(this.triggerEl, '?', false);
3780 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3787 hideMenuItems : function()
3789 Roo.log("hide Menu Items");
3794 this.el.select('.open',true).each(function(aa) {
3796 aa.removeClass('open');
3800 addxtypeChild : function (tree, cntr) {
3801 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3803 this.menuitems.add(comp);
3815 this.getEl().dom.innerHTML = '';
3816 this.menuitems.clear();
3830 * @class Roo.bootstrap.MenuItem
3831 * @extends Roo.bootstrap.Component
3832 * Bootstrap MenuItem class
3833 * @cfg {String} html the menu label
3834 * @cfg {String} href the link
3835 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3836 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3837 * @cfg {Boolean} active used on sidebars to highlight active itesm
3838 * @cfg {String} fa favicon to show on left of menu item.
3839 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3843 * Create a new MenuItem
3844 * @param {Object} config The config object
3848 Roo.bootstrap.MenuItem = function(config){
3849 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3854 * The raw click event for the entire grid.
3855 * @param {Roo.bootstrap.MenuItem} this
3856 * @param {Roo.EventObject} e
3862 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3866 preventDefault: false,
3867 isContainer : false,
3871 getAutoCreate : function(){
3873 if(this.isContainer){
3876 cls: 'dropdown-menu-item '
3886 cls : 'dropdown-item',
3891 if (this.fa !== false) {
3894 cls : 'fa fa-' + this.fa
3903 cls: 'dropdown-menu-item',
3906 if (this.parent().type == 'treeview') {
3907 cfg.cls = 'treeview-menu';
3910 cfg.cls += ' active';
3915 anc.href = this.href || cfg.cn[0].href ;
3916 ctag.html = this.html || cfg.cn[0].html ;
3920 initEvents: function()
3922 if (this.parent().type == 'treeview') {
3923 this.el.select('a').on('click', this.onClick, this);
3927 this.menu.parentType = this.xtype;
3928 this.menu.triggerEl = this.el;
3929 this.menu = this.addxtype(Roo.apply({}, this.menu));
3933 onClick : function(e)
3935 Roo.log('item on click ');
3937 if(this.preventDefault){
3940 //this.parent().hideMenuItems();
3942 this.fireEvent('click', this, e);
3961 * @class Roo.bootstrap.MenuSeparator
3962 * @extends Roo.bootstrap.Component
3963 * Bootstrap MenuSeparator class
3966 * Create a new MenuItem
3967 * @param {Object} config The config object
3971 Roo.bootstrap.MenuSeparator = function(config){
3972 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3975 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3977 getAutoCreate : function(){
3996 * @class Roo.bootstrap.Modal
3997 * @extends Roo.bootstrap.Component
3998 * Bootstrap Modal class
3999 * @cfg {String} title Title of dialog
4000 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4001 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4002 * @cfg {Boolean} specificTitle default false
4003 * @cfg {Array} buttons Array of buttons or standard button set..
4004 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4005 * @cfg {Boolean} animate default true
4006 * @cfg {Boolean} allow_close default true
4007 * @cfg {Boolean} fitwindow default false
4008 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4009 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4010 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4011 * @cfg {String} size (sm|lg|xl) default empty
4012 * @cfg {Number} max_width set the max width of modal
4013 * @cfg {Boolean} editableTitle can the title be edited
4018 * Create a new Modal Dialog
4019 * @param {Object} config The config object
4022 Roo.bootstrap.Modal = function(config){
4023 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4028 * The raw btnclick event for the button
4029 * @param {Roo.EventObject} e
4034 * Fire when dialog resize
4035 * @param {Roo.bootstrap.Modal} this
4036 * @param {Roo.EventObject} e
4040 * @event titlechanged
4041 * Fire when the editable title has been changed
4042 * @param {Roo.bootstrap.Modal} this
4043 * @param {Roo.EventObject} value
4045 "titlechanged" : true
4048 this.buttons = this.buttons || [];
4051 this.tmpl = Roo.factory(this.tmpl);
4056 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4058 title : 'test dialog',
4068 specificTitle: false,
4070 buttonPosition: 'right',
4092 editableTitle : false,
4094 onRender : function(ct, position)
4096 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4099 var cfg = Roo.apply({}, this.getAutoCreate());
4102 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4104 //if (!cfg.name.length) {
4108 cfg.cls += ' ' + this.cls;
4111 cfg.style = this.style;
4113 this.el = Roo.get(document.body).createChild(cfg, position);
4115 //var type = this.el.dom.type;
4118 if(this.tabIndex !== undefined){
4119 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4122 this.dialogEl = this.el.select('.modal-dialog',true).first();
4123 this.bodyEl = this.el.select('.modal-body',true).first();
4124 this.closeEl = this.el.select('.modal-header .close', true).first();
4125 this.headerEl = this.el.select('.modal-header',true).first();
4126 this.titleEl = this.el.select('.modal-title',true).first();
4127 this.footerEl = this.el.select('.modal-footer',true).first();
4129 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4131 //this.el.addClass("x-dlg-modal");
4133 if (this.buttons.length) {
4134 Roo.each(this.buttons, function(bb) {
4135 var b = Roo.apply({}, bb);
4136 b.xns = b.xns || Roo.bootstrap;
4137 b.xtype = b.xtype || 'Button';
4138 if (typeof(b.listeners) == 'undefined') {
4139 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4142 var btn = Roo.factory(b);
4144 btn.render(this.getButtonContainer());
4148 // render the children.
4151 if(typeof(this.items) != 'undefined'){
4152 var items = this.items;
4155 for(var i =0;i < items.length;i++) {
4156 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4160 this.items = nitems;
4162 // where are these used - they used to be body/close/footer
4166 //this.el.addClass([this.fieldClass, this.cls]);
4170 getAutoCreate : function()
4172 // we will default to modal-body-overflow - might need to remove or make optional later.
4174 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4175 html : this.html || ''
4180 cls : 'modal-title',
4184 if(this.specificTitle){ // WTF is this?
4189 if (this.allow_close && Roo.bootstrap.version == 3) {
4199 if (this.editableTitle) {
4201 cls: 'form-control roo-editable-title d-none',
4207 if (this.allow_close && Roo.bootstrap.version == 4) {
4217 if(this.size.length){
4218 size = 'modal-' + this.size;
4221 var footer = Roo.bootstrap.version == 3 ?
4223 cls : 'modal-footer',
4227 cls: 'btn-' + this.buttonPosition
4232 { // BS4 uses mr-auto on left buttons....
4233 cls : 'modal-footer'
4244 cls: "modal-dialog " + size,
4247 cls : "modal-content",
4250 cls : 'modal-header',
4265 modal.cls += ' fade';
4271 getChildContainer : function() {
4276 getButtonContainer : function() {
4278 return Roo.bootstrap.version == 4 ?
4279 this.el.select('.modal-footer',true).first()
4280 : this.el.select('.modal-footer div',true).first();
4283 initEvents : function()
4285 if (this.allow_close) {
4286 this.closeEl.on('click', this.hide, this);
4288 Roo.EventManager.onWindowResize(this.resize, this, true);
4289 if (this.editableTitle) {
4290 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4291 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4292 this.headerEditEl.on('keyup', function(e) {
4293 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4294 this.toggleHeaderInput(false)
4297 this.headerEditEl.on('blur', function(e) {
4298 this.toggleHeaderInput(false)
4307 this.maskEl.setSize(
4308 Roo.lib.Dom.getViewWidth(true),
4309 Roo.lib.Dom.getViewHeight(true)
4312 if (this.fitwindow) {
4314 this.dialogEl.setStyle( { 'max-width' : '100%' });
4316 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4317 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4322 if(this.max_width !== 0) {
4324 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4327 this.setSize(w, this.height);
4331 if(this.max_height) {
4332 this.setSize(w,Math.min(
4334 Roo.lib.Dom.getViewportHeight(true) - 60
4340 if(!this.fit_content) {
4341 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4345 this.setSize(w, Math.min(
4347 this.headerEl.getHeight() +
4348 this.footerEl.getHeight() +
4349 this.getChildHeight(this.bodyEl.dom.childNodes),
4350 Roo.lib.Dom.getViewportHeight(true) - 60)
4356 setSize : function(w,h)
4367 if (!this.rendered) {
4370 this.toggleHeaderInput(false);
4371 //this.el.setStyle('display', 'block');
4372 this.el.removeClass('hideing');
4373 this.el.dom.style.display='block';
4375 Roo.get(document.body).addClass('modal-open');
4377 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4380 this.el.addClass('show');
4381 this.el.addClass('in');
4384 this.el.addClass('show');
4385 this.el.addClass('in');
4388 // not sure how we can show data in here..
4390 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4393 Roo.get(document.body).addClass("x-body-masked");
4395 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4396 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4397 this.maskEl.dom.style.display = 'block';
4398 this.maskEl.addClass('show');
4403 this.fireEvent('show', this);
4405 // set zindex here - otherwise it appears to be ignored...
4406 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4409 this.items.forEach( function(e) {
4410 e.layout ? e.layout() : false;
4418 if(this.fireEvent("beforehide", this) !== false){
4420 this.maskEl.removeClass('show');
4422 this.maskEl.dom.style.display = '';
4423 Roo.get(document.body).removeClass("x-body-masked");
4424 this.el.removeClass('in');
4425 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4427 if(this.animate){ // why
4428 this.el.addClass('hideing');
4429 this.el.removeClass('show');
4431 if (!this.el.hasClass('hideing')) {
4432 return; // it's been shown again...
4435 this.el.dom.style.display='';
4437 Roo.get(document.body).removeClass('modal-open');
4438 this.el.removeClass('hideing');
4442 this.el.removeClass('show');
4443 this.el.dom.style.display='';
4444 Roo.get(document.body).removeClass('modal-open');
4447 this.fireEvent('hide', this);
4450 isVisible : function()
4453 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4457 addButton : function(str, cb)
4461 var b = Roo.apply({}, { html : str } );
4462 b.xns = b.xns || Roo.bootstrap;
4463 b.xtype = b.xtype || 'Button';
4464 if (typeof(b.listeners) == 'undefined') {
4465 b.listeners = { click : cb.createDelegate(this) };
4468 var btn = Roo.factory(b);
4470 btn.render(this.getButtonContainer());
4476 setDefaultButton : function(btn)
4478 //this.el.select('.modal-footer').()
4481 resizeTo: function(w,h)
4483 this.dialogEl.setWidth(w);
4485 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4487 this.bodyEl.setHeight(h - diff);
4489 this.fireEvent('resize', this);
4492 setContentSize : function(w, h)
4496 onButtonClick: function(btn,e)
4499 this.fireEvent('btnclick', btn.name, e);
4502 * Set the title of the Dialog
4503 * @param {String} str new Title
4505 setTitle: function(str) {
4506 this.titleEl.dom.innerHTML = str;
4510 * Set the body of the Dialog
4511 * @param {String} str new Title
4513 setBody: function(str) {
4514 this.bodyEl.dom.innerHTML = str;
4517 * Set the body of the Dialog using the template
4518 * @param {Obj} data - apply this data to the template and replace the body contents.
4520 applyBody: function(obj)
4523 Roo.log("Error - using apply Body without a template");
4526 this.tmpl.overwrite(this.bodyEl, obj);
4529 getChildHeight : function(child_nodes)
4533 child_nodes.length == 0
4538 var child_height = 0;
4540 for(var i = 0; i < child_nodes.length; i++) {
4543 * for modal with tabs...
4544 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4546 var layout_childs = child_nodes[i].childNodes;
4548 for(var j = 0; j < layout_childs.length; j++) {
4550 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4552 var layout_body_childs = layout_childs[j].childNodes;
4554 for(var k = 0; k < layout_body_childs.length; k++) {
4556 if(layout_body_childs[k].classList.contains('navbar')) {
4557 child_height += layout_body_childs[k].offsetHeight;
4561 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4563 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4565 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4567 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4568 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4583 child_height += child_nodes[i].offsetHeight;
4584 // Roo.log(child_nodes[i].offsetHeight);
4587 return child_height;
4589 toggleHeaderInput : function(is_edit)
4591 if (!this.editableTitle) {
4592 return; // not editable.
4594 if (is_edit && this.is_header_editing) {
4595 return; // already editing..
4599 this.headerEditEl.dom.value = this.title;
4600 this.headerEditEl.removeClass('d-none');
4601 this.headerEditEl.dom.focus();
4602 this.titleEl.addClass('d-none');
4604 this.is_header_editing = true;
4607 // flip back to not editing.
4608 this.title = this.headerEditEl.dom.value;
4609 this.headerEditEl.addClass('d-none');
4610 this.titleEl.removeClass('d-none');
4611 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4612 this.is_header_editing = false;
4613 this.fireEvent('titlechanged', this, this.title);
4622 Roo.apply(Roo.bootstrap.Modal, {
4624 * Button config that displays a single OK button
4633 * Button config that displays Yes and No buttons
4649 * Button config that displays OK and Cancel buttons
4664 * Button config that displays Yes, No and Cancel buttons
4689 * messagebox - can be used as a replace
4693 * @class Roo.MessageBox
4694 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4698 Roo.Msg.alert('Status', 'Changes saved successfully.');
4700 // Prompt for user data:
4701 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4703 // process text value...
4707 // Show a dialog using config options:
4709 title:'Save Changes?',
4710 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4711 buttons: Roo.Msg.YESNOCANCEL,
4718 Roo.bootstrap.MessageBox = function(){
4719 var dlg, opt, mask, waitTimer;
4720 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4721 var buttons, activeTextEl, bwidth;
4725 var handleButton = function(button){
4727 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4731 var handleHide = function(){
4733 dlg.el.removeClass(opt.cls);
4736 // Roo.TaskMgr.stop(waitTimer);
4737 // waitTimer = null;
4742 var updateButtons = function(b){
4745 buttons["ok"].hide();
4746 buttons["cancel"].hide();
4747 buttons["yes"].hide();
4748 buttons["no"].hide();
4749 dlg.footerEl.hide();
4753 dlg.footerEl.show();
4754 for(var k in buttons){
4755 if(typeof buttons[k] != "function"){
4758 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4759 width += buttons[k].el.getWidth()+15;
4769 var handleEsc = function(d, k, e){
4770 if(opt && opt.closable !== false){
4780 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4781 * @return {Roo.BasicDialog} The BasicDialog element
4783 getDialog : function(){
4785 dlg = new Roo.bootstrap.Modal( {
4788 //constraintoviewport:false,
4790 //collapsible : false,
4795 //buttonAlign:"center",
4796 closeClick : function(){
4797 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4800 handleButton("cancel");
4805 dlg.on("hide", handleHide);
4807 //dlg.addKeyListener(27, handleEsc);
4809 this.buttons = buttons;
4810 var bt = this.buttonText;
4811 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4812 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4813 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4814 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4816 bodyEl = dlg.bodyEl.createChild({
4818 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4819 '<textarea class="roo-mb-textarea"></textarea>' +
4820 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4822 msgEl = bodyEl.dom.firstChild;
4823 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4824 textboxEl.enableDisplayMode();
4825 textboxEl.addKeyListener([10,13], function(){
4826 if(dlg.isVisible() && opt && opt.buttons){
4829 }else if(opt.buttons.yes){
4830 handleButton("yes");
4834 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4835 textareaEl.enableDisplayMode();
4836 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4837 progressEl.enableDisplayMode();
4839 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4840 var pf = progressEl.dom.firstChild;
4842 pp = Roo.get(pf.firstChild);
4843 pp.setHeight(pf.offsetHeight);
4851 * Updates the message box body text
4852 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4853 * the XHTML-compliant non-breaking space character '&#160;')
4854 * @return {Roo.MessageBox} This message box
4856 updateText : function(text)
4858 if(!dlg.isVisible() && !opt.width){
4859 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4860 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4862 msgEl.innerHTML = text || ' ';
4864 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4865 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4867 Math.min(opt.width || cw , this.maxWidth),
4868 Math.max(opt.minWidth || this.minWidth, bwidth)
4871 activeTextEl.setWidth(w);
4873 if(dlg.isVisible()){
4874 dlg.fixedcenter = false;
4876 // to big, make it scroll. = But as usual stupid IE does not support
4879 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4880 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4881 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4883 bodyEl.dom.style.height = '';
4884 bodyEl.dom.style.overflowY = '';
4887 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4889 bodyEl.dom.style.overflowX = '';
4892 dlg.setContentSize(w, bodyEl.getHeight());
4893 if(dlg.isVisible()){
4894 dlg.fixedcenter = true;
4900 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4901 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4902 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4903 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4904 * @return {Roo.MessageBox} This message box
4906 updateProgress : function(value, text){
4908 this.updateText(text);
4911 if (pp) { // weird bug on my firefox - for some reason this is not defined
4912 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4913 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4919 * Returns true if the message box is currently displayed
4920 * @return {Boolean} True if the message box is visible, else false
4922 isVisible : function(){
4923 return dlg && dlg.isVisible();
4927 * Hides the message box if it is displayed
4930 if(this.isVisible()){
4936 * Displays a new message box, or reinitializes an existing message box, based on the config options
4937 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4938 * The following config object properties are supported:
4940 Property Type Description
4941 ---------- --------------- ------------------------------------------------------------------------------------
4942 animEl String/Element An id or Element from which the message box should animate as it opens and
4943 closes (defaults to undefined)
4944 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4945 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4946 closable Boolean False to hide the top-right close button (defaults to true). Note that
4947 progress and wait dialogs will ignore this property and always hide the
4948 close button as they can only be closed programmatically.
4949 cls String A custom CSS class to apply to the message box element
4950 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4951 displayed (defaults to 75)
4952 fn Function A callback function to execute after closing the dialog. The arguments to the
4953 function will be btn (the name of the button that was clicked, if applicable,
4954 e.g. "ok"), and text (the value of the active text field, if applicable).
4955 Progress and wait dialogs will ignore this option since they do not respond to
4956 user actions and can only be closed programmatically, so any required function
4957 should be called by the same code after it closes the dialog.
4958 icon String A CSS class that provides a background image to be used as an icon for
4959 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4960 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4961 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4962 modal Boolean False to allow user interaction with the page while the message box is
4963 displayed (defaults to true)
4964 msg String A string that will replace the existing message box body text (defaults
4965 to the XHTML-compliant non-breaking space character ' ')
4966 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4967 progress Boolean True to display a progress bar (defaults to false)
4968 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4969 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4970 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4971 title String The title text
4972 value String The string value to set into the active textbox element if displayed
4973 wait Boolean True to display a progress bar (defaults to false)
4974 width Number The width of the dialog in pixels
4981 msg: 'Please enter your address:',
4983 buttons: Roo.MessageBox.OKCANCEL,
4986 animEl: 'addAddressBtn'
4989 * @param {Object} config Configuration options
4990 * @return {Roo.MessageBox} This message box
4992 show : function(options)
4995 // this causes nightmares if you show one dialog after another
4996 // especially on callbacks..
4998 if(this.isVisible()){
5001 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5002 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5003 Roo.log("New Dialog Message:" + options.msg )
5004 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5005 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5008 var d = this.getDialog();
5010 d.setTitle(opt.title || " ");
5011 d.closeEl.setDisplayed(opt.closable !== false);
5012 activeTextEl = textboxEl;
5013 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5018 textareaEl.setHeight(typeof opt.multiline == "number" ?
5019 opt.multiline : this.defaultTextHeight);
5020 activeTextEl = textareaEl;
5029 progressEl.setDisplayed(opt.progress === true);
5031 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5033 this.updateProgress(0);
5034 activeTextEl.dom.value = opt.value || "";
5036 dlg.setDefaultButton(activeTextEl);
5038 var bs = opt.buttons;
5042 }else if(bs && bs.yes){
5043 db = buttons["yes"];
5045 dlg.setDefaultButton(db);
5047 bwidth = updateButtons(opt.buttons);
5048 this.updateText(opt.msg);
5050 d.el.addClass(opt.cls);
5052 d.proxyDrag = opt.proxyDrag === true;
5053 d.modal = opt.modal !== false;
5054 d.mask = opt.modal !== false ? mask : false;
5056 // force it to the end of the z-index stack so it gets a cursor in FF
5057 document.body.appendChild(dlg.el.dom);
5058 d.animateTarget = null;
5059 d.show(options.animEl);
5065 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5066 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5067 * and closing the message box when the process is complete.
5068 * @param {String} title The title bar text
5069 * @param {String} msg The message box body text
5070 * @return {Roo.MessageBox} This message box
5072 progress : function(title, msg){
5079 minWidth: this.minProgressWidth,
5086 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5087 * If a callback function is passed it will be called after the user clicks the button, and the
5088 * id of the button that was clicked will be passed as the only parameter to the callback
5089 * (could also be the top-right close button).
5090 * @param {String} title The title bar text
5091 * @param {String} msg The message box body text
5092 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5093 * @param {Object} scope (optional) The scope of the callback function
5094 * @return {Roo.MessageBox} This message box
5096 alert : function(title, msg, fn, scope)
5111 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5112 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5113 * You are responsible for closing the message box when the process is complete.
5114 * @param {String} msg The message box body text
5115 * @param {String} title (optional) The title bar text
5116 * @return {Roo.MessageBox} This message box
5118 wait : function(msg, title){
5129 waitTimer = Roo.TaskMgr.start({
5131 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5139 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5140 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5141 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5142 * @param {String} title The title bar text
5143 * @param {String} msg The message box body text
5144 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5145 * @param {Object} scope (optional) The scope of the callback function
5146 * @return {Roo.MessageBox} This message box
5148 confirm : function(title, msg, fn, scope){
5152 buttons: this.YESNO,
5161 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5162 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5163 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5164 * (could also be the top-right close button) and the text that was entered will be passed as the two
5165 * parameters to the callback.
5166 * @param {String} title The title bar text
5167 * @param {String} msg The message box body text
5168 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5169 * @param {Object} scope (optional) The scope of the callback function
5170 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5171 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5172 * @return {Roo.MessageBox} This message box
5174 prompt : function(title, msg, fn, scope, multiline){
5178 buttons: this.OKCANCEL,
5183 multiline: multiline,
5190 * Button config that displays a single OK button
5195 * Button config that displays Yes and No buttons
5198 YESNO : {yes:true, no:true},
5200 * Button config that displays OK and Cancel buttons
5203 OKCANCEL : {ok:true, cancel:true},
5205 * Button config that displays Yes, No and Cancel buttons
5208 YESNOCANCEL : {yes:true, no:true, cancel:true},
5211 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5214 defaultTextHeight : 75,
5216 * The maximum width in pixels of the message box (defaults to 600)
5221 * The minimum width in pixels of the message box (defaults to 100)
5226 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5227 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5230 minProgressWidth : 250,
5232 * An object containing the default button text strings that can be overriden for localized language support.
5233 * Supported properties are: ok, cancel, yes and no.
5234 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5247 * Shorthand for {@link Roo.MessageBox}
5249 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5250 Roo.Msg = Roo.Msg || Roo.MessageBox;
5259 * @class Roo.bootstrap.Navbar
5260 * @extends Roo.bootstrap.Component
5261 * Bootstrap Navbar class
5264 * Create a new Navbar
5265 * @param {Object} config The config object
5269 Roo.bootstrap.Navbar = function(config){
5270 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5274 * @event beforetoggle
5275 * Fire before toggle the menu
5276 * @param {Roo.EventObject} e
5278 "beforetoggle" : true
5282 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5291 getAutoCreate : function(){
5294 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5298 initEvents :function ()
5300 //Roo.log(this.el.select('.navbar-toggle',true));
5301 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5308 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5310 var size = this.el.getSize();
5311 this.maskEl.setSize(size.width, size.height);
5312 this.maskEl.enableDisplayMode("block");
5321 getChildContainer : function()
5323 if (this.el && this.el.select('.collapse').getCount()) {
5324 return this.el.select('.collapse',true).first();
5339 onToggle : function()
5342 if(this.fireEvent('beforetoggle', this) === false){
5345 var ce = this.el.select('.navbar-collapse',true).first();
5347 if (!ce.hasClass('show')) {
5357 * Expand the navbar pulldown
5359 expand : function ()
5362 var ce = this.el.select('.navbar-collapse',true).first();
5363 if (ce.hasClass('collapsing')) {
5366 ce.dom.style.height = '';
5368 ce.addClass('in'); // old...
5369 ce.removeClass('collapse');
5370 ce.addClass('show');
5371 var h = ce.getHeight();
5373 ce.removeClass('show');
5374 // at this point we should be able to see it..
5375 ce.addClass('collapsing');
5377 ce.setHeight(0); // resize it ...
5378 ce.on('transitionend', function() {
5379 //Roo.log('done transition');
5380 ce.removeClass('collapsing');
5381 ce.addClass('show');
5382 ce.removeClass('collapse');
5384 ce.dom.style.height = '';
5385 }, this, { single: true} );
5387 ce.dom.scrollTop = 0;
5390 * Collapse the navbar pulldown
5392 collapse : function()
5394 var ce = this.el.select('.navbar-collapse',true).first();
5396 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5397 // it's collapsed or collapsing..
5400 ce.removeClass('in'); // old...
5401 ce.setHeight(ce.getHeight());
5402 ce.removeClass('show');
5403 ce.addClass('collapsing');
5405 ce.on('transitionend', function() {
5406 ce.dom.style.height = '';
5407 ce.removeClass('collapsing');
5408 ce.addClass('collapse');
5409 }, this, { single: true} );
5429 * @class Roo.bootstrap.NavSimplebar
5430 * @extends Roo.bootstrap.Navbar
5431 * Bootstrap Sidebar class
5433 * @cfg {Boolean} inverse is inverted color
5435 * @cfg {String} type (nav | pills | tabs)
5436 * @cfg {Boolean} arrangement stacked | justified
5437 * @cfg {String} align (left | right) alignment
5439 * @cfg {Boolean} main (true|false) main nav bar? default false
5440 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5442 * @cfg {String} tag (header|footer|nav|div) default is nav
5444 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5448 * Create a new Sidebar
5449 * @param {Object} config The config object
5453 Roo.bootstrap.NavSimplebar = function(config){
5454 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5457 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5473 getAutoCreate : function(){
5477 tag : this.tag || 'div',
5478 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5480 if (['light','white'].indexOf(this.weight) > -1) {
5481 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5483 cfg.cls += ' bg-' + this.weight;
5486 cfg.cls += ' navbar-inverse';
5490 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5492 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5501 cls: 'nav nav-' + this.xtype,
5507 this.type = this.type || 'nav';
5508 if (['tabs','pills'].indexOf(this.type) != -1) {
5509 cfg.cn[0].cls += ' nav-' + this.type
5513 if (this.type!=='nav') {
5514 Roo.log('nav type must be nav/tabs/pills')
5516 cfg.cn[0].cls += ' navbar-nav'
5522 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5523 cfg.cn[0].cls += ' nav-' + this.arrangement;
5527 if (this.align === 'right') {
5528 cfg.cn[0].cls += ' navbar-right';
5553 * navbar-expand-md fixed-top
5557 * @class Roo.bootstrap.NavHeaderbar
5558 * @extends Roo.bootstrap.NavSimplebar
5559 * Bootstrap Sidebar class
5561 * @cfg {String} brand what is brand
5562 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5563 * @cfg {String} brand_href href of the brand
5564 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5565 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5566 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5567 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5570 * Create a new Sidebar
5571 * @param {Object} config The config object
5575 Roo.bootstrap.NavHeaderbar = function(config){
5576 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5580 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5587 desktopCenter : false,
5590 getAutoCreate : function(){
5593 tag: this.nav || 'nav',
5594 cls: 'navbar navbar-expand-md',
5600 if (this.desktopCenter) {
5601 cn.push({cls : 'container', cn : []});
5609 cls: 'navbar-toggle navbar-toggler',
5610 'data-toggle': 'collapse',
5615 html: 'Toggle navigation'
5619 cls: 'icon-bar navbar-toggler-icon'
5632 cn.push( Roo.bootstrap.version == 4 ? btn : {
5634 cls: 'navbar-header',
5643 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5647 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5649 if (['light','white'].indexOf(this.weight) > -1) {
5650 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5652 cfg.cls += ' bg-' + this.weight;
5655 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5656 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5658 // tag can override this..
5660 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5663 if (this.brand !== '') {
5664 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5665 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5667 href: this.brand_href ? this.brand_href : '#',
5668 cls: 'navbar-brand',
5676 cfg.cls += ' main-nav';
5684 getHeaderChildContainer : function()
5686 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5687 return this.el.select('.navbar-header',true).first();
5690 return this.getChildContainer();
5693 getChildContainer : function()
5696 return this.el.select('.roo-navbar-collapse',true).first();
5701 initEvents : function()
5703 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5705 if (this.autohide) {
5710 Roo.get(document).on('scroll',function(e) {
5711 var ns = Roo.get(document).getScroll().top;
5712 var os = prevScroll;
5716 ft.removeClass('slideDown');
5717 ft.addClass('slideUp');
5720 ft.removeClass('slideUp');
5721 ft.addClass('slideDown');
5742 * @class Roo.bootstrap.NavSidebar
5743 * @extends Roo.bootstrap.Navbar
5744 * Bootstrap Sidebar class
5747 * Create a new Sidebar
5748 * @param {Object} config The config object
5752 Roo.bootstrap.NavSidebar = function(config){
5753 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5756 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5758 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5760 getAutoCreate : function(){
5765 cls: 'sidebar sidebar-nav'
5787 * @class Roo.bootstrap.NavGroup
5788 * @extends Roo.bootstrap.Component
5789 * Bootstrap NavGroup class
5790 * @cfg {String} align (left|right)
5791 * @cfg {Boolean} inverse
5792 * @cfg {String} type (nav|pills|tab) default nav
5793 * @cfg {String} navId - reference Id for navbar.
5794 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5797 * Create a new nav group
5798 * @param {Object} config The config object
5801 Roo.bootstrap.NavGroup = function(config){
5802 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5805 Roo.bootstrap.NavGroup.register(this);
5809 * Fires when the active item changes
5810 * @param {Roo.bootstrap.NavGroup} this
5811 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5812 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5819 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5831 getAutoCreate : function()
5833 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5839 if (Roo.bootstrap.version == 4) {
5840 if (['tabs','pills'].indexOf(this.type) != -1) {
5841 cfg.cls += ' nav-' + this.type;
5843 // trying to remove so header bar can right align top?
5844 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5845 // do not use on header bar...
5846 cfg.cls += ' navbar-nav';
5851 if (['tabs','pills'].indexOf(this.type) != -1) {
5852 cfg.cls += ' nav-' + this.type
5854 if (this.type !== 'nav') {
5855 Roo.log('nav type must be nav/tabs/pills')
5857 cfg.cls += ' navbar-nav'
5861 if (this.parent() && this.parent().sidebar) {
5864 cls: 'dashboard-menu sidebar-menu'
5870 if (this.form === true) {
5873 cls: 'navbar-form form-inline'
5875 //nav navbar-right ml-md-auto
5876 if (this.align === 'right') {
5877 cfg.cls += ' navbar-right ml-md-auto';
5879 cfg.cls += ' navbar-left';
5883 if (this.align === 'right') {
5884 cfg.cls += ' navbar-right ml-md-auto';
5886 cfg.cls += ' mr-auto';
5890 cfg.cls += ' navbar-inverse';
5898 * sets the active Navigation item
5899 * @param {Roo.bootstrap.NavItem} the new current navitem
5901 setActiveItem : function(item)
5904 Roo.each(this.navItems, function(v){
5909 v.setActive(false, true);
5916 item.setActive(true, true);
5917 this.fireEvent('changed', this, item, prev);
5922 * gets the active Navigation item
5923 * @return {Roo.bootstrap.NavItem} the current navitem
5925 getActive : function()
5929 Roo.each(this.navItems, function(v){
5940 indexOfNav : function()
5944 Roo.each(this.navItems, function(v,i){
5955 * adds a Navigation item
5956 * @param {Roo.bootstrap.NavItem} the navitem to add
5958 addItem : function(cfg)
5960 if (this.form && Roo.bootstrap.version == 4) {
5963 var cn = new Roo.bootstrap.NavItem(cfg);
5965 cn.parentId = this.id;
5966 cn.onRender(this.el, null);
5970 * register a Navigation item
5971 * @param {Roo.bootstrap.NavItem} the navitem to add
5973 register : function(item)
5975 this.navItems.push( item);
5976 item.navId = this.navId;
5981 * clear all the Navigation item
5984 clearAll : function()
5987 this.el.dom.innerHTML = '';
5990 getNavItem: function(tabId)
5993 Roo.each(this.navItems, function(e) {
5994 if (e.tabId == tabId) {
6004 setActiveNext : function()
6006 var i = this.indexOfNav(this.getActive());
6007 if (i > this.navItems.length) {
6010 this.setActiveItem(this.navItems[i+1]);
6012 setActivePrev : function()
6014 var i = this.indexOfNav(this.getActive());
6018 this.setActiveItem(this.navItems[i-1]);
6020 clearWasActive : function(except) {
6021 Roo.each(this.navItems, function(e) {
6022 if (e.tabId != except.tabId && e.was_active) {
6023 e.was_active = false;
6030 getWasActive : function ()
6033 Roo.each(this.navItems, function(e) {
6048 Roo.apply(Roo.bootstrap.NavGroup, {
6052 * register a Navigation Group
6053 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6055 register : function(navgrp)
6057 this.groups[navgrp.navId] = navgrp;
6061 * fetch a Navigation Group based on the navigation ID
6062 * @param {string} the navgroup to add
6063 * @returns {Roo.bootstrap.NavGroup} the navgroup
6065 get: function(navId) {
6066 if (typeof(this.groups[navId]) == 'undefined') {
6068 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6070 return this.groups[navId] ;
6085 * @class Roo.bootstrap.NavItem
6086 * @extends Roo.bootstrap.Component
6087 * Bootstrap Navbar.NavItem class
6088 * @cfg {String} href link to
6089 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6090 * @cfg {Boolean} button_outline show and outlined button
6091 * @cfg {String} html content of button
6092 * @cfg {String} badge text inside badge
6093 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6094 * @cfg {String} glyphicon DEPRICATED - use fa
6095 * @cfg {String} icon DEPRICATED - use fa
6096 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6097 * @cfg {Boolean} active Is item active
6098 * @cfg {Boolean} disabled Is item disabled
6099 * @cfg {String} linkcls Link Class
6100 * @cfg {Boolean} preventDefault (true | false) default false
6101 * @cfg {String} tabId the tab that this item activates.
6102 * @cfg {String} tagtype (a|span) render as a href or span?
6103 * @cfg {Boolean} animateRef (true|false) link to element default false
6106 * Create a new Navbar Item
6107 * @param {Object} config The config object
6109 Roo.bootstrap.NavItem = function(config){
6110 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6115 * The raw click event for the entire grid.
6116 * @param {Roo.EventObject} e
6121 * Fires when the active item active state changes
6122 * @param {Roo.bootstrap.NavItem} this
6123 * @param {boolean} state the new state
6129 * Fires when scroll to element
6130 * @param {Roo.bootstrap.NavItem} this
6131 * @param {Object} options
6132 * @param {Roo.EventObject} e
6140 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6149 preventDefault : false,
6157 button_outline : false,
6161 getAutoCreate : function(){
6168 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6171 cfg.cls += ' active' ;
6173 if (this.disabled) {
6174 cfg.cls += ' disabled';
6178 if (this.button_weight.length) {
6179 cfg.tag = this.href ? 'a' : 'button';
6180 cfg.html = this.html || '';
6181 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6183 cfg.href = this.href;
6186 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6188 cfg.cls += " nav-html";
6191 // menu .. should add dropdown-menu class - so no need for carat..
6193 if (this.badge !== '') {
6195 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6200 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6204 href : this.href || "#",
6205 html: this.html || '',
6209 if (this.tagtype == 'a') {
6210 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6214 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6215 } else if (this.fa) {
6216 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6217 } else if(this.glyphicon) {
6218 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6220 cfg.cn[0].cls += " nav-html";
6224 cfg.cn[0].html += " <span class='caret'></span>";
6228 if (this.badge !== '') {
6229 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6237 onRender : function(ct, position)
6239 // Roo.log("Call onRender: " + this.xtype);
6240 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6244 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6245 this.navLink = this.el.select('.nav-link',true).first();
6246 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6251 initEvents: function()
6253 if (typeof (this.menu) != 'undefined') {
6254 this.menu.parentType = this.xtype;
6255 this.menu.triggerEl = this.el;
6256 this.menu = this.addxtype(Roo.apply({}, this.menu));
6259 this.el.on('click', this.onClick, this);
6261 //if(this.tagtype == 'span'){
6262 // this.el.select('span',true).on('click', this.onClick, this);
6265 // at this point parent should be available..
6266 this.parent().register(this);
6269 onClick : function(e)
6271 if (e.getTarget('.dropdown-menu-item')) {
6272 // did you click on a menu itemm.... - then don't trigger onclick..
6277 this.preventDefault ||
6280 Roo.log("NavItem - prevent Default?");
6284 if (this.disabled) {
6288 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6289 if (tg && tg.transition) {
6290 Roo.log("waiting for the transitionend");
6296 //Roo.log("fire event clicked");
6297 if(this.fireEvent('click', this, e) === false){
6301 if(this.tagtype == 'span'){
6305 //Roo.log(this.href);
6306 var ael = this.el.select('a',true).first();
6309 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6310 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6311 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6312 return; // ignore... - it's a 'hash' to another page.
6314 Roo.log("NavItem - prevent Default?");
6316 this.scrollToElement(e);
6320 var p = this.parent();
6322 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6323 if (typeof(p.setActiveItem) !== 'undefined') {
6324 p.setActiveItem(this);
6328 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6329 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6330 // remove the collapsed menu expand...
6331 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6335 isActive: function () {
6338 setActive : function(state, fire, is_was_active)
6340 if (this.active && !state && this.navId) {
6341 this.was_active = true;
6342 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6344 nv.clearWasActive(this);
6348 this.active = state;
6351 this.el.removeClass('active');
6352 this.navLink ? this.navLink.removeClass('active') : false;
6353 } else if (!this.el.hasClass('active')) {
6355 this.el.addClass('active');
6356 if (Roo.bootstrap.version == 4 && this.navLink ) {
6357 this.navLink.addClass('active');
6362 this.fireEvent('changed', this, state);
6365 // show a panel if it's registered and related..
6367 if (!this.navId || !this.tabId || !state || is_was_active) {
6371 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6375 var pan = tg.getPanelByName(this.tabId);
6379 // if we can not flip to new panel - go back to old nav highlight..
6380 if (false == tg.showPanel(pan)) {
6381 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6383 var onav = nv.getWasActive();
6385 onav.setActive(true, false, true);
6394 // this should not be here...
6395 setDisabled : function(state)
6397 this.disabled = state;
6399 this.el.removeClass('disabled');
6400 } else if (!this.el.hasClass('disabled')) {
6401 this.el.addClass('disabled');
6407 * Fetch the element to display the tooltip on.
6408 * @return {Roo.Element} defaults to this.el
6410 tooltipEl : function()
6412 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6415 scrollToElement : function(e)
6417 var c = document.body;
6420 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6422 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6423 c = document.documentElement;
6426 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6432 var o = target.calcOffsetsTo(c);
6439 this.fireEvent('scrollto', this, options, e);
6441 Roo.get(c).scrollTo('top', options.value, true);
6446 * Set the HTML (text content) of the item
6447 * @param {string} html content for the nav item
6449 setHtml : function(html)
6452 this.htmlEl.dom.innerHTML = html;
6464 * <span> icon </span>
6465 * <span> text </span>
6466 * <span>badge </span>
6470 * @class Roo.bootstrap.NavSidebarItem
6471 * @extends Roo.bootstrap.NavItem
6472 * Bootstrap Navbar.NavSidebarItem class
6473 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6474 * {Boolean} open is the menu open
6475 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6476 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6477 * {String} buttonSize (sm|md|lg)the extra classes for the button
6478 * {Boolean} showArrow show arrow next to the text (default true)
6480 * Create a new Navbar Button
6481 * @param {Object} config The config object
6483 Roo.bootstrap.NavSidebarItem = function(config){
6484 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6489 * The raw click event for the entire grid.
6490 * @param {Roo.EventObject} e
6495 * Fires when the active item active state changes
6496 * @param {Roo.bootstrap.NavSidebarItem} this
6497 * @param {boolean} state the new state
6505 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6507 badgeWeight : 'default',
6513 buttonWeight : 'default',
6519 getAutoCreate : function(){
6524 href : this.href || '#',
6530 if(this.buttonView){
6533 href : this.href || '#',
6534 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6547 cfg.cls += ' active';
6550 if (this.disabled) {
6551 cfg.cls += ' disabled';
6554 cfg.cls += ' open x-open';
6557 if (this.glyphicon || this.icon) {
6558 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6559 a.cn.push({ tag : 'i', cls : c }) ;
6562 if(!this.buttonView){
6565 html : this.html || ''
6572 if (this.badge !== '') {
6573 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6579 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6582 a.cls += ' dropdown-toggle treeview' ;
6588 initEvents : function()
6590 if (typeof (this.menu) != 'undefined') {
6591 this.menu.parentType = this.xtype;
6592 this.menu.triggerEl = this.el;
6593 this.menu = this.addxtype(Roo.apply({}, this.menu));
6596 this.el.on('click', this.onClick, this);
6598 if(this.badge !== ''){
6599 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6604 onClick : function(e)
6611 if(this.preventDefault){
6615 this.fireEvent('click', this, e);
6618 disable : function()
6620 this.setDisabled(true);
6625 this.setDisabled(false);
6628 setDisabled : function(state)
6630 if(this.disabled == state){
6634 this.disabled = state;
6637 this.el.addClass('disabled');
6641 this.el.removeClass('disabled');
6646 setActive : function(state)
6648 if(this.active == state){
6652 this.active = state;
6655 this.el.addClass('active');
6659 this.el.removeClass('active');
6664 isActive: function ()
6669 setBadge : function(str)
6675 this.badgeEl.dom.innerHTML = str;
6690 Roo.namespace('Roo.bootstrap.breadcrumb');
6694 * @class Roo.bootstrap.breadcrumb.Nav
6695 * @extends Roo.bootstrap.Component
6696 * Bootstrap Breadcrumb Nav Class
6698 * @children Roo.bootstrap.breadcrumb.Item
6701 * Create a new breadcrumb.Nav
6702 * @param {Object} config The config object
6706 Roo.bootstrap.breadcrumb.Nav = function(config){
6707 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6712 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6714 getAutoCreate : function()
6731 initEvents: function()
6733 this.olEl = this.el.select('ol',true).first();
6735 getChildContainer : function()
6751 * @class Roo.bootstrap.breadcrumb.Nav
6752 * @extends Roo.bootstrap.Component
6753 * Bootstrap Breadcrumb Nav Class
6755 * @children Roo.bootstrap.breadcrumb.Component
6756 * @cfg {String} html the content of the link.
6757 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6758 * @cfg {Boolean} active is it active
6762 * Create a new breadcrumb.Nav
6763 * @param {Object} config The config object
6766 Roo.bootstrap.breadcrumb.Item = function(config){
6767 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6772 * The img click event for the img.
6773 * @param {Roo.EventObject} e
6780 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6785 getAutoCreate : function()
6790 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6792 if (this.href !== false) {
6799 cfg.html = this.html;
6805 initEvents: function()
6808 this.el.select('a', true).first().on('click',this.onClick, this)
6812 onClick : function(e)
6815 this.fireEvent('click',this, e);
6828 * @class Roo.bootstrap.Row
6829 * @extends Roo.bootstrap.Component
6830 * Bootstrap Row class (contains columns...)
6834 * @param {Object} config The config object
6837 Roo.bootstrap.Row = function(config){
6838 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6841 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6843 getAutoCreate : function(){
6862 * @class Roo.bootstrap.Pagination
6863 * @extends Roo.bootstrap.Component
6864 * Bootstrap Pagination class
6865 * @cfg {String} size xs | sm | md | lg
6866 * @cfg {Boolean} inverse false | true
6869 * Create a new Pagination
6870 * @param {Object} config The config object
6873 Roo.bootstrap.Pagination = function(config){
6874 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6877 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6883 getAutoCreate : function(){
6889 cfg.cls += ' inverse';
6895 cfg.cls += " " + this.cls;
6913 * @class Roo.bootstrap.PaginationItem
6914 * @extends Roo.bootstrap.Component
6915 * Bootstrap PaginationItem class
6916 * @cfg {String} html text
6917 * @cfg {String} href the link
6918 * @cfg {Boolean} preventDefault (true | false) default true
6919 * @cfg {Boolean} active (true | false) default false
6920 * @cfg {Boolean} disabled default false
6924 * Create a new PaginationItem
6925 * @param {Object} config The config object
6929 Roo.bootstrap.PaginationItem = function(config){
6930 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6935 * The raw click event for the entire grid.
6936 * @param {Roo.EventObject} e
6942 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6946 preventDefault: true,
6951 getAutoCreate : function(){
6957 href : this.href ? this.href : '#',
6958 html : this.html ? this.html : ''
6968 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6972 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6978 initEvents: function() {
6980 this.el.on('click', this.onClick, this);
6983 onClick : function(e)
6985 Roo.log('PaginationItem on click ');
6986 if(this.preventDefault){
6994 this.fireEvent('click', this, e);
7010 * @class Roo.bootstrap.Slider
7011 * @extends Roo.bootstrap.Component
7012 * Bootstrap Slider class
7015 * Create a new Slider
7016 * @param {Object} config The config object
7019 Roo.bootstrap.Slider = function(config){
7020 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7023 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7025 getAutoCreate : function(){
7029 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7033 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7045 * Ext JS Library 1.1.1
7046 * Copyright(c) 2006-2007, Ext JS, LLC.
7048 * Originally Released Under LGPL - original licence link has changed is not relivant.
7051 * <script type="text/javascript">
7056 * @class Roo.grid.ColumnModel
7057 * @extends Roo.util.Observable
7058 * This is the default implementation of a ColumnModel used by the Grid. It defines
7059 * the columns in the grid.
7062 var colModel = new Roo.grid.ColumnModel([
7063 {header: "Ticker", width: 60, sortable: true, locked: true},
7064 {header: "Company Name", width: 150, sortable: true},
7065 {header: "Market Cap.", width: 100, sortable: true},
7066 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7067 {header: "Employees", width: 100, sortable: true, resizable: false}
7072 * The config options listed for this class are options which may appear in each
7073 * individual column definition.
7074 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7076 * @param {Object} config An Array of column config objects. See this class's
7077 * config objects for details.
7079 Roo.grid.ColumnModel = function(config){
7081 * The config passed into the constructor
7083 this.config = config;
7086 // if no id, create one
7087 // if the column does not have a dataIndex mapping,
7088 // map it to the order it is in the config
7089 for(var i = 0, len = config.length; i < len; i++){
7091 if(typeof c.dataIndex == "undefined"){
7094 if(typeof c.renderer == "string"){
7095 c.renderer = Roo.util.Format[c.renderer];
7097 if(typeof c.id == "undefined"){
7100 if(c.editor && c.editor.xtype){
7101 c.editor = Roo.factory(c.editor, Roo.grid);
7103 if(c.editor && c.editor.isFormField){
7104 c.editor = new Roo.grid.GridEditor(c.editor);
7106 this.lookup[c.id] = c;
7110 * The width of columns which have no width specified (defaults to 100)
7113 this.defaultWidth = 100;
7116 * Default sortable of columns which have no sortable specified (defaults to false)
7119 this.defaultSortable = false;
7123 * @event widthchange
7124 * Fires when the width of a column changes.
7125 * @param {ColumnModel} this
7126 * @param {Number} columnIndex The column index
7127 * @param {Number} newWidth The new width
7129 "widthchange": true,
7131 * @event headerchange
7132 * Fires when the text of a header changes.
7133 * @param {ColumnModel} this
7134 * @param {Number} columnIndex The column index
7135 * @param {Number} newText The new header text
7137 "headerchange": true,
7139 * @event hiddenchange
7140 * Fires when a column is hidden or "unhidden".
7141 * @param {ColumnModel} this
7142 * @param {Number} columnIndex The column index
7143 * @param {Boolean} hidden true if hidden, false otherwise
7145 "hiddenchange": true,
7147 * @event columnmoved
7148 * Fires when a column is moved.
7149 * @param {ColumnModel} this
7150 * @param {Number} oldIndex
7151 * @param {Number} newIndex
7153 "columnmoved" : true,
7155 * @event columlockchange
7156 * Fires when a column's locked state is changed
7157 * @param {ColumnModel} this
7158 * @param {Number} colIndex
7159 * @param {Boolean} locked true if locked
7161 "columnlockchange" : true
7163 Roo.grid.ColumnModel.superclass.constructor.call(this);
7165 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7167 * @cfg {String} header The header text to display in the Grid view.
7170 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7171 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7172 * specified, the column's index is used as an index into the Record's data Array.
7175 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7176 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7179 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7180 * Defaults to the value of the {@link #defaultSortable} property.
7181 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7184 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7187 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7190 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7193 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7196 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7197 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7198 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7199 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7202 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7205 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7208 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7211 * @cfg {String} cursor (Optional)
7214 * @cfg {String} tooltip (Optional)
7217 * @cfg {Number} xs (Optional)
7220 * @cfg {Number} sm (Optional)
7223 * @cfg {Number} md (Optional)
7226 * @cfg {Number} lg (Optional)
7229 * Returns the id of the column at the specified index.
7230 * @param {Number} index The column index
7231 * @return {String} the id
7233 getColumnId : function(index){
7234 return this.config[index].id;
7238 * Returns the column for a specified id.
7239 * @param {String} id The column id
7240 * @return {Object} the column
7242 getColumnById : function(id){
7243 return this.lookup[id];
7248 * Returns the column for a specified dataIndex.
7249 * @param {String} dataIndex The column dataIndex
7250 * @return {Object|Boolean} the column or false if not found
7252 getColumnByDataIndex: function(dataIndex){
7253 var index = this.findColumnIndex(dataIndex);
7254 return index > -1 ? this.config[index] : false;
7258 * Returns the index for a specified column id.
7259 * @param {String} id The column id
7260 * @return {Number} the index, or -1 if not found
7262 getIndexById : function(id){
7263 for(var i = 0, len = this.config.length; i < len; i++){
7264 if(this.config[i].id == id){
7272 * Returns the index for a specified column dataIndex.
7273 * @param {String} dataIndex The column dataIndex
7274 * @return {Number} the index, or -1 if not found
7277 findColumnIndex : function(dataIndex){
7278 for(var i = 0, len = this.config.length; i < len; i++){
7279 if(this.config[i].dataIndex == dataIndex){
7287 moveColumn : function(oldIndex, newIndex){
7288 var c = this.config[oldIndex];
7289 this.config.splice(oldIndex, 1);
7290 this.config.splice(newIndex, 0, c);
7291 this.dataMap = null;
7292 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7295 isLocked : function(colIndex){
7296 return this.config[colIndex].locked === true;
7299 setLocked : function(colIndex, value, suppressEvent){
7300 if(this.isLocked(colIndex) == value){
7303 this.config[colIndex].locked = value;
7305 this.fireEvent("columnlockchange", this, colIndex, value);
7309 getTotalLockedWidth : function(){
7311 for(var i = 0; i < this.config.length; i++){
7312 if(this.isLocked(i) && !this.isHidden(i)){
7313 this.totalWidth += this.getColumnWidth(i);
7319 getLockedCount : function(){
7320 for(var i = 0, len = this.config.length; i < len; i++){
7321 if(!this.isLocked(i)){
7326 return this.config.length;
7330 * Returns the number of columns.
7333 getColumnCount : function(visibleOnly){
7334 if(visibleOnly === true){
7336 for(var i = 0, len = this.config.length; i < len; i++){
7337 if(!this.isHidden(i)){
7343 return this.config.length;
7347 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7348 * @param {Function} fn
7349 * @param {Object} scope (optional)
7350 * @return {Array} result
7352 getColumnsBy : function(fn, scope){
7354 for(var i = 0, len = this.config.length; i < len; i++){
7355 var c = this.config[i];
7356 if(fn.call(scope||this, c, i) === true){
7364 * Returns true if the specified column is sortable.
7365 * @param {Number} col The column index
7368 isSortable : function(col){
7369 if(typeof this.config[col].sortable == "undefined"){
7370 return this.defaultSortable;
7372 return this.config[col].sortable;
7376 * Returns the rendering (formatting) function defined for the column.
7377 * @param {Number} col The column index.
7378 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7380 getRenderer : function(col){
7381 if(!this.config[col].renderer){
7382 return Roo.grid.ColumnModel.defaultRenderer;
7384 return this.config[col].renderer;
7388 * Sets the rendering (formatting) function for a column.
7389 * @param {Number} col The column index
7390 * @param {Function} fn The function to use to process the cell's raw data
7391 * to return HTML markup for the grid view. The render function is called with
7392 * the following parameters:<ul>
7393 * <li>Data value.</li>
7394 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7395 * <li>css A CSS style string to apply to the table cell.</li>
7396 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7397 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7398 * <li>Row index</li>
7399 * <li>Column index</li>
7400 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7402 setRenderer : function(col, fn){
7403 this.config[col].renderer = fn;
7407 * Returns the width for the specified column.
7408 * @param {Number} col The column index
7411 getColumnWidth : function(col){
7412 return this.config[col].width * 1 || this.defaultWidth;
7416 * Sets the width for a column.
7417 * @param {Number} col The column index
7418 * @param {Number} width The new width
7420 setColumnWidth : function(col, width, suppressEvent){
7421 this.config[col].width = width;
7422 this.totalWidth = null;
7424 this.fireEvent("widthchange", this, col, width);
7429 * Returns the total width of all columns.
7430 * @param {Boolean} includeHidden True to include hidden column widths
7433 getTotalWidth : function(includeHidden){
7434 if(!this.totalWidth){
7435 this.totalWidth = 0;
7436 for(var i = 0, len = this.config.length; i < len; i++){
7437 if(includeHidden || !this.isHidden(i)){
7438 this.totalWidth += this.getColumnWidth(i);
7442 return this.totalWidth;
7446 * Returns the header for the specified column.
7447 * @param {Number} col The column index
7450 getColumnHeader : function(col){
7451 return this.config[col].header;
7455 * Sets the header for a column.
7456 * @param {Number} col The column index
7457 * @param {String} header The new header
7459 setColumnHeader : function(col, header){
7460 this.config[col].header = header;
7461 this.fireEvent("headerchange", this, col, header);
7465 * Returns the tooltip for the specified column.
7466 * @param {Number} col The column index
7469 getColumnTooltip : function(col){
7470 return this.config[col].tooltip;
7473 * Sets the tooltip for a column.
7474 * @param {Number} col The column index
7475 * @param {String} tooltip The new tooltip
7477 setColumnTooltip : function(col, tooltip){
7478 this.config[col].tooltip = tooltip;
7482 * Returns the dataIndex for the specified column.
7483 * @param {Number} col The column index
7486 getDataIndex : function(col){
7487 return this.config[col].dataIndex;
7491 * Sets the dataIndex for a column.
7492 * @param {Number} col The column index
7493 * @param {Number} dataIndex The new dataIndex
7495 setDataIndex : function(col, dataIndex){
7496 this.config[col].dataIndex = dataIndex;
7502 * Returns true if the cell is editable.
7503 * @param {Number} colIndex The column index
7504 * @param {Number} rowIndex The row index - this is nto actually used..?
7507 isCellEditable : function(colIndex, rowIndex){
7508 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7512 * Returns the editor defined for the cell/column.
7513 * return false or null to disable editing.
7514 * @param {Number} colIndex The column index
7515 * @param {Number} rowIndex The row index
7518 getCellEditor : function(colIndex, rowIndex){
7519 return this.config[colIndex].editor;
7523 * Sets if a column is editable.
7524 * @param {Number} col The column index
7525 * @param {Boolean} editable True if the column is editable
7527 setEditable : function(col, editable){
7528 this.config[col].editable = editable;
7533 * Returns true if the column is hidden.
7534 * @param {Number} colIndex The column index
7537 isHidden : function(colIndex){
7538 return this.config[colIndex].hidden;
7543 * Returns true if the column width cannot be changed
7545 isFixed : function(colIndex){
7546 return this.config[colIndex].fixed;
7550 * Returns true if the column can be resized
7553 isResizable : function(colIndex){
7554 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7557 * Sets if a column is hidden.
7558 * @param {Number} colIndex The column index
7559 * @param {Boolean} hidden True if the column is hidden
7561 setHidden : function(colIndex, hidden){
7562 this.config[colIndex].hidden = hidden;
7563 this.totalWidth = null;
7564 this.fireEvent("hiddenchange", this, colIndex, hidden);
7568 * Sets the editor for a column.
7569 * @param {Number} col The column index
7570 * @param {Object} editor The editor object
7572 setEditor : function(col, editor){
7573 this.config[col].editor = editor;
7577 Roo.grid.ColumnModel.defaultRenderer = function(value)
7579 if(typeof value == "object") {
7582 if(typeof value == "string" && value.length < 1){
7586 return String.format("{0}", value);
7589 // Alias for backwards compatibility
7590 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7593 * Ext JS Library 1.1.1
7594 * Copyright(c) 2006-2007, Ext JS, LLC.
7596 * Originally Released Under LGPL - original licence link has changed is not relivant.
7599 * <script type="text/javascript">
7603 * @class Roo.LoadMask
7604 * A simple utility class for generically masking elements while loading data. If the element being masked has
7605 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7606 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7607 * element's UpdateManager load indicator and will be destroyed after the initial load.
7609 * Create a new LoadMask
7610 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7611 * @param {Object} config The config object
7613 Roo.LoadMask = function(el, config){
7614 this.el = Roo.get(el);
7615 Roo.apply(this, config);
7617 this.store.on('beforeload', this.onBeforeLoad, this);
7618 this.store.on('load', this.onLoad, this);
7619 this.store.on('loadexception', this.onLoadException, this);
7620 this.removeMask = false;
7622 var um = this.el.getUpdateManager();
7623 um.showLoadIndicator = false; // disable the default indicator
7624 um.on('beforeupdate', this.onBeforeLoad, this);
7625 um.on('update', this.onLoad, this);
7626 um.on('failure', this.onLoad, this);
7627 this.removeMask = true;
7631 Roo.LoadMask.prototype = {
7633 * @cfg {Boolean} removeMask
7634 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7635 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7639 * The text to display in a centered loading message box (defaults to 'Loading...')
7643 * @cfg {String} msgCls
7644 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7646 msgCls : 'x-mask-loading',
7649 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7655 * Disables the mask to prevent it from being displayed
7657 disable : function(){
7658 this.disabled = true;
7662 * Enables the mask so that it can be displayed
7664 enable : function(){
7665 this.disabled = false;
7668 onLoadException : function()
7672 if (typeof(arguments[3]) != 'undefined') {
7673 Roo.MessageBox.alert("Error loading",arguments[3]);
7677 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7678 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7685 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7690 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7694 onBeforeLoad : function(){
7696 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7701 destroy : function(){
7703 this.store.un('beforeload', this.onBeforeLoad, this);
7704 this.store.un('load', this.onLoad, this);
7705 this.store.un('loadexception', this.onLoadException, this);
7707 var um = this.el.getUpdateManager();
7708 um.un('beforeupdate', this.onBeforeLoad, this);
7709 um.un('update', this.onLoad, this);
7710 um.un('failure', this.onLoad, this);
7721 * @class Roo.bootstrap.Table
7722 * @extends Roo.bootstrap.Component
7723 * Bootstrap Table class
7724 * @cfg {String} cls table class
7725 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7726 * @cfg {String} bgcolor Specifies the background color for a table
7727 * @cfg {Number} border Specifies whether the table cells should have borders or not
7728 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7729 * @cfg {Number} cellspacing Specifies the space between cells
7730 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7731 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7732 * @cfg {String} sortable Specifies that the table should be sortable
7733 * @cfg {String} summary Specifies a summary of the content of a table
7734 * @cfg {Number} width Specifies the width of a table
7735 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7737 * @cfg {boolean} striped Should the rows be alternative striped
7738 * @cfg {boolean} bordered Add borders to the table
7739 * @cfg {boolean} hover Add hover highlighting
7740 * @cfg {boolean} condensed Format condensed
7741 * @cfg {boolean} responsive Format condensed
7742 * @cfg {Boolean} loadMask (true|false) default false
7743 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7744 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7745 * @cfg {Boolean} rowSelection (true|false) default false
7746 * @cfg {Boolean} cellSelection (true|false) default false
7747 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7748 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7749 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7750 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7754 * Create a new Table
7755 * @param {Object} config The config object
7758 Roo.bootstrap.Table = function(config){
7759 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7764 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7765 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7766 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7767 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7769 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7771 this.sm.grid = this;
7772 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7773 this.sm = this.selModel;
7774 this.sm.xmodule = this.xmodule || false;
7777 if (this.cm && typeof(this.cm.config) == 'undefined') {
7778 this.colModel = new Roo.grid.ColumnModel(this.cm);
7779 this.cm = this.colModel;
7780 this.cm.xmodule = this.xmodule || false;
7783 this.store= Roo.factory(this.store, Roo.data);
7784 this.ds = this.store;
7785 this.ds.xmodule = this.xmodule || false;
7788 if (this.footer && this.store) {
7789 this.footer.dataSource = this.ds;
7790 this.footer = Roo.factory(this.footer);
7797 * Fires when a cell is clicked
7798 * @param {Roo.bootstrap.Table} this
7799 * @param {Roo.Element} el
7800 * @param {Number} rowIndex
7801 * @param {Number} columnIndex
7802 * @param {Roo.EventObject} e
7806 * @event celldblclick
7807 * Fires when a cell is double clicked
7808 * @param {Roo.bootstrap.Table} this
7809 * @param {Roo.Element} el
7810 * @param {Number} rowIndex
7811 * @param {Number} columnIndex
7812 * @param {Roo.EventObject} e
7814 "celldblclick" : true,
7817 * Fires when a row is clicked
7818 * @param {Roo.bootstrap.Table} this
7819 * @param {Roo.Element} el
7820 * @param {Number} rowIndex
7821 * @param {Roo.EventObject} e
7825 * @event rowdblclick
7826 * Fires when a row is double clicked
7827 * @param {Roo.bootstrap.Table} this
7828 * @param {Roo.Element} el
7829 * @param {Number} rowIndex
7830 * @param {Roo.EventObject} e
7832 "rowdblclick" : true,
7835 * Fires when a mouseover occur
7836 * @param {Roo.bootstrap.Table} this
7837 * @param {Roo.Element} el
7838 * @param {Number} rowIndex
7839 * @param {Number} columnIndex
7840 * @param {Roo.EventObject} e
7845 * Fires when a mouseout occur
7846 * @param {Roo.bootstrap.Table} this
7847 * @param {Roo.Element} el
7848 * @param {Number} rowIndex
7849 * @param {Number} columnIndex
7850 * @param {Roo.EventObject} e
7855 * Fires when a row is rendered, so you can change add a style to it.
7856 * @param {Roo.bootstrap.Table} this
7857 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7861 * @event rowsrendered
7862 * Fires when all the rows have been rendered
7863 * @param {Roo.bootstrap.Table} this
7865 'rowsrendered' : true,
7867 * @event contextmenu
7868 * The raw contextmenu event for the entire grid.
7869 * @param {Roo.EventObject} e
7871 "contextmenu" : true,
7873 * @event rowcontextmenu
7874 * Fires when a row is right clicked
7875 * @param {Roo.bootstrap.Table} this
7876 * @param {Number} rowIndex
7877 * @param {Roo.EventObject} e
7879 "rowcontextmenu" : true,
7881 * @event cellcontextmenu
7882 * Fires when a cell is right clicked
7883 * @param {Roo.bootstrap.Table} this
7884 * @param {Number} rowIndex
7885 * @param {Number} cellIndex
7886 * @param {Roo.EventObject} e
7888 "cellcontextmenu" : true,
7890 * @event headercontextmenu
7891 * Fires when a header is right clicked
7892 * @param {Roo.bootstrap.Table} this
7893 * @param {Number} columnIndex
7894 * @param {Roo.EventObject} e
7896 "headercontextmenu" : true
7900 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7926 rowSelection : false,
7927 cellSelection : false,
7930 // Roo.Element - the tbody
7932 // Roo.Element - thead element
7935 container: false, // used by gridpanel...
7941 auto_hide_footer : false,
7943 getAutoCreate : function()
7945 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7952 if (this.scrollBody) {
7953 cfg.cls += ' table-body-fixed';
7956 cfg.cls += ' table-striped';
7960 cfg.cls += ' table-hover';
7962 if (this.bordered) {
7963 cfg.cls += ' table-bordered';
7965 if (this.condensed) {
7966 cfg.cls += ' table-condensed';
7968 if (this.responsive) {
7969 cfg.cls += ' table-responsive';
7973 cfg.cls+= ' ' +this.cls;
7976 // this lot should be simplifed...
7989 ].forEach(function(k) {
7997 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8000 if(this.store || this.cm){
8001 if(this.headerShow){
8002 cfg.cn.push(this.renderHeader());
8005 cfg.cn.push(this.renderBody());
8007 if(this.footerShow){
8008 cfg.cn.push(this.renderFooter());
8010 // where does this come from?
8011 //cfg.cls+= ' TableGrid';
8014 return { cn : [ cfg ] };
8017 initEvents : function()
8019 if(!this.store || !this.cm){
8022 if (this.selModel) {
8023 this.selModel.initEvents();
8027 //Roo.log('initEvents with ds!!!!');
8029 this.mainBody = this.el.select('tbody', true).first();
8030 this.mainHead = this.el.select('thead', true).first();
8031 this.mainFoot = this.el.select('tfoot', true).first();
8037 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8038 e.on('click', _this.sort, _this);
8041 this.mainBody.on("click", this.onClick, this);
8042 this.mainBody.on("dblclick", this.onDblClick, this);
8044 // why is this done????? = it breaks dialogs??
8045 //this.parent().el.setStyle('position', 'relative');
8049 this.footer.parentId = this.id;
8050 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8053 this.el.select('tfoot tr td').first().addClass('hide');
8058 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8061 this.store.on('load', this.onLoad, this);
8062 this.store.on('beforeload', this.onBeforeLoad, this);
8063 this.store.on('update', this.onUpdate, this);
8064 this.store.on('add', this.onAdd, this);
8065 this.store.on("clear", this.clear, this);
8067 this.el.on("contextmenu", this.onContextMenu, this);
8069 this.mainBody.on('scroll', this.onBodyScroll, this);
8071 this.cm.on("headerchange", this.onHeaderChange, this);
8073 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8077 onContextMenu : function(e, t)
8079 this.processEvent("contextmenu", e);
8082 processEvent : function(name, e)
8084 if (name != 'touchstart' ) {
8085 this.fireEvent(name, e);
8088 var t = e.getTarget();
8090 var cell = Roo.get(t);
8096 if(cell.findParent('tfoot', false, true)){
8100 if(cell.findParent('thead', false, true)){
8102 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8103 cell = Roo.get(t).findParent('th', false, true);
8105 Roo.log("failed to find th in thead?");
8106 Roo.log(e.getTarget());
8111 var cellIndex = cell.dom.cellIndex;
8113 var ename = name == 'touchstart' ? 'click' : name;
8114 this.fireEvent("header" + ename, this, cellIndex, e);
8119 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8120 cell = Roo.get(t).findParent('td', false, true);
8122 Roo.log("failed to find th in tbody?");
8123 Roo.log(e.getTarget());
8128 var row = cell.findParent('tr', false, true);
8129 var cellIndex = cell.dom.cellIndex;
8130 var rowIndex = row.dom.rowIndex - 1;
8134 this.fireEvent("row" + name, this, rowIndex, e);
8138 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8144 onMouseover : function(e, el)
8146 var cell = Roo.get(el);
8152 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8153 cell = cell.findParent('td', false, true);
8156 var row = cell.findParent('tr', false, true);
8157 var cellIndex = cell.dom.cellIndex;
8158 var rowIndex = row.dom.rowIndex - 1; // start from 0
8160 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8164 onMouseout : function(e, el)
8166 var cell = Roo.get(el);
8172 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8173 cell = cell.findParent('td', false, true);
8176 var row = cell.findParent('tr', false, true);
8177 var cellIndex = cell.dom.cellIndex;
8178 var rowIndex = row.dom.rowIndex - 1; // start from 0
8180 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8184 onClick : function(e, el)
8186 var cell = Roo.get(el);
8188 if(!cell || (!this.cellSelection && !this.rowSelection)){
8192 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8193 cell = cell.findParent('td', false, true);
8196 if(!cell || typeof(cell) == 'undefined'){
8200 var row = cell.findParent('tr', false, true);
8202 if(!row || typeof(row) == 'undefined'){
8206 var cellIndex = cell.dom.cellIndex;
8207 var rowIndex = this.getRowIndex(row);
8209 // why??? - should these not be based on SelectionModel?
8210 if(this.cellSelection){
8211 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8214 if(this.rowSelection){
8215 this.fireEvent('rowclick', this, row, rowIndex, e);
8221 onDblClick : function(e,el)
8223 var cell = Roo.get(el);
8225 if(!cell || (!this.cellSelection && !this.rowSelection)){
8229 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8230 cell = cell.findParent('td', false, true);
8233 if(!cell || typeof(cell) == 'undefined'){
8237 var row = cell.findParent('tr', false, true);
8239 if(!row || typeof(row) == 'undefined'){
8243 var cellIndex = cell.dom.cellIndex;
8244 var rowIndex = this.getRowIndex(row);
8246 if(this.cellSelection){
8247 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8250 if(this.rowSelection){
8251 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8255 sort : function(e,el)
8257 var col = Roo.get(el);
8259 if(!col.hasClass('sortable')){
8263 var sort = col.attr('sort');
8266 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8270 this.store.sortInfo = {field : sort, direction : dir};
8273 Roo.log("calling footer first");
8274 this.footer.onClick('first');
8277 this.store.load({ params : { start : 0 } });
8281 renderHeader : function()
8289 this.totalWidth = 0;
8291 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8293 var config = cm.config[i];
8297 cls : 'x-hcol-' + i,
8299 html: cm.getColumnHeader(i)
8304 if(typeof(config.sortable) != 'undefined' && config.sortable){
8306 c.html = '<i class="glyphicon"></i>' + c.html;
8309 // could use BS4 hidden-..-down
8311 if(typeof(config.lgHeader) != 'undefined'){
8312 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8315 if(typeof(config.mdHeader) != 'undefined'){
8316 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8319 if(typeof(config.smHeader) != 'undefined'){
8320 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8323 if(typeof(config.xsHeader) != 'undefined'){
8324 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8331 if(typeof(config.tooltip) != 'undefined'){
8332 c.tooltip = config.tooltip;
8335 if(typeof(config.colspan) != 'undefined'){
8336 c.colspan = config.colspan;
8339 if(typeof(config.hidden) != 'undefined' && config.hidden){
8340 c.style += ' display:none;';
8343 if(typeof(config.dataIndex) != 'undefined'){
8344 c.sort = config.dataIndex;
8349 if(typeof(config.align) != 'undefined' && config.align.length){
8350 c.style += ' text-align:' + config.align + ';';
8353 if(typeof(config.width) != 'undefined'){
8354 c.style += ' width:' + config.width + 'px;';
8355 this.totalWidth += config.width;
8357 this.totalWidth += 100; // assume minimum of 100 per column?
8360 if(typeof(config.cls) != 'undefined'){
8361 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8364 ['xs','sm','md','lg'].map(function(size){
8366 if(typeof(config[size]) == 'undefined'){
8370 if (!config[size]) { // 0 = hidden
8371 // BS 4 '0' is treated as hide that column and below.
8372 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8376 c.cls += ' col-' + size + '-' + config[size] + (
8377 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8389 renderBody : function()
8399 colspan : this.cm.getColumnCount()
8409 renderFooter : function()
8419 colspan : this.cm.getColumnCount()
8433 // Roo.log('ds onload');
8438 var ds = this.store;
8440 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8441 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8442 if (_this.store.sortInfo) {
8444 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8445 e.select('i', true).addClass(['glyphicon-arrow-up']);
8448 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8449 e.select('i', true).addClass(['glyphicon-arrow-down']);
8454 var tbody = this.mainBody;
8456 if(ds.getCount() > 0){
8457 ds.data.each(function(d,rowIndex){
8458 var row = this.renderRow(cm, ds, rowIndex);
8460 tbody.createChild(row);
8464 if(row.cellObjects.length){
8465 Roo.each(row.cellObjects, function(r){
8466 _this.renderCellObject(r);
8473 var tfoot = this.el.select('tfoot', true).first();
8475 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8477 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8479 var total = this.ds.getTotalCount();
8481 if(this.footer.pageSize < total){
8482 this.mainFoot.show();
8486 Roo.each(this.el.select('tbody td', true).elements, function(e){
8487 e.on('mouseover', _this.onMouseover, _this);
8490 Roo.each(this.el.select('tbody td', true).elements, function(e){
8491 e.on('mouseout', _this.onMouseout, _this);
8493 this.fireEvent('rowsrendered', this);
8499 onUpdate : function(ds,record)
8501 this.refreshRow(record);
8505 onRemove : function(ds, record, index, isUpdate){
8506 if(isUpdate !== true){
8507 this.fireEvent("beforerowremoved", this, index, record);
8509 var bt = this.mainBody.dom;
8511 var rows = this.el.select('tbody > tr', true).elements;
8513 if(typeof(rows[index]) != 'undefined'){
8514 bt.removeChild(rows[index].dom);
8517 // if(bt.rows[index]){
8518 // bt.removeChild(bt.rows[index]);
8521 if(isUpdate !== true){
8522 //this.stripeRows(index);
8523 //this.syncRowHeights(index, index);
8525 this.fireEvent("rowremoved", this, index, record);
8529 onAdd : function(ds, records, rowIndex)
8531 //Roo.log('on Add called');
8532 // - note this does not handle multiple adding very well..
8533 var bt = this.mainBody.dom;
8534 for (var i =0 ; i < records.length;i++) {
8535 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8536 //Roo.log(records[i]);
8537 //Roo.log(this.store.getAt(rowIndex+i));
8538 this.insertRow(this.store, rowIndex + i, false);
8545 refreshRow : function(record){
8546 var ds = this.store, index;
8547 if(typeof record == 'number'){
8549 record = ds.getAt(index);
8551 index = ds.indexOf(record);
8553 return; // should not happen - but seems to
8556 this.insertRow(ds, index, true);
8558 this.onRemove(ds, record, index+1, true);
8560 //this.syncRowHeights(index, index);
8562 this.fireEvent("rowupdated", this, index, record);
8565 insertRow : function(dm, rowIndex, isUpdate){
8568 this.fireEvent("beforerowsinserted", this, rowIndex);
8570 //var s = this.getScrollState();
8571 var row = this.renderRow(this.cm, this.store, rowIndex);
8572 // insert before rowIndex..
8573 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8577 if(row.cellObjects.length){
8578 Roo.each(row.cellObjects, function(r){
8579 _this.renderCellObject(r);
8584 this.fireEvent("rowsinserted", this, rowIndex);
8585 //this.syncRowHeights(firstRow, lastRow);
8586 //this.stripeRows(firstRow);
8593 getRowDom : function(rowIndex)
8595 var rows = this.el.select('tbody > tr', true).elements;
8597 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8600 // returns the object tree for a tr..
8603 renderRow : function(cm, ds, rowIndex)
8605 var d = ds.getAt(rowIndex);
8609 cls : 'x-row-' + rowIndex,
8613 var cellObjects = [];
8615 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8616 var config = cm.config[i];
8618 var renderer = cm.getRenderer(i);
8622 if(typeof(renderer) !== 'undefined'){
8623 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8625 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8626 // and are rendered into the cells after the row is rendered - using the id for the element.
8628 if(typeof(value) === 'object'){
8638 rowIndex : rowIndex,
8643 this.fireEvent('rowclass', this, rowcfg);
8647 cls : rowcfg.rowClass + ' x-col-' + i,
8649 html: (typeof(value) === 'object') ? '' : value
8656 if(typeof(config.colspan) != 'undefined'){
8657 td.colspan = config.colspan;
8660 if(typeof(config.hidden) != 'undefined' && config.hidden){
8661 td.style += ' display:none;';
8664 if(typeof(config.align) != 'undefined' && config.align.length){
8665 td.style += ' text-align:' + config.align + ';';
8667 if(typeof(config.valign) != 'undefined' && config.valign.length){
8668 td.style += ' vertical-align:' + config.valign + ';';
8671 if(typeof(config.width) != 'undefined'){
8672 td.style += ' width:' + config.width + 'px;';
8675 if(typeof(config.cursor) != 'undefined'){
8676 td.style += ' cursor:' + config.cursor + ';';
8679 if(typeof(config.cls) != 'undefined'){
8680 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8683 ['xs','sm','md','lg'].map(function(size){
8685 if(typeof(config[size]) == 'undefined'){
8691 if (!config[size]) { // 0 = hidden
8692 // BS 4 '0' is treated as hide that column and below.
8693 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8697 td.cls += ' col-' + size + '-' + config[size] + (
8698 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8708 row.cellObjects = cellObjects;
8716 onBeforeLoad : function()
8725 this.el.select('tbody', true).first().dom.innerHTML = '';
8728 * Show or hide a row.
8729 * @param {Number} rowIndex to show or hide
8730 * @param {Boolean} state hide
8732 setRowVisibility : function(rowIndex, state)
8734 var bt = this.mainBody.dom;
8736 var rows = this.el.select('tbody > tr', true).elements;
8738 if(typeof(rows[rowIndex]) == 'undefined'){
8741 rows[rowIndex].dom.style.display = state ? '' : 'none';
8745 getSelectionModel : function(){
8747 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8749 return this.selModel;
8752 * Render the Roo.bootstrap object from renderder
8754 renderCellObject : function(r)
8758 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8760 var t = r.cfg.render(r.container);
8763 Roo.each(r.cfg.cn, function(c){
8765 container: t.getChildContainer(),
8768 _this.renderCellObject(child);
8773 getRowIndex : function(row)
8777 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8788 * Returns the grid's underlying element = used by panel.Grid
8789 * @return {Element} The element
8791 getGridEl : function(){
8795 * Forces a resize - used by panel.Grid
8796 * @return {Element} The element
8798 autoSize : function()
8800 //var ctr = Roo.get(this.container.dom.parentElement);
8801 var ctr = Roo.get(this.el.dom);
8803 var thd = this.getGridEl().select('thead',true).first();
8804 var tbd = this.getGridEl().select('tbody', true).first();
8805 var tfd = this.getGridEl().select('tfoot', true).first();
8807 var cw = ctr.getWidth();
8808 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8812 tbd.setWidth(ctr.getWidth());
8813 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8814 // this needs fixing for various usage - currently only hydra job advers I think..
8816 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8818 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8821 cw = Math.max(cw, this.totalWidth);
8822 this.getGridEl().select('tbody tr',true).setWidth(cw);
8824 // resize 'expandable coloumn?
8826 return; // we doe not have a view in this design..
8829 onBodyScroll: function()
8831 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8833 this.mainHead.setStyle({
8834 'position' : 'relative',
8835 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8841 var scrollHeight = this.mainBody.dom.scrollHeight;
8843 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8845 var height = this.mainBody.getHeight();
8847 if(scrollHeight - height == scrollTop) {
8849 var total = this.ds.getTotalCount();
8851 if(this.footer.cursor + this.footer.pageSize < total){
8853 this.footer.ds.load({
8855 start : this.footer.cursor + this.footer.pageSize,
8856 limit : this.footer.pageSize
8866 onHeaderChange : function()
8868 var header = this.renderHeader();
8869 var table = this.el.select('table', true).first();
8871 this.mainHead.remove();
8872 this.mainHead = table.createChild(header, this.mainBody, false);
8875 onHiddenChange : function(colModel, colIndex, hidden)
8877 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8878 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8880 this.CSS.updateRule(thSelector, "display", "");
8881 this.CSS.updateRule(tdSelector, "display", "");
8884 this.CSS.updateRule(thSelector, "display", "none");
8885 this.CSS.updateRule(tdSelector, "display", "none");
8888 this.onHeaderChange();
8892 setColumnWidth: function(col_index, width)
8894 // width = "md-2 xs-2..."
8895 if(!this.colModel.config[col_index]) {
8899 var w = width.split(" ");
8901 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8903 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8906 for(var j = 0; j < w.length; j++) {
8912 var size_cls = w[j].split("-");
8914 if(!Number.isInteger(size_cls[1] * 1)) {
8918 if(!this.colModel.config[col_index][size_cls[0]]) {
8922 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8926 h_row[0].classList.replace(
8927 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8928 "col-"+size_cls[0]+"-"+size_cls[1]
8931 for(var i = 0; i < rows.length; i++) {
8933 var size_cls = w[j].split("-");
8935 if(!Number.isInteger(size_cls[1] * 1)) {
8939 if(!this.colModel.config[col_index][size_cls[0]]) {
8943 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8947 rows[i].classList.replace(
8948 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8949 "col-"+size_cls[0]+"-"+size_cls[1]
8953 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8968 * @class Roo.bootstrap.TableCell
8969 * @extends Roo.bootstrap.Component
8970 * Bootstrap TableCell class
8971 * @cfg {String} html cell contain text
8972 * @cfg {String} cls cell class
8973 * @cfg {String} tag cell tag (td|th) default td
8974 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8975 * @cfg {String} align Aligns the content in a cell
8976 * @cfg {String} axis Categorizes cells
8977 * @cfg {String} bgcolor Specifies the background color of a cell
8978 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8979 * @cfg {Number} colspan Specifies the number of columns a cell should span
8980 * @cfg {String} headers Specifies one or more header cells a cell is related to
8981 * @cfg {Number} height Sets the height of a cell
8982 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8983 * @cfg {Number} rowspan Sets the number of rows a cell should span
8984 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8985 * @cfg {String} valign Vertical aligns the content in a cell
8986 * @cfg {Number} width Specifies the width of a cell
8989 * Create a new TableCell
8990 * @param {Object} config The config object
8993 Roo.bootstrap.TableCell = function(config){
8994 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8997 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9017 getAutoCreate : function(){
9018 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9038 cfg.align=this.align
9044 cfg.bgcolor=this.bgcolor
9047 cfg.charoff=this.charoff
9050 cfg.colspan=this.colspan
9053 cfg.headers=this.headers
9056 cfg.height=this.height
9059 cfg.nowrap=this.nowrap
9062 cfg.rowspan=this.rowspan
9065 cfg.scope=this.scope
9068 cfg.valign=this.valign
9071 cfg.width=this.width
9090 * @class Roo.bootstrap.TableRow
9091 * @extends Roo.bootstrap.Component
9092 * Bootstrap TableRow class
9093 * @cfg {String} cls row class
9094 * @cfg {String} align Aligns the content in a table row
9095 * @cfg {String} bgcolor Specifies a background color for a table row
9096 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9097 * @cfg {String} valign Vertical aligns the content in a table row
9100 * Create a new TableRow
9101 * @param {Object} config The config object
9104 Roo.bootstrap.TableRow = function(config){
9105 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9108 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9116 getAutoCreate : function(){
9117 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9127 cfg.align = this.align;
9130 cfg.bgcolor = this.bgcolor;
9133 cfg.charoff = this.charoff;
9136 cfg.valign = this.valign;
9154 * @class Roo.bootstrap.TableBody
9155 * @extends Roo.bootstrap.Component
9156 * Bootstrap TableBody class
9157 * @cfg {String} cls element class
9158 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9159 * @cfg {String} align Aligns the content inside the element
9160 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9161 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9164 * Create a new TableBody
9165 * @param {Object} config The config object
9168 Roo.bootstrap.TableBody = function(config){
9169 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9172 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9180 getAutoCreate : function(){
9181 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9195 cfg.align = this.align;
9198 cfg.charoff = this.charoff;
9201 cfg.valign = this.valign;
9208 // initEvents : function()
9215 // this.store = Roo.factory(this.store, Roo.data);
9216 // this.store.on('load', this.onLoad, this);
9218 // this.store.load();
9222 // onLoad: function ()
9224 // this.fireEvent('load', this);
9234 * Ext JS Library 1.1.1
9235 * Copyright(c) 2006-2007, Ext JS, LLC.
9237 * Originally Released Under LGPL - original licence link has changed is not relivant.
9240 * <script type="text/javascript">
9243 // as we use this in bootstrap.
9244 Roo.namespace('Roo.form');
9246 * @class Roo.form.Action
9247 * Internal Class used to handle form actions
9249 * @param {Roo.form.BasicForm} el The form element or its id
9250 * @param {Object} config Configuration options
9255 // define the action interface
9256 Roo.form.Action = function(form, options){
9258 this.options = options || {};
9261 * Client Validation Failed
9264 Roo.form.Action.CLIENT_INVALID = 'client';
9266 * Server Validation Failed
9269 Roo.form.Action.SERVER_INVALID = 'server';
9271 * Connect to Server Failed
9274 Roo.form.Action.CONNECT_FAILURE = 'connect';
9276 * Reading Data from Server Failed
9279 Roo.form.Action.LOAD_FAILURE = 'load';
9281 Roo.form.Action.prototype = {
9283 failureType : undefined,
9284 response : undefined,
9288 run : function(options){
9293 success : function(response){
9298 handleResponse : function(response){
9302 // default connection failure
9303 failure : function(response){
9305 this.response = response;
9306 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9307 this.form.afterAction(this, false);
9310 processResponse : function(response){
9311 this.response = response;
9312 if(!response.responseText){
9315 this.result = this.handleResponse(response);
9319 // utility functions used internally
9320 getUrl : function(appendParams){
9321 var url = this.options.url || this.form.url || this.form.el.dom.action;
9323 var p = this.getParams();
9325 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9331 getMethod : function(){
9332 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9335 getParams : function(){
9336 var bp = this.form.baseParams;
9337 var p = this.options.params;
9339 if(typeof p == "object"){
9340 p = Roo.urlEncode(Roo.applyIf(p, bp));
9341 }else if(typeof p == 'string' && bp){
9342 p += '&' + Roo.urlEncode(bp);
9345 p = Roo.urlEncode(bp);
9350 createCallback : function(){
9352 success: this.success,
9353 failure: this.failure,
9355 timeout: (this.form.timeout*1000),
9356 upload: this.form.fileUpload ? this.success : undefined
9361 Roo.form.Action.Submit = function(form, options){
9362 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9365 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9368 haveProgress : false,
9369 uploadComplete : false,
9371 // uploadProgress indicator.
9372 uploadProgress : function()
9374 if (!this.form.progressUrl) {
9378 if (!this.haveProgress) {
9379 Roo.MessageBox.progress("Uploading", "Uploading");
9381 if (this.uploadComplete) {
9382 Roo.MessageBox.hide();
9386 this.haveProgress = true;
9388 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9390 var c = new Roo.data.Connection();
9392 url : this.form.progressUrl,
9397 success : function(req){
9398 //console.log(data);
9402 rdata = Roo.decode(req.responseText)
9404 Roo.log("Invalid data from server..");
9408 if (!rdata || !rdata.success) {
9410 Roo.MessageBox.alert(Roo.encode(rdata));
9413 var data = rdata.data;
9415 if (this.uploadComplete) {
9416 Roo.MessageBox.hide();
9421 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9422 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9425 this.uploadProgress.defer(2000,this);
9428 failure: function(data) {
9429 Roo.log('progress url failed ');
9440 // run get Values on the form, so it syncs any secondary forms.
9441 this.form.getValues();
9443 var o = this.options;
9444 var method = this.getMethod();
9445 var isPost = method == 'POST';
9446 if(o.clientValidation === false || this.form.isValid()){
9448 if (this.form.progressUrl) {
9449 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9450 (new Date() * 1) + '' + Math.random());
9455 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9456 form:this.form.el.dom,
9457 url:this.getUrl(!isPost),
9459 params:isPost ? this.getParams() : null,
9460 isUpload: this.form.fileUpload,
9461 formData : this.form.formData
9464 this.uploadProgress();
9466 }else if (o.clientValidation !== false){ // client validation failed
9467 this.failureType = Roo.form.Action.CLIENT_INVALID;
9468 this.form.afterAction(this, false);
9472 success : function(response)
9474 this.uploadComplete= true;
9475 if (this.haveProgress) {
9476 Roo.MessageBox.hide();
9480 var result = this.processResponse(response);
9481 if(result === true || result.success){
9482 this.form.afterAction(this, true);
9486 this.form.markInvalid(result.errors);
9487 this.failureType = Roo.form.Action.SERVER_INVALID;
9489 this.form.afterAction(this, false);
9491 failure : function(response)
9493 this.uploadComplete= true;
9494 if (this.haveProgress) {
9495 Roo.MessageBox.hide();
9498 this.response = response;
9499 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9500 this.form.afterAction(this, false);
9503 handleResponse : function(response){
9504 if(this.form.errorReader){
9505 var rs = this.form.errorReader.read(response);
9508 for(var i = 0, len = rs.records.length; i < len; i++) {
9509 var r = rs.records[i];
9513 if(errors.length < 1){
9517 success : rs.success,
9523 ret = Roo.decode(response.responseText);
9527 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9537 Roo.form.Action.Load = function(form, options){
9538 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9539 this.reader = this.form.reader;
9542 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9547 Roo.Ajax.request(Roo.apply(
9548 this.createCallback(), {
9549 method:this.getMethod(),
9550 url:this.getUrl(false),
9551 params:this.getParams()
9555 success : function(response){
9557 var result = this.processResponse(response);
9558 if(result === true || !result.success || !result.data){
9559 this.failureType = Roo.form.Action.LOAD_FAILURE;
9560 this.form.afterAction(this, false);
9563 this.form.clearInvalid();
9564 this.form.setValues(result.data);
9565 this.form.afterAction(this, true);
9568 handleResponse : function(response){
9569 if(this.form.reader){
9570 var rs = this.form.reader.read(response);
9571 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9573 success : rs.success,
9577 return Roo.decode(response.responseText);
9581 Roo.form.Action.ACTION_TYPES = {
9582 'load' : Roo.form.Action.Load,
9583 'submit' : Roo.form.Action.Submit
9592 * @class Roo.bootstrap.Form
9593 * @extends Roo.bootstrap.Component
9594 * Bootstrap Form class
9595 * @cfg {String} method GET | POST (default POST)
9596 * @cfg {String} labelAlign top | left (default top)
9597 * @cfg {String} align left | right - for navbars
9598 * @cfg {Boolean} loadMask load mask when submit (default true)
9603 * @param {Object} config The config object
9607 Roo.bootstrap.Form = function(config){
9609 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9611 Roo.bootstrap.Form.popover.apply();
9615 * @event clientvalidation
9616 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9617 * @param {Form} this
9618 * @param {Boolean} valid true if the form has passed client-side validation
9620 clientvalidation: true,
9622 * @event beforeaction
9623 * Fires before any action is performed. Return false to cancel the action.
9624 * @param {Form} this
9625 * @param {Action} action The action to be performed
9629 * @event actionfailed
9630 * Fires when an action fails.
9631 * @param {Form} this
9632 * @param {Action} action The action that failed
9634 actionfailed : true,
9636 * @event actioncomplete
9637 * Fires when an action is completed.
9638 * @param {Form} this
9639 * @param {Action} action The action that completed
9641 actioncomplete : true
9645 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9648 * @cfg {String} method
9649 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9654 * The URL to use for form actions if one isn't supplied in the action options.
9657 * @cfg {Boolean} fileUpload
9658 * Set to true if this form is a file upload.
9662 * @cfg {Object} baseParams
9663 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9667 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9671 * @cfg {Sting} align (left|right) for navbar forms
9676 activeAction : null,
9679 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9680 * element by passing it or its id or mask the form itself by passing in true.
9683 waitMsgTarget : false,
9688 * @cfg {Boolean} errorMask (true|false) default false
9693 * @cfg {Number} maskOffset Default 100
9698 * @cfg {Boolean} maskBody
9702 getAutoCreate : function(){
9706 method : this.method || 'POST',
9707 id : this.id || Roo.id(),
9710 if (this.parent().xtype.match(/^Nav/)) {
9711 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9715 if (this.labelAlign == 'left' ) {
9716 cfg.cls += ' form-horizontal';
9722 initEvents : function()
9724 this.el.on('submit', this.onSubmit, this);
9725 // this was added as random key presses on the form where triggering form submit.
9726 this.el.on('keypress', function(e) {
9727 if (e.getCharCode() != 13) {
9730 // we might need to allow it for textareas.. and some other items.
9731 // check e.getTarget().
9733 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9737 Roo.log("keypress blocked");
9745 onSubmit : function(e){
9750 * Returns true if client-side validation on the form is successful.
9753 isValid : function(){
9754 var items = this.getItems();
9758 items.each(function(f){
9764 Roo.log('invalid field: ' + f.name);
9768 if(!target && f.el.isVisible(true)){
9774 if(this.errorMask && !valid){
9775 Roo.bootstrap.Form.popover.mask(this, target);
9782 * Returns true if any fields in this form have changed since their original load.
9785 isDirty : function(){
9787 var items = this.getItems();
9788 items.each(function(f){
9798 * Performs a predefined action (submit or load) or custom actions you define on this form.
9799 * @param {String} actionName The name of the action type
9800 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9801 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9802 * accept other config options):
9804 Property Type Description
9805 ---------------- --------------- ----------------------------------------------------------------------------------
9806 url String The url for the action (defaults to the form's url)
9807 method String The form method to use (defaults to the form's method, or POST if not defined)
9808 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9809 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9810 validate the form on the client (defaults to false)
9812 * @return {BasicForm} this
9814 doAction : function(action, options){
9815 if(typeof action == 'string'){
9816 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9818 if(this.fireEvent('beforeaction', this, action) !== false){
9819 this.beforeAction(action);
9820 action.run.defer(100, action);
9826 beforeAction : function(action){
9827 var o = action.options;
9832 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9834 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9837 // not really supported yet.. ??
9839 //if(this.waitMsgTarget === true){
9840 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9841 //}else if(this.waitMsgTarget){
9842 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9843 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9845 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9851 afterAction : function(action, success){
9852 this.activeAction = null;
9853 var o = action.options;
9858 Roo.get(document.body).unmask();
9864 //if(this.waitMsgTarget === true){
9865 // this.el.unmask();
9866 //}else if(this.waitMsgTarget){
9867 // this.waitMsgTarget.unmask();
9869 // Roo.MessageBox.updateProgress(1);
9870 // Roo.MessageBox.hide();
9877 Roo.callback(o.success, o.scope, [this, action]);
9878 this.fireEvent('actioncomplete', this, action);
9882 // failure condition..
9883 // we have a scenario where updates need confirming.
9884 // eg. if a locking scenario exists..
9885 // we look for { errors : { needs_confirm : true }} in the response.
9887 (typeof(action.result) != 'undefined') &&
9888 (typeof(action.result.errors) != 'undefined') &&
9889 (typeof(action.result.errors.needs_confirm) != 'undefined')
9892 Roo.log("not supported yet");
9895 Roo.MessageBox.confirm(
9896 "Change requires confirmation",
9897 action.result.errorMsg,
9902 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9912 Roo.callback(o.failure, o.scope, [this, action]);
9913 // show an error message if no failed handler is set..
9914 if (!this.hasListener('actionfailed')) {
9915 Roo.log("need to add dialog support");
9917 Roo.MessageBox.alert("Error",
9918 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9919 action.result.errorMsg :
9920 "Saving Failed, please check your entries or try again"
9925 this.fireEvent('actionfailed', this, action);
9930 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9931 * @param {String} id The value to search for
9934 findField : function(id){
9935 var items = this.getItems();
9936 var field = items.get(id);
9938 items.each(function(f){
9939 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9946 return field || null;
9949 * Mark fields in this form invalid in bulk.
9950 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9951 * @return {BasicForm} this
9953 markInvalid : function(errors){
9954 if(errors instanceof Array){
9955 for(var i = 0, len = errors.length; i < len; i++){
9956 var fieldError = errors[i];
9957 var f = this.findField(fieldError.id);
9959 f.markInvalid(fieldError.msg);
9965 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9966 field.markInvalid(errors[id]);
9970 //Roo.each(this.childForms || [], function (f) {
9971 // f.markInvalid(errors);
9978 * Set values for fields in this form in bulk.
9979 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9980 * @return {BasicForm} this
9982 setValues : function(values){
9983 if(values instanceof Array){ // array of objects
9984 for(var i = 0, len = values.length; i < len; i++){
9986 var f = this.findField(v.id);
9988 f.setValue(v.value);
9989 if(this.trackResetOnLoad){
9990 f.originalValue = f.getValue();
9994 }else{ // object hash
9997 if(typeof values[id] != 'function' && (field = this.findField(id))){
9999 if (field.setFromData &&
10000 field.valueField &&
10001 field.displayField &&
10002 // combos' with local stores can
10003 // be queried via setValue()
10004 // to set their value..
10005 (field.store && !field.store.isLocal)
10009 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10010 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10011 field.setFromData(sd);
10013 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10015 field.setFromData(values);
10018 field.setValue(values[id]);
10022 if(this.trackResetOnLoad){
10023 field.originalValue = field.getValue();
10029 //Roo.each(this.childForms || [], function (f) {
10030 // f.setValues(values);
10037 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10038 * they are returned as an array.
10039 * @param {Boolean} asString
10042 getValues : function(asString){
10043 //if (this.childForms) {
10044 // copy values from the child forms
10045 // Roo.each(this.childForms, function (f) {
10046 // this.setValues(f.getValues());
10052 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10053 if(asString === true){
10056 return Roo.urlDecode(fs);
10060 * Returns the fields in this form as an object with key/value pairs.
10061 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10064 getFieldValues : function(with_hidden)
10066 var items = this.getItems();
10068 items.each(function(f){
10070 if (!f.getName()) {
10074 var v = f.getValue();
10076 if (f.inputType =='radio') {
10077 if (typeof(ret[f.getName()]) == 'undefined') {
10078 ret[f.getName()] = ''; // empty..
10081 if (!f.el.dom.checked) {
10085 v = f.el.dom.value;
10089 if(f.xtype == 'MoneyField'){
10090 ret[f.currencyName] = f.getCurrency();
10093 // not sure if this supported any more..
10094 if ((typeof(v) == 'object') && f.getRawValue) {
10095 v = f.getRawValue() ; // dates..
10097 // combo boxes where name != hiddenName...
10098 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10099 ret[f.name] = f.getRawValue();
10101 ret[f.getName()] = v;
10108 * Clears all invalid messages in this form.
10109 * @return {BasicForm} this
10111 clearInvalid : function(){
10112 var items = this.getItems();
10114 items.each(function(f){
10122 * Resets this form.
10123 * @return {BasicForm} this
10125 reset : function(){
10126 var items = this.getItems();
10127 items.each(function(f){
10131 Roo.each(this.childForms || [], function (f) {
10139 getItems : function()
10141 var r=new Roo.util.MixedCollection(false, function(o){
10142 return o.id || (o.id = Roo.id());
10144 var iter = function(el) {
10151 Roo.each(el.items,function(e) {
10160 hideFields : function(items)
10162 Roo.each(items, function(i){
10164 var f = this.findField(i);
10175 showFields : function(items)
10177 Roo.each(items, function(i){
10179 var f = this.findField(i);
10192 Roo.apply(Roo.bootstrap.Form, {
10208 intervalID : false,
10214 if(this.isApplied){
10219 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10220 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10221 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10222 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10225 this.maskEl.top.enableDisplayMode("block");
10226 this.maskEl.left.enableDisplayMode("block");
10227 this.maskEl.bottom.enableDisplayMode("block");
10228 this.maskEl.right.enableDisplayMode("block");
10230 this.toolTip = new Roo.bootstrap.Tooltip({
10231 cls : 'roo-form-error-popover',
10233 'left' : ['r-l', [-2,0], 'right'],
10234 'right' : ['l-r', [2,0], 'left'],
10235 'bottom' : ['tl-bl', [0,2], 'top'],
10236 'top' : [ 'bl-tl', [0,-2], 'bottom']
10240 this.toolTip.render(Roo.get(document.body));
10242 this.toolTip.el.enableDisplayMode("block");
10244 Roo.get(document.body).on('click', function(){
10248 Roo.get(document.body).on('touchstart', function(){
10252 this.isApplied = true
10255 mask : function(form, target)
10259 this.target = target;
10261 if(!this.form.errorMask || !target.el){
10265 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10267 Roo.log(scrollable);
10269 var ot = this.target.el.calcOffsetsTo(scrollable);
10271 var scrollTo = ot[1] - this.form.maskOffset;
10273 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10275 scrollable.scrollTo('top', scrollTo);
10277 var box = this.target.el.getBox();
10279 var zIndex = Roo.bootstrap.Modal.zIndex++;
10282 this.maskEl.top.setStyle('position', 'absolute');
10283 this.maskEl.top.setStyle('z-index', zIndex);
10284 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10285 this.maskEl.top.setLeft(0);
10286 this.maskEl.top.setTop(0);
10287 this.maskEl.top.show();
10289 this.maskEl.left.setStyle('position', 'absolute');
10290 this.maskEl.left.setStyle('z-index', zIndex);
10291 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10292 this.maskEl.left.setLeft(0);
10293 this.maskEl.left.setTop(box.y - this.padding);
10294 this.maskEl.left.show();
10296 this.maskEl.bottom.setStyle('position', 'absolute');
10297 this.maskEl.bottom.setStyle('z-index', zIndex);
10298 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10299 this.maskEl.bottom.setLeft(0);
10300 this.maskEl.bottom.setTop(box.bottom + this.padding);
10301 this.maskEl.bottom.show();
10303 this.maskEl.right.setStyle('position', 'absolute');
10304 this.maskEl.right.setStyle('z-index', zIndex);
10305 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10306 this.maskEl.right.setLeft(box.right + this.padding);
10307 this.maskEl.right.setTop(box.y - this.padding);
10308 this.maskEl.right.show();
10310 this.toolTip.bindEl = this.target.el;
10312 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10314 var tip = this.target.blankText;
10316 if(this.target.getValue() !== '' ) {
10318 if (this.target.invalidText.length) {
10319 tip = this.target.invalidText;
10320 } else if (this.target.regexText.length){
10321 tip = this.target.regexText;
10325 this.toolTip.show(tip);
10327 this.intervalID = window.setInterval(function() {
10328 Roo.bootstrap.Form.popover.unmask();
10331 window.onwheel = function(){ return false;};
10333 (function(){ this.isMasked = true; }).defer(500, this);
10337 unmask : function()
10339 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10343 this.maskEl.top.setStyle('position', 'absolute');
10344 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10345 this.maskEl.top.hide();
10347 this.maskEl.left.setStyle('position', 'absolute');
10348 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10349 this.maskEl.left.hide();
10351 this.maskEl.bottom.setStyle('position', 'absolute');
10352 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10353 this.maskEl.bottom.hide();
10355 this.maskEl.right.setStyle('position', 'absolute');
10356 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10357 this.maskEl.right.hide();
10359 this.toolTip.hide();
10361 this.toolTip.el.hide();
10363 window.onwheel = function(){ return true;};
10365 if(this.intervalID){
10366 window.clearInterval(this.intervalID);
10367 this.intervalID = false;
10370 this.isMasked = false;
10380 * Ext JS Library 1.1.1
10381 * Copyright(c) 2006-2007, Ext JS, LLC.
10383 * Originally Released Under LGPL - original licence link has changed is not relivant.
10386 * <script type="text/javascript">
10389 * @class Roo.form.VTypes
10390 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10393 Roo.form.VTypes = function(){
10394 // closure these in so they are only created once.
10395 var alpha = /^[a-zA-Z_]+$/;
10396 var alphanum = /^[a-zA-Z0-9_]+$/;
10397 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10398 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10400 // All these messages and functions are configurable
10403 * The function used to validate email addresses
10404 * @param {String} value The email address
10406 'email' : function(v){
10407 return email.test(v);
10410 * The error text to display when the email validation function returns false
10413 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10415 * The keystroke filter mask to be applied on email input
10418 'emailMask' : /[a-z0-9_\.\-@]/i,
10421 * The function used to validate URLs
10422 * @param {String} value The URL
10424 'url' : function(v){
10425 return url.test(v);
10428 * The error text to display when the url validation function returns false
10431 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10434 * The function used to validate alpha values
10435 * @param {String} value The value
10437 'alpha' : function(v){
10438 return alpha.test(v);
10441 * The error text to display when the alpha validation function returns false
10444 'alphaText' : 'This field should only contain letters and _',
10446 * The keystroke filter mask to be applied on alpha input
10449 'alphaMask' : /[a-z_]/i,
10452 * The function used to validate alphanumeric values
10453 * @param {String} value The value
10455 'alphanum' : function(v){
10456 return alphanum.test(v);
10459 * The error text to display when the alphanumeric validation function returns false
10462 'alphanumText' : 'This field should only contain letters, numbers and _',
10464 * The keystroke filter mask to be applied on alphanumeric input
10467 'alphanumMask' : /[a-z0-9_]/i
10477 * @class Roo.bootstrap.Input
10478 * @extends Roo.bootstrap.Component
10479 * Bootstrap Input class
10480 * @cfg {Boolean} disabled is it disabled
10481 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10482 * @cfg {String} name name of the input
10483 * @cfg {string} fieldLabel - the label associated
10484 * @cfg {string} placeholder - placeholder to put in text.
10485 * @cfg {string} before - input group add on before
10486 * @cfg {string} after - input group add on after
10487 * @cfg {string} size - (lg|sm) or leave empty..
10488 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10489 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10490 * @cfg {Number} md colspan out of 12 for computer-sized screens
10491 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10492 * @cfg {string} value default value of the input
10493 * @cfg {Number} labelWidth set the width of label
10494 * @cfg {Number} labellg set the width of label (1-12)
10495 * @cfg {Number} labelmd set the width of label (1-12)
10496 * @cfg {Number} labelsm set the width of label (1-12)
10497 * @cfg {Number} labelxs set the width of label (1-12)
10498 * @cfg {String} labelAlign (top|left)
10499 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10500 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10501 * @cfg {String} indicatorpos (left|right) default left
10502 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10503 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10504 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10506 * @cfg {String} align (left|center|right) Default left
10507 * @cfg {Boolean} forceFeedback (true|false) Default false
10510 * Create a new Input
10511 * @param {Object} config The config object
10514 Roo.bootstrap.Input = function(config){
10516 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10521 * Fires when this field receives input focus.
10522 * @param {Roo.form.Field} this
10527 * Fires when this field loses input focus.
10528 * @param {Roo.form.Field} this
10532 * @event specialkey
10533 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10534 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10535 * @param {Roo.form.Field} this
10536 * @param {Roo.EventObject} e The event object
10541 * Fires just before the field blurs if the field value has changed.
10542 * @param {Roo.form.Field} this
10543 * @param {Mixed} newValue The new value
10544 * @param {Mixed} oldValue The original value
10549 * Fires after the field has been marked as invalid.
10550 * @param {Roo.form.Field} this
10551 * @param {String} msg The validation message
10556 * Fires after the field has been validated with no errors.
10557 * @param {Roo.form.Field} this
10562 * Fires after the key up
10563 * @param {Roo.form.Field} this
10564 * @param {Roo.EventObject} e The event Object
10569 * Fires after the user pastes into input
10570 * @param {Roo.form.Field} this
10571 * @param {Roo.EventObject} e The event Object
10577 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10579 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10580 automatic validation (defaults to "keyup").
10582 validationEvent : "keyup",
10584 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10586 validateOnBlur : true,
10588 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10590 validationDelay : 250,
10592 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10594 focusClass : "x-form-focus", // not needed???
10598 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10600 invalidClass : "has-warning",
10603 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10605 validClass : "has-success",
10608 * @cfg {Boolean} hasFeedback (true|false) default true
10610 hasFeedback : true,
10613 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10615 invalidFeedbackClass : "glyphicon-warning-sign",
10618 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10620 validFeedbackClass : "glyphicon-ok",
10623 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10625 selectOnFocus : false,
10628 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10632 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10637 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10639 disableKeyFilter : false,
10642 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10646 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10650 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10652 blankText : "Please complete this mandatory field",
10655 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10659 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10661 maxLength : Number.MAX_VALUE,
10663 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10665 minLengthText : "The minimum length for this field is {0}",
10667 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10669 maxLengthText : "The maximum length for this field is {0}",
10673 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10674 * If available, this function will be called only after the basic validators all return true, and will be passed the
10675 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10679 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10680 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10681 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10685 * @cfg {String} regexText -- Depricated - use Invalid Text
10690 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10696 autocomplete: false,
10700 inputType : 'text',
10703 placeholder: false,
10708 preventMark: false,
10709 isFormField : true,
10712 labelAlign : false,
10715 formatedValue : false,
10716 forceFeedback : false,
10718 indicatorpos : 'left',
10728 parentLabelAlign : function()
10731 while (parent.parent()) {
10732 parent = parent.parent();
10733 if (typeof(parent.labelAlign) !='undefined') {
10734 return parent.labelAlign;
10741 getAutoCreate : function()
10743 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10749 if(this.inputType != 'hidden'){
10750 cfg.cls = 'form-group' //input-group
10756 type : this.inputType,
10757 value : this.value,
10758 cls : 'form-control',
10759 placeholder : this.placeholder || '',
10760 autocomplete : this.autocomplete || 'new-password'
10762 if (this.inputType == 'file') {
10763 input.style = 'overflow:hidden'; // why not in CSS?
10766 if(this.capture.length){
10767 input.capture = this.capture;
10770 if(this.accept.length){
10771 input.accept = this.accept + "/*";
10775 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10778 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10779 input.maxLength = this.maxLength;
10782 if (this.disabled) {
10783 input.disabled=true;
10786 if (this.readOnly) {
10787 input.readonly=true;
10791 input.name = this.name;
10795 input.cls += ' input-' + this.size;
10799 ['xs','sm','md','lg'].map(function(size){
10800 if (settings[size]) {
10801 cfg.cls += ' col-' + size + '-' + settings[size];
10805 var inputblock = input;
10809 cls: 'glyphicon form-control-feedback'
10812 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10815 cls : 'has-feedback',
10823 if (this.before || this.after) {
10826 cls : 'input-group',
10830 if (this.before && typeof(this.before) == 'string') {
10832 inputblock.cn.push({
10834 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10838 if (this.before && typeof(this.before) == 'object') {
10839 this.before = Roo.factory(this.before);
10841 inputblock.cn.push({
10843 cls : 'roo-input-before input-group-prepend input-group-' +
10844 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10848 inputblock.cn.push(input);
10850 if (this.after && typeof(this.after) == 'string') {
10851 inputblock.cn.push({
10853 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10857 if (this.after && typeof(this.after) == 'object') {
10858 this.after = Roo.factory(this.after);
10860 inputblock.cn.push({
10862 cls : 'roo-input-after input-group-append input-group-' +
10863 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10867 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10868 inputblock.cls += ' has-feedback';
10869 inputblock.cn.push(feedback);
10874 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10875 tooltip : 'This field is required'
10877 if (this.allowBlank ) {
10878 indicator.style = this.allowBlank ? ' display:none' : '';
10880 if (align ==='left' && this.fieldLabel.length) {
10882 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10889 cls : 'control-label col-form-label',
10890 html : this.fieldLabel
10901 var labelCfg = cfg.cn[1];
10902 var contentCfg = cfg.cn[2];
10904 if(this.indicatorpos == 'right'){
10909 cls : 'control-label col-form-label',
10913 html : this.fieldLabel
10927 labelCfg = cfg.cn[0];
10928 contentCfg = cfg.cn[1];
10932 if(this.labelWidth > 12){
10933 labelCfg.style = "width: " + this.labelWidth + 'px';
10936 if(this.labelWidth < 13 && this.labelmd == 0){
10937 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
10940 if(this.labellg > 0){
10941 labelCfg.cls += ' col-lg-' + this.labellg;
10942 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10945 if(this.labelmd > 0){
10946 labelCfg.cls += ' col-md-' + this.labelmd;
10947 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10950 if(this.labelsm > 0){
10951 labelCfg.cls += ' col-sm-' + this.labelsm;
10952 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10955 if(this.labelxs > 0){
10956 labelCfg.cls += ' col-xs-' + this.labelxs;
10957 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10961 } else if ( this.fieldLabel.length) {
10968 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10969 tooltip : 'This field is required',
10970 style : this.allowBlank ? ' display:none' : ''
10974 //cls : 'input-group-addon',
10975 html : this.fieldLabel
10983 if(this.indicatorpos == 'right'){
10988 //cls : 'input-group-addon',
10989 html : this.fieldLabel
10994 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10995 tooltip : 'This field is required',
10996 style : this.allowBlank ? ' display:none' : ''
11016 if (this.parentType === 'Navbar' && this.parent().bar) {
11017 cfg.cls += ' navbar-form';
11020 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11021 // on BS4 we do this only if not form
11022 cfg.cls += ' navbar-form';
11030 * return the real input element.
11032 inputEl: function ()
11034 return this.el.select('input.form-control',true).first();
11037 tooltipEl : function()
11039 return this.inputEl();
11042 indicatorEl : function()
11044 if (Roo.bootstrap.version == 4) {
11045 return false; // not enabled in v4 yet.
11048 var indicator = this.el.select('i.roo-required-indicator',true).first();
11058 setDisabled : function(v)
11060 var i = this.inputEl().dom;
11062 i.removeAttribute('disabled');
11066 i.setAttribute('disabled','true');
11068 initEvents : function()
11071 this.inputEl().on("keydown" , this.fireKey, this);
11072 this.inputEl().on("focus", this.onFocus, this);
11073 this.inputEl().on("blur", this.onBlur, this);
11075 this.inputEl().relayEvent('keyup', this);
11076 this.inputEl().relayEvent('paste', this);
11078 this.indicator = this.indicatorEl();
11080 if(this.indicator){
11081 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11084 // reference to original value for reset
11085 this.originalValue = this.getValue();
11086 //Roo.form.TextField.superclass.initEvents.call(this);
11087 if(this.validationEvent == 'keyup'){
11088 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11089 this.inputEl().on('keyup', this.filterValidation, this);
11091 else if(this.validationEvent !== false){
11092 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11095 if(this.selectOnFocus){
11096 this.on("focus", this.preFocus, this);
11099 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11100 this.inputEl().on("keypress", this.filterKeys, this);
11102 this.inputEl().relayEvent('keypress', this);
11105 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11106 this.el.on("click", this.autoSize, this);
11109 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11110 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11113 if (typeof(this.before) == 'object') {
11114 this.before.render(this.el.select('.roo-input-before',true).first());
11116 if (typeof(this.after) == 'object') {
11117 this.after.render(this.el.select('.roo-input-after',true).first());
11120 this.inputEl().on('change', this.onChange, this);
11123 filterValidation : function(e){
11124 if(!e.isNavKeyPress()){
11125 this.validationTask.delay(this.validationDelay);
11129 * Validates the field value
11130 * @return {Boolean} True if the value is valid, else false
11132 validate : function(){
11133 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11134 if(this.disabled || this.validateValue(this.getRawValue())){
11139 this.markInvalid();
11145 * Validates a value according to the field's validation rules and marks the field as invalid
11146 * if the validation fails
11147 * @param {Mixed} value The value to validate
11148 * @return {Boolean} True if the value is valid, else false
11150 validateValue : function(value)
11152 if(this.getVisibilityEl().hasClass('hidden')){
11156 if(value.length < 1) { // if it's blank
11157 if(this.allowBlank){
11163 if(value.length < this.minLength){
11166 if(value.length > this.maxLength){
11170 var vt = Roo.form.VTypes;
11171 if(!vt[this.vtype](value, this)){
11175 if(typeof this.validator == "function"){
11176 var msg = this.validator(value);
11180 if (typeof(msg) == 'string') {
11181 this.invalidText = msg;
11185 if(this.regex && !this.regex.test(value)){
11193 fireKey : function(e){
11194 //Roo.log('field ' + e.getKey());
11195 if(e.isNavKeyPress()){
11196 this.fireEvent("specialkey", this, e);
11199 focus : function (selectText){
11201 this.inputEl().focus();
11202 if(selectText === true){
11203 this.inputEl().dom.select();
11209 onFocus : function(){
11210 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11211 // this.el.addClass(this.focusClass);
11213 if(!this.hasFocus){
11214 this.hasFocus = true;
11215 this.startValue = this.getValue();
11216 this.fireEvent("focus", this);
11220 beforeBlur : Roo.emptyFn,
11224 onBlur : function(){
11226 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11227 //this.el.removeClass(this.focusClass);
11229 this.hasFocus = false;
11230 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11233 var v = this.getValue();
11234 if(String(v) !== String(this.startValue)){
11235 this.fireEvent('change', this, v, this.startValue);
11237 this.fireEvent("blur", this);
11240 onChange : function(e)
11242 var v = this.getValue();
11243 if(String(v) !== String(this.startValue)){
11244 this.fireEvent('change', this, v, this.startValue);
11250 * Resets the current field value to the originally loaded value and clears any validation messages
11252 reset : function(){
11253 this.setValue(this.originalValue);
11257 * Returns the name of the field
11258 * @return {Mixed} name The name field
11260 getName: function(){
11264 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11265 * @return {Mixed} value The field value
11267 getValue : function(){
11269 var v = this.inputEl().getValue();
11274 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11275 * @return {Mixed} value The field value
11277 getRawValue : function(){
11278 var v = this.inputEl().getValue();
11284 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11285 * @param {Mixed} value The value to set
11287 setRawValue : function(v){
11288 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11291 selectText : function(start, end){
11292 var v = this.getRawValue();
11294 start = start === undefined ? 0 : start;
11295 end = end === undefined ? v.length : end;
11296 var d = this.inputEl().dom;
11297 if(d.setSelectionRange){
11298 d.setSelectionRange(start, end);
11299 }else if(d.createTextRange){
11300 var range = d.createTextRange();
11301 range.moveStart("character", start);
11302 range.moveEnd("character", v.length-end);
11309 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11310 * @param {Mixed} value The value to set
11312 setValue : function(v){
11315 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11321 processValue : function(value){
11322 if(this.stripCharsRe){
11323 var newValue = value.replace(this.stripCharsRe, '');
11324 if(newValue !== value){
11325 this.setRawValue(newValue);
11332 preFocus : function(){
11334 if(this.selectOnFocus){
11335 this.inputEl().dom.select();
11338 filterKeys : function(e){
11339 var k = e.getKey();
11340 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11343 var c = e.getCharCode(), cc = String.fromCharCode(c);
11344 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11347 if(!this.maskRe.test(cc)){
11352 * Clear any invalid styles/messages for this field
11354 clearInvalid : function(){
11356 if(!this.el || this.preventMark){ // not rendered
11361 this.el.removeClass([this.invalidClass, 'is-invalid']);
11363 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11365 var feedback = this.el.select('.form-control-feedback', true).first();
11368 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11373 if(this.indicator){
11374 this.indicator.removeClass('visible');
11375 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11378 this.fireEvent('valid', this);
11382 * Mark this field as valid
11384 markValid : function()
11386 if(!this.el || this.preventMark){ // not rendered...
11390 this.el.removeClass([this.invalidClass, this.validClass]);
11391 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11393 var feedback = this.el.select('.form-control-feedback', true).first();
11396 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11399 if(this.indicator){
11400 this.indicator.removeClass('visible');
11401 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11409 if(this.allowBlank && !this.getRawValue().length){
11412 if (Roo.bootstrap.version == 3) {
11413 this.el.addClass(this.validClass);
11415 this.inputEl().addClass('is-valid');
11418 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11420 var feedback = this.el.select('.form-control-feedback', true).first();
11423 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11424 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11429 this.fireEvent('valid', this);
11433 * Mark this field as invalid
11434 * @param {String} msg The validation message
11436 markInvalid : function(msg)
11438 if(!this.el || this.preventMark){ // not rendered
11442 this.el.removeClass([this.invalidClass, this.validClass]);
11443 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11445 var feedback = this.el.select('.form-control-feedback', true).first();
11448 this.el.select('.form-control-feedback', true).first().removeClass(
11449 [this.invalidFeedbackClass, this.validFeedbackClass]);
11456 if(this.allowBlank && !this.getRawValue().length){
11460 if(this.indicator){
11461 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11462 this.indicator.addClass('visible');
11464 if (Roo.bootstrap.version == 3) {
11465 this.el.addClass(this.invalidClass);
11467 this.inputEl().addClass('is-invalid');
11472 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11474 var feedback = this.el.select('.form-control-feedback', true).first();
11477 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11479 if(this.getValue().length || this.forceFeedback){
11480 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11487 this.fireEvent('invalid', this, msg);
11490 SafariOnKeyDown : function(event)
11492 // this is a workaround for a password hang bug on chrome/ webkit.
11493 if (this.inputEl().dom.type != 'password') {
11497 var isSelectAll = false;
11499 if(this.inputEl().dom.selectionEnd > 0){
11500 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11502 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11503 event.preventDefault();
11508 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11510 event.preventDefault();
11511 // this is very hacky as keydown always get's upper case.
11513 var cc = String.fromCharCode(event.getCharCode());
11514 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11518 adjustWidth : function(tag, w){
11519 tag = tag.toLowerCase();
11520 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11521 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11522 if(tag == 'input'){
11525 if(tag == 'textarea'){
11528 }else if(Roo.isOpera){
11529 if(tag == 'input'){
11532 if(tag == 'textarea'){
11540 setFieldLabel : function(v)
11542 if(!this.rendered){
11546 if(this.indicatorEl()){
11547 var ar = this.el.select('label > span',true);
11549 if (ar.elements.length) {
11550 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11551 this.fieldLabel = v;
11555 var br = this.el.select('label',true);
11557 if(br.elements.length) {
11558 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11559 this.fieldLabel = v;
11563 Roo.log('Cannot Found any of label > span || label in input');
11567 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11568 this.fieldLabel = v;
11583 * @class Roo.bootstrap.TextArea
11584 * @extends Roo.bootstrap.Input
11585 * Bootstrap TextArea class
11586 * @cfg {Number} cols Specifies the visible width of a text area
11587 * @cfg {Number} rows Specifies the visible number of lines in a text area
11588 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11589 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11590 * @cfg {string} html text
11593 * Create a new TextArea
11594 * @param {Object} config The config object
11597 Roo.bootstrap.TextArea = function(config){
11598 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11602 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11612 getAutoCreate : function(){
11614 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11620 if(this.inputType != 'hidden'){
11621 cfg.cls = 'form-group' //input-group
11629 value : this.value || '',
11630 html: this.html || '',
11631 cls : 'form-control',
11632 placeholder : this.placeholder || ''
11636 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11637 input.maxLength = this.maxLength;
11641 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11645 input.cols = this.cols;
11648 if (this.readOnly) {
11649 input.readonly = true;
11653 input.name = this.name;
11657 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11661 ['xs','sm','md','lg'].map(function(size){
11662 if (settings[size]) {
11663 cfg.cls += ' col-' + size + '-' + settings[size];
11667 var inputblock = input;
11669 if(this.hasFeedback && !this.allowBlank){
11673 cls: 'glyphicon form-control-feedback'
11677 cls : 'has-feedback',
11686 if (this.before || this.after) {
11689 cls : 'input-group',
11693 inputblock.cn.push({
11695 cls : 'input-group-addon',
11700 inputblock.cn.push(input);
11702 if(this.hasFeedback && !this.allowBlank){
11703 inputblock.cls += ' has-feedback';
11704 inputblock.cn.push(feedback);
11708 inputblock.cn.push({
11710 cls : 'input-group-addon',
11717 if (align ==='left' && this.fieldLabel.length) {
11722 cls : 'control-label',
11723 html : this.fieldLabel
11734 if(this.labelWidth > 12){
11735 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11738 if(this.labelWidth < 13 && this.labelmd == 0){
11739 this.labelmd = this.labelWidth;
11742 if(this.labellg > 0){
11743 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11744 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11747 if(this.labelmd > 0){
11748 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11749 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11752 if(this.labelsm > 0){
11753 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11754 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11757 if(this.labelxs > 0){
11758 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11759 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11762 } else if ( this.fieldLabel.length) {
11767 //cls : 'input-group-addon',
11768 html : this.fieldLabel
11786 if (this.disabled) {
11787 input.disabled=true;
11794 * return the real textarea element.
11796 inputEl: function ()
11798 return this.el.select('textarea.form-control',true).first();
11802 * Clear any invalid styles/messages for this field
11804 clearInvalid : function()
11807 if(!this.el || this.preventMark){ // not rendered
11811 var label = this.el.select('label', true).first();
11812 var icon = this.el.select('i.fa-star', true).first();
11817 this.el.removeClass( this.validClass);
11818 this.inputEl().removeClass('is-invalid');
11820 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11822 var feedback = this.el.select('.form-control-feedback', true).first();
11825 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11830 this.fireEvent('valid', this);
11834 * Mark this field as valid
11836 markValid : function()
11838 if(!this.el || this.preventMark){ // not rendered
11842 this.el.removeClass([this.invalidClass, this.validClass]);
11843 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11845 var feedback = this.el.select('.form-control-feedback', true).first();
11848 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11851 if(this.disabled || this.allowBlank){
11855 var label = this.el.select('label', true).first();
11856 var icon = this.el.select('i.fa-star', true).first();
11861 if (Roo.bootstrap.version == 3) {
11862 this.el.addClass(this.validClass);
11864 this.inputEl().addClass('is-valid');
11868 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11870 var feedback = this.el.select('.form-control-feedback', true).first();
11873 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11874 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11879 this.fireEvent('valid', this);
11883 * Mark this field as invalid
11884 * @param {String} msg The validation message
11886 markInvalid : function(msg)
11888 if(!this.el || this.preventMark){ // not rendered
11892 this.el.removeClass([this.invalidClass, this.validClass]);
11893 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11895 var feedback = this.el.select('.form-control-feedback', true).first();
11898 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11901 if(this.disabled || this.allowBlank){
11905 var label = this.el.select('label', true).first();
11906 var icon = this.el.select('i.fa-star', true).first();
11908 if(!this.getValue().length && label && !icon){
11909 this.el.createChild({
11911 cls : 'text-danger fa fa-lg fa-star',
11912 tooltip : 'This field is required',
11913 style : 'margin-right:5px;'
11917 if (Roo.bootstrap.version == 3) {
11918 this.el.addClass(this.invalidClass);
11920 this.inputEl().addClass('is-invalid');
11923 // fixme ... this may be depricated need to test..
11924 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11926 var feedback = this.el.select('.form-control-feedback', true).first();
11929 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11931 if(this.getValue().length || this.forceFeedback){
11932 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11939 this.fireEvent('invalid', this, msg);
11947 * trigger field - base class for combo..
11952 * @class Roo.bootstrap.TriggerField
11953 * @extends Roo.bootstrap.Input
11954 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11955 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11956 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11957 * for which you can provide a custom implementation. For example:
11959 var trigger = new Roo.bootstrap.TriggerField();
11960 trigger.onTriggerClick = myTriggerFn;
11961 trigger.applyTo('my-field');
11964 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11965 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11966 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11967 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11968 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11971 * Create a new TriggerField.
11972 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11973 * to the base TextField)
11975 Roo.bootstrap.TriggerField = function(config){
11976 this.mimicing = false;
11977 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11980 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11982 * @cfg {String} triggerClass A CSS class to apply to the trigger
11985 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11990 * @cfg {Boolean} removable (true|false) special filter default false
11994 /** @cfg {Boolean} grow @hide */
11995 /** @cfg {Number} growMin @hide */
11996 /** @cfg {Number} growMax @hide */
12002 autoSize: Roo.emptyFn,
12006 deferHeight : true,
12009 actionMode : 'wrap',
12014 getAutoCreate : function(){
12016 var align = this.labelAlign || this.parentLabelAlign();
12021 cls: 'form-group' //input-group
12028 type : this.inputType,
12029 cls : 'form-control',
12030 autocomplete: 'new-password',
12031 placeholder : this.placeholder || ''
12035 input.name = this.name;
12038 input.cls += ' input-' + this.size;
12041 if (this.disabled) {
12042 input.disabled=true;
12045 var inputblock = input;
12047 if(this.hasFeedback && !this.allowBlank){
12051 cls: 'glyphicon form-control-feedback'
12054 if(this.removable && !this.editable ){
12056 cls : 'has-feedback',
12062 cls : 'roo-combo-removable-btn close'
12069 cls : 'has-feedback',
12078 if(this.removable && !this.editable ){
12080 cls : 'roo-removable',
12086 cls : 'roo-combo-removable-btn close'
12093 if (this.before || this.after) {
12096 cls : 'input-group',
12100 inputblock.cn.push({
12102 cls : 'input-group-addon input-group-prepend input-group-text',
12107 inputblock.cn.push(input);
12109 if(this.hasFeedback && !this.allowBlank){
12110 inputblock.cls += ' has-feedback';
12111 inputblock.cn.push(feedback);
12115 inputblock.cn.push({
12117 cls : 'input-group-addon input-group-append input-group-text',
12126 var ibwrap = inputblock;
12131 cls: 'roo-select2-choices',
12135 cls: 'roo-select2-search-field',
12147 cls: 'roo-select2-container input-group',
12152 cls: 'form-hidden-field'
12158 if(!this.multiple && this.showToggleBtn){
12164 if (this.caret != false) {
12167 cls: 'fa fa-' + this.caret
12174 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12176 Roo.bootstrap.version == 3 ? caret : '',
12179 cls: 'combobox-clear',
12193 combobox.cls += ' roo-select2-container-multi';
12197 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12198 tooltip : 'This field is required'
12200 if (Roo.bootstrap.version == 4) {
12203 style : 'display:none'
12208 if (align ==='left' && this.fieldLabel.length) {
12210 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12217 cls : 'control-label',
12218 html : this.fieldLabel
12230 var labelCfg = cfg.cn[1];
12231 var contentCfg = cfg.cn[2];
12233 if(this.indicatorpos == 'right'){
12238 cls : 'control-label',
12242 html : this.fieldLabel
12256 labelCfg = cfg.cn[0];
12257 contentCfg = cfg.cn[1];
12260 if(this.labelWidth > 12){
12261 labelCfg.style = "width: " + this.labelWidth + 'px';
12264 if(this.labelWidth < 13 && this.labelmd == 0){
12265 this.labelmd = this.labelWidth;
12268 if(this.labellg > 0){
12269 labelCfg.cls += ' col-lg-' + this.labellg;
12270 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12273 if(this.labelmd > 0){
12274 labelCfg.cls += ' col-md-' + this.labelmd;
12275 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12278 if(this.labelsm > 0){
12279 labelCfg.cls += ' col-sm-' + this.labelsm;
12280 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12283 if(this.labelxs > 0){
12284 labelCfg.cls += ' col-xs-' + this.labelxs;
12285 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12288 } else if ( this.fieldLabel.length) {
12289 // Roo.log(" label");
12294 //cls : 'input-group-addon',
12295 html : this.fieldLabel
12303 if(this.indicatorpos == 'right'){
12311 html : this.fieldLabel
12325 // Roo.log(" no label && no align");
12332 ['xs','sm','md','lg'].map(function(size){
12333 if (settings[size]) {
12334 cfg.cls += ' col-' + size + '-' + settings[size];
12345 onResize : function(w, h){
12346 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12347 // if(typeof w == 'number'){
12348 // var x = w - this.trigger.getWidth();
12349 // this.inputEl().setWidth(this.adjustWidth('input', x));
12350 // this.trigger.setStyle('left', x+'px');
12355 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12358 getResizeEl : function(){
12359 return this.inputEl();
12363 getPositionEl : function(){
12364 return this.inputEl();
12368 alignErrorIcon : function(){
12369 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12373 initEvents : function(){
12377 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12378 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12379 if(!this.multiple && this.showToggleBtn){
12380 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12381 if(this.hideTrigger){
12382 this.trigger.setDisplayed(false);
12384 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12388 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12391 if(this.removable && !this.editable && !this.tickable){
12392 var close = this.closeTriggerEl();
12395 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12396 close.on('click', this.removeBtnClick, this, close);
12400 //this.trigger.addClassOnOver('x-form-trigger-over');
12401 //this.trigger.addClassOnClick('x-form-trigger-click');
12404 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12408 closeTriggerEl : function()
12410 var close = this.el.select('.roo-combo-removable-btn', true).first();
12411 return close ? close : false;
12414 removeBtnClick : function(e, h, el)
12416 e.preventDefault();
12418 if(this.fireEvent("remove", this) !== false){
12420 this.fireEvent("afterremove", this)
12424 createList : function()
12426 this.list = Roo.get(document.body).createChild({
12427 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12428 cls: 'typeahead typeahead-long dropdown-menu shadow',
12429 style: 'display:none'
12432 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12437 initTrigger : function(){
12442 onDestroy : function(){
12444 this.trigger.removeAllListeners();
12445 // this.trigger.remove();
12448 // this.wrap.remove();
12450 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12454 onFocus : function(){
12455 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12457 if(!this.mimicing){
12458 this.wrap.addClass('x-trigger-wrap-focus');
12459 this.mimicing = true;
12460 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12461 if(this.monitorTab){
12462 this.el.on("keydown", this.checkTab, this);
12469 checkTab : function(e){
12470 if(e.getKey() == e.TAB){
12471 this.triggerBlur();
12476 onBlur : function(){
12481 mimicBlur : function(e, t){
12483 if(!this.wrap.contains(t) && this.validateBlur()){
12484 this.triggerBlur();
12490 triggerBlur : function(){
12491 this.mimicing = false;
12492 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12493 if(this.monitorTab){
12494 this.el.un("keydown", this.checkTab, this);
12496 //this.wrap.removeClass('x-trigger-wrap-focus');
12497 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12501 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12502 validateBlur : function(e, t){
12507 onDisable : function(){
12508 this.inputEl().dom.disabled = true;
12509 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12511 // this.wrap.addClass('x-item-disabled');
12516 onEnable : function(){
12517 this.inputEl().dom.disabled = false;
12518 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12520 // this.el.removeClass('x-item-disabled');
12525 onShow : function(){
12526 var ae = this.getActionEl();
12529 ae.dom.style.display = '';
12530 ae.dom.style.visibility = 'visible';
12536 onHide : function(){
12537 var ae = this.getActionEl();
12538 ae.dom.style.display = 'none';
12542 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12543 * by an implementing function.
12545 * @param {EventObject} e
12547 onTriggerClick : Roo.emptyFn
12555 * @class Roo.bootstrap.CardUploader
12556 * @extends Roo.bootstrap.Button
12557 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12558 * @cfg {Number} errorTimeout default 3000
12559 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12560 * @cfg {Array} html The button text.
12564 * Create a new CardUploader
12565 * @param {Object} config The config object
12568 Roo.bootstrap.CardUploader = function(config){
12572 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12575 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12583 * When a image is clicked on - and needs to display a slideshow or similar..
12584 * @param {Roo.bootstrap.Card} this
12585 * @param {Object} The image information data
12591 * When a the download link is clicked
12592 * @param {Roo.bootstrap.Card} this
12593 * @param {Object} The image information data contains
12600 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12603 errorTimeout : 3000,
12607 fileCollection : false,
12610 getAutoCreate : function()
12614 cls :'form-group' ,
12619 //cls : 'input-group-addon',
12620 html : this.fieldLabel
12628 value : this.value,
12629 cls : 'd-none form-control'
12634 multiple : 'multiple',
12636 cls : 'd-none roo-card-upload-selector'
12640 cls : 'roo-card-uploader-button-container w-100 mb-2'
12643 cls : 'card-columns roo-card-uploader-container'
12653 getChildContainer : function() /// what children are added to.
12655 return this.containerEl;
12658 getButtonContainer : function() /// what children are added to.
12660 return this.el.select(".roo-card-uploader-button-container").first();
12663 initEvents : function()
12666 Roo.bootstrap.Input.prototype.initEvents.call(this);
12670 xns: Roo.bootstrap,
12673 container_method : 'getButtonContainer' ,
12674 html : this.html, // fix changable?
12677 'click' : function(btn, e) {
12686 this.urlAPI = (window.createObjectURL && window) ||
12687 (window.URL && URL.revokeObjectURL && URL) ||
12688 (window.webkitURL && webkitURL);
12693 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12695 this.selectorEl.on('change', this.onFileSelected, this);
12698 this.images.forEach(function(img) {
12701 this.images = false;
12703 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12709 onClick : function(e)
12711 e.preventDefault();
12713 this.selectorEl.dom.click();
12717 onFileSelected : function(e)
12719 e.preventDefault();
12721 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12725 Roo.each(this.selectorEl.dom.files, function(file){
12726 this.addFile(file);
12735 addFile : function(file)
12738 if(typeof(file) === 'string'){
12739 throw "Add file by name?"; // should not happen
12743 if(!file || !this.urlAPI){
12753 var url = _this.urlAPI.createObjectURL( file);
12756 id : Roo.bootstrap.CardUploader.ID--,
12757 is_uploaded : false,
12761 mimetype : file.type,
12769 * addCard - add an Attachment to the uploader
12770 * @param data - the data about the image to upload
12774 title : "Title of file",
12775 is_uploaded : false,
12776 src : "http://.....",
12777 srcfile : { the File upload object },
12778 mimetype : file.type,
12781 .. any other data...
12787 addCard : function (data)
12789 // hidden input element?
12790 // if the file is not an image...
12791 //then we need to use something other that and header_image
12796 xns : Roo.bootstrap,
12797 xtype : 'CardFooter',
12800 xns : Roo.bootstrap,
12806 xns : Roo.bootstrap,
12808 html : String.format("<small>{0}</small>", data.title),
12809 cls : 'col-10 text-left',
12814 click : function() {
12816 t.fireEvent( "download", t, data );
12822 xns : Roo.bootstrap,
12824 style: 'max-height: 28px; ',
12830 click : function() {
12831 t.removeCard(data.id)
12843 var cn = this.addxtype(
12846 xns : Roo.bootstrap,
12849 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12850 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12851 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12856 initEvents : function() {
12857 Roo.bootstrap.Card.prototype.initEvents.call(this);
12859 this.imgEl = this.el.select('.card-img-top').first();
12861 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12862 this.imgEl.set({ 'pointer' : 'cursor' });
12865 this.getCardFooter().addClass('p-1');
12872 // dont' really need ot update items.
12873 // this.items.push(cn);
12874 this.fileCollection.add(cn);
12876 if (!data.srcfile) {
12877 this.updateInput();
12882 var reader = new FileReader();
12883 reader.addEventListener("load", function() {
12884 data.srcdata = reader.result;
12887 reader.readAsDataURL(data.srcfile);
12892 removeCard : function(id)
12895 var card = this.fileCollection.get(id);
12896 card.data.is_deleted = 1;
12897 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12898 //this.fileCollection.remove(card);
12899 //this.items = this.items.filter(function(e) { return e != card });
12900 // dont' really need ot update items.
12901 card.el.dom.parentNode.removeChild(card.el.dom);
12902 this.updateInput();
12908 this.fileCollection.each(function(card) {
12909 if (card.el.dom && card.el.dom.parentNode) {
12910 card.el.dom.parentNode.removeChild(card.el.dom);
12913 this.fileCollection.clear();
12914 this.updateInput();
12917 updateInput : function()
12920 this.fileCollection.each(function(e) {
12924 this.inputEl().dom.value = JSON.stringify(data);
12934 Roo.bootstrap.CardUploader.ID = -1;/*
12936 * Ext JS Library 1.1.1
12937 * Copyright(c) 2006-2007, Ext JS, LLC.
12939 * Originally Released Under LGPL - original licence link has changed is not relivant.
12942 * <script type="text/javascript">
12947 * @class Roo.data.SortTypes
12949 * Defines the default sorting (casting?) comparison functions used when sorting data.
12951 Roo.data.SortTypes = {
12953 * Default sort that does nothing
12954 * @param {Mixed} s The value being converted
12955 * @return {Mixed} The comparison value
12957 none : function(s){
12962 * The regular expression used to strip tags
12966 stripTagsRE : /<\/?[^>]+>/gi,
12969 * Strips all HTML tags to sort on text only
12970 * @param {Mixed} s The value being converted
12971 * @return {String} The comparison value
12973 asText : function(s){
12974 return String(s).replace(this.stripTagsRE, "");
12978 * Strips all HTML tags to sort on text only - Case insensitive
12979 * @param {Mixed} s The value being converted
12980 * @return {String} The comparison value
12982 asUCText : function(s){
12983 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12987 * Case insensitive string
12988 * @param {Mixed} s The value being converted
12989 * @return {String} The comparison value
12991 asUCString : function(s) {
12992 return String(s).toUpperCase();
12997 * @param {Mixed} s The value being converted
12998 * @return {Number} The comparison value
13000 asDate : function(s) {
13004 if(s instanceof Date){
13005 return s.getTime();
13007 return Date.parse(String(s));
13012 * @param {Mixed} s The value being converted
13013 * @return {Float} The comparison value
13015 asFloat : function(s) {
13016 var val = parseFloat(String(s).replace(/,/g, ""));
13025 * @param {Mixed} s The value being converted
13026 * @return {Number} The comparison value
13028 asInt : function(s) {
13029 var val = parseInt(String(s).replace(/,/g, ""));
13037 * Ext JS Library 1.1.1
13038 * Copyright(c) 2006-2007, Ext JS, LLC.
13040 * Originally Released Under LGPL - original licence link has changed is not relivant.
13043 * <script type="text/javascript">
13047 * @class Roo.data.Record
13048 * Instances of this class encapsulate both record <em>definition</em> information, and record
13049 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13050 * to access Records cached in an {@link Roo.data.Store} object.<br>
13052 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13053 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13056 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13058 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13059 * {@link #create}. The parameters are the same.
13060 * @param {Array} data An associative Array of data values keyed by the field name.
13061 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13062 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13063 * not specified an integer id is generated.
13065 Roo.data.Record = function(data, id){
13066 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13071 * Generate a constructor for a specific record layout.
13072 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13073 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13074 * Each field definition object may contain the following properties: <ul>
13075 * <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,
13076 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13077 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13078 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13079 * is being used, then this is a string containing the javascript expression to reference the data relative to
13080 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13081 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13082 * this may be omitted.</p></li>
13083 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13084 * <ul><li>auto (Default, implies no conversion)</li>
13089 * <li>date</li></ul></p></li>
13090 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13091 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13092 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13093 * by the Reader into an object that will be stored in the Record. It is passed the
13094 * following parameters:<ul>
13095 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13097 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13099 * <br>usage:<br><pre><code>
13100 var TopicRecord = Roo.data.Record.create(
13101 {name: 'title', mapping: 'topic_title'},
13102 {name: 'author', mapping: 'username'},
13103 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13104 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13105 {name: 'lastPoster', mapping: 'user2'},
13106 {name: 'excerpt', mapping: 'post_text'}
13109 var myNewRecord = new TopicRecord({
13110 title: 'Do my job please',
13113 lastPost: new Date(),
13114 lastPoster: 'Animal',
13115 excerpt: 'No way dude!'
13117 myStore.add(myNewRecord);
13122 Roo.data.Record.create = function(o){
13123 var f = function(){
13124 f.superclass.constructor.apply(this, arguments);
13126 Roo.extend(f, Roo.data.Record);
13127 var p = f.prototype;
13128 p.fields = new Roo.util.MixedCollection(false, function(field){
13131 for(var i = 0, len = o.length; i < len; i++){
13132 p.fields.add(new Roo.data.Field(o[i]));
13134 f.getField = function(name){
13135 return p.fields.get(name);
13140 Roo.data.Record.AUTO_ID = 1000;
13141 Roo.data.Record.EDIT = 'edit';
13142 Roo.data.Record.REJECT = 'reject';
13143 Roo.data.Record.COMMIT = 'commit';
13145 Roo.data.Record.prototype = {
13147 * Readonly flag - true if this record has been modified.
13156 join : function(store){
13157 this.store = store;
13161 * Set the named field to the specified value.
13162 * @param {String} name The name of the field to set.
13163 * @param {Object} value The value to set the field to.
13165 set : function(name, value){
13166 if(this.data[name] == value){
13170 if(!this.modified){
13171 this.modified = {};
13173 if(typeof this.modified[name] == 'undefined'){
13174 this.modified[name] = this.data[name];
13176 this.data[name] = value;
13177 if(!this.editing && this.store){
13178 this.store.afterEdit(this);
13183 * Get the value of the named field.
13184 * @param {String} name The name of the field to get the value of.
13185 * @return {Object} The value of the field.
13187 get : function(name){
13188 return this.data[name];
13192 beginEdit : function(){
13193 this.editing = true;
13194 this.modified = {};
13198 cancelEdit : function(){
13199 this.editing = false;
13200 delete this.modified;
13204 endEdit : function(){
13205 this.editing = false;
13206 if(this.dirty && this.store){
13207 this.store.afterEdit(this);
13212 * Usually called by the {@link Roo.data.Store} which owns the Record.
13213 * Rejects all changes made to the Record since either creation, or the last commit operation.
13214 * Modified fields are reverted to their original values.
13216 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13217 * of reject operations.
13219 reject : function(){
13220 var m = this.modified;
13222 if(typeof m[n] != "function"){
13223 this.data[n] = m[n];
13226 this.dirty = false;
13227 delete this.modified;
13228 this.editing = false;
13230 this.store.afterReject(this);
13235 * Usually called by the {@link Roo.data.Store} which owns the Record.
13236 * Commits all changes made to the Record since either creation, or the last commit operation.
13238 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13239 * of commit operations.
13241 commit : function(){
13242 this.dirty = false;
13243 delete this.modified;
13244 this.editing = false;
13246 this.store.afterCommit(this);
13251 hasError : function(){
13252 return this.error != null;
13256 clearError : function(){
13261 * Creates a copy of this record.
13262 * @param {String} id (optional) A new record id if you don't want to use this record's id
13265 copy : function(newId) {
13266 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13270 * Ext JS Library 1.1.1
13271 * Copyright(c) 2006-2007, Ext JS, LLC.
13273 * Originally Released Under LGPL - original licence link has changed is not relivant.
13276 * <script type="text/javascript">
13282 * @class Roo.data.Store
13283 * @extends Roo.util.Observable
13284 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13285 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13287 * 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
13288 * has no knowledge of the format of the data returned by the Proxy.<br>
13290 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13291 * instances from the data object. These records are cached and made available through accessor functions.
13293 * Creates a new Store.
13294 * @param {Object} config A config object containing the objects needed for the Store to access data,
13295 * and read the data into Records.
13297 Roo.data.Store = function(config){
13298 this.data = new Roo.util.MixedCollection(false);
13299 this.data.getKey = function(o){
13302 this.baseParams = {};
13304 this.paramNames = {
13309 "multisort" : "_multisort"
13312 if(config && config.data){
13313 this.inlineData = config.data;
13314 delete config.data;
13317 Roo.apply(this, config);
13319 if(this.reader){ // reader passed
13320 this.reader = Roo.factory(this.reader, Roo.data);
13321 this.reader.xmodule = this.xmodule || false;
13322 if(!this.recordType){
13323 this.recordType = this.reader.recordType;
13325 if(this.reader.onMetaChange){
13326 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13330 if(this.recordType){
13331 this.fields = this.recordType.prototype.fields;
13333 this.modified = [];
13337 * @event datachanged
13338 * Fires when the data cache has changed, and a widget which is using this Store
13339 * as a Record cache should refresh its view.
13340 * @param {Store} this
13342 datachanged : true,
13344 * @event metachange
13345 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13346 * @param {Store} this
13347 * @param {Object} meta The JSON metadata
13352 * Fires when Records have been added to the Store
13353 * @param {Store} this
13354 * @param {Roo.data.Record[]} records The array of Records added
13355 * @param {Number} index The index at which the record(s) were added
13360 * Fires when a Record has been removed from the Store
13361 * @param {Store} this
13362 * @param {Roo.data.Record} record The Record that was removed
13363 * @param {Number} index The index at which the record was removed
13368 * Fires when a Record has been updated
13369 * @param {Store} this
13370 * @param {Roo.data.Record} record The Record that was updated
13371 * @param {String} operation The update operation being performed. Value may be one of:
13373 Roo.data.Record.EDIT
13374 Roo.data.Record.REJECT
13375 Roo.data.Record.COMMIT
13381 * Fires when the data cache has been cleared.
13382 * @param {Store} this
13386 * @event beforeload
13387 * Fires before a request is made for a new data object. If the beforeload handler returns false
13388 * the load action will be canceled.
13389 * @param {Store} this
13390 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13394 * @event beforeloadadd
13395 * Fires after a new set of Records has been loaded.
13396 * @param {Store} this
13397 * @param {Roo.data.Record[]} records The Records that were loaded
13398 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13400 beforeloadadd : true,
13403 * Fires after a new set of Records has been loaded, before they are added to the store.
13404 * @param {Store} this
13405 * @param {Roo.data.Record[]} records The Records that were loaded
13406 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13407 * @params {Object} return from reader
13411 * @event loadexception
13412 * Fires if an exception occurs in the Proxy during loading.
13413 * Called with the signature of the Proxy's "loadexception" event.
13414 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13417 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13418 * @param {Object} load options
13419 * @param {Object} jsonData from your request (normally this contains the Exception)
13421 loadexception : true
13425 this.proxy = Roo.factory(this.proxy, Roo.data);
13426 this.proxy.xmodule = this.xmodule || false;
13427 this.relayEvents(this.proxy, ["loadexception"]);
13429 this.sortToggle = {};
13430 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13432 Roo.data.Store.superclass.constructor.call(this);
13434 if(this.inlineData){
13435 this.loadData(this.inlineData);
13436 delete this.inlineData;
13440 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13442 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13443 * without a remote query - used by combo/forms at present.
13447 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13450 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13453 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13454 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13457 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13458 * on any HTTP request
13461 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13464 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13468 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13469 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13471 remoteSort : false,
13474 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13475 * loaded or when a record is removed. (defaults to false).
13477 pruneModifiedRecords : false,
13480 lastOptions : null,
13483 * Add Records to the Store and fires the add event.
13484 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13486 add : function(records){
13487 records = [].concat(records);
13488 for(var i = 0, len = records.length; i < len; i++){
13489 records[i].join(this);
13491 var index = this.data.length;
13492 this.data.addAll(records);
13493 this.fireEvent("add", this, records, index);
13497 * Remove a Record from the Store and fires the remove event.
13498 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13500 remove : function(record){
13501 var index = this.data.indexOf(record);
13502 this.data.removeAt(index);
13504 if(this.pruneModifiedRecords){
13505 this.modified.remove(record);
13507 this.fireEvent("remove", this, record, index);
13511 * Remove all Records from the Store and fires the clear event.
13513 removeAll : function(){
13515 if(this.pruneModifiedRecords){
13516 this.modified = [];
13518 this.fireEvent("clear", this);
13522 * Inserts Records to the Store at the given index and fires the add event.
13523 * @param {Number} index The start index at which to insert the passed Records.
13524 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13526 insert : function(index, records){
13527 records = [].concat(records);
13528 for(var i = 0, len = records.length; i < len; i++){
13529 this.data.insert(index, records[i]);
13530 records[i].join(this);
13532 this.fireEvent("add", this, records, index);
13536 * Get the index within the cache of the passed Record.
13537 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13538 * @return {Number} The index of the passed Record. Returns -1 if not found.
13540 indexOf : function(record){
13541 return this.data.indexOf(record);
13545 * Get the index within the cache of the Record with the passed id.
13546 * @param {String} id The id of the Record to find.
13547 * @return {Number} The index of the Record. Returns -1 if not found.
13549 indexOfId : function(id){
13550 return this.data.indexOfKey(id);
13554 * Get the Record with the specified id.
13555 * @param {String} id The id of the Record to find.
13556 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13558 getById : function(id){
13559 return this.data.key(id);
13563 * Get the Record at the specified index.
13564 * @param {Number} index The index of the Record to find.
13565 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13567 getAt : function(index){
13568 return this.data.itemAt(index);
13572 * Returns a range of Records between specified indices.
13573 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13574 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13575 * @return {Roo.data.Record[]} An array of Records
13577 getRange : function(start, end){
13578 return this.data.getRange(start, end);
13582 storeOptions : function(o){
13583 o = Roo.apply({}, o);
13586 this.lastOptions = o;
13590 * Loads the Record cache from the configured Proxy using the configured Reader.
13592 * If using remote paging, then the first load call must specify the <em>start</em>
13593 * and <em>limit</em> properties in the options.params property to establish the initial
13594 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13596 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13597 * and this call will return before the new data has been loaded. Perform any post-processing
13598 * in a callback function, or in a "load" event handler.</strong>
13600 * @param {Object} options An object containing properties which control loading options:<ul>
13601 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13602 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13603 * passed the following arguments:<ul>
13604 * <li>r : Roo.data.Record[]</li>
13605 * <li>options: Options object from the load call</li>
13606 * <li>success: Boolean success indicator</li></ul></li>
13607 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13608 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13611 load : function(options){
13612 options = options || {};
13613 if(this.fireEvent("beforeload", this, options) !== false){
13614 this.storeOptions(options);
13615 var p = Roo.apply(options.params || {}, this.baseParams);
13616 // if meta was not loaded from remote source.. try requesting it.
13617 if (!this.reader.metaFromRemote) {
13618 p._requestMeta = 1;
13620 if(this.sortInfo && this.remoteSort){
13621 var pn = this.paramNames;
13622 p[pn["sort"]] = this.sortInfo.field;
13623 p[pn["dir"]] = this.sortInfo.direction;
13625 if (this.multiSort) {
13626 var pn = this.paramNames;
13627 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13630 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13635 * Reloads the Record cache from the configured Proxy using the configured Reader and
13636 * the options from the last load operation performed.
13637 * @param {Object} options (optional) An object containing properties which may override the options
13638 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13639 * the most recently used options are reused).
13641 reload : function(options){
13642 this.load(Roo.applyIf(options||{}, this.lastOptions));
13646 // Called as a callback by the Reader during a load operation.
13647 loadRecords : function(o, options, success){
13648 if(!o || success === false){
13649 if(success !== false){
13650 this.fireEvent("load", this, [], options, o);
13652 if(options.callback){
13653 options.callback.call(options.scope || this, [], options, false);
13657 // if data returned failure - throw an exception.
13658 if (o.success === false) {
13659 // show a message if no listener is registered.
13660 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13661 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13663 // loadmask wil be hooked into this..
13664 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13667 var r = o.records, t = o.totalRecords || r.length;
13669 this.fireEvent("beforeloadadd", this, r, options, o);
13671 if(!options || options.add !== true){
13672 if(this.pruneModifiedRecords){
13673 this.modified = [];
13675 for(var i = 0, len = r.length; i < len; i++){
13679 this.data = this.snapshot;
13680 delete this.snapshot;
13683 this.data.addAll(r);
13684 this.totalLength = t;
13686 this.fireEvent("datachanged", this);
13688 this.totalLength = Math.max(t, this.data.length+r.length);
13692 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13694 var e = new Roo.data.Record({});
13696 e.set(this.parent.displayField, this.parent.emptyTitle);
13697 e.set(this.parent.valueField, '');
13702 this.fireEvent("load", this, r, options, o);
13703 if(options.callback){
13704 options.callback.call(options.scope || this, r, options, true);
13710 * Loads data from a passed data block. A Reader which understands the format of the data
13711 * must have been configured in the constructor.
13712 * @param {Object} data The data block from which to read the Records. The format of the data expected
13713 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13714 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13716 loadData : function(o, append){
13717 var r = this.reader.readRecords(o);
13718 this.loadRecords(r, {add: append}, true);
13722 * using 'cn' the nested child reader read the child array into it's child stores.
13723 * @param {Object} rec The record with a 'children array
13725 loadDataFromChildren : function(rec)
13727 this.loadData(this.reader.toLoadData(rec));
13732 * Gets the number of cached records.
13734 * <em>If using paging, this may not be the total size of the dataset. If the data object
13735 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13736 * the data set size</em>
13738 getCount : function(){
13739 return this.data.length || 0;
13743 * Gets the total number of records in the dataset as returned by the server.
13745 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13746 * the dataset size</em>
13748 getTotalCount : function(){
13749 return this.totalLength || 0;
13753 * Returns the sort state of the Store as an object with two properties:
13755 field {String} The name of the field by which the Records are sorted
13756 direction {String} The sort order, "ASC" or "DESC"
13759 getSortState : function(){
13760 return this.sortInfo;
13764 applySort : function(){
13765 if(this.sortInfo && !this.remoteSort){
13766 var s = this.sortInfo, f = s.field;
13767 var st = this.fields.get(f).sortType;
13768 var fn = function(r1, r2){
13769 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13770 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13772 this.data.sort(s.direction, fn);
13773 if(this.snapshot && this.snapshot != this.data){
13774 this.snapshot.sort(s.direction, fn);
13780 * Sets the default sort column and order to be used by the next load operation.
13781 * @param {String} fieldName The name of the field to sort by.
13782 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13784 setDefaultSort : function(field, dir){
13785 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13789 * Sort the Records.
13790 * If remote sorting is used, the sort is performed on the server, and the cache is
13791 * reloaded. If local sorting is used, the cache is sorted internally.
13792 * @param {String} fieldName The name of the field to sort by.
13793 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13795 sort : function(fieldName, dir){
13796 var f = this.fields.get(fieldName);
13798 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13800 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13801 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13806 this.sortToggle[f.name] = dir;
13807 this.sortInfo = {field: f.name, direction: dir};
13808 if(!this.remoteSort){
13810 this.fireEvent("datachanged", this);
13812 this.load(this.lastOptions);
13817 * Calls the specified function for each of the Records in the cache.
13818 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13819 * Returning <em>false</em> aborts and exits the iteration.
13820 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13822 each : function(fn, scope){
13823 this.data.each(fn, scope);
13827 * Gets all records modified since the last commit. Modified records are persisted across load operations
13828 * (e.g., during paging).
13829 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13831 getModifiedRecords : function(){
13832 return this.modified;
13836 createFilterFn : function(property, value, anyMatch){
13837 if(!value.exec){ // not a regex
13838 value = String(value);
13839 if(value.length == 0){
13842 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13844 return function(r){
13845 return value.test(r.data[property]);
13850 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13851 * @param {String} property A field on your records
13852 * @param {Number} start The record index to start at (defaults to 0)
13853 * @param {Number} end The last record index to include (defaults to length - 1)
13854 * @return {Number} The sum
13856 sum : function(property, start, end){
13857 var rs = this.data.items, v = 0;
13858 start = start || 0;
13859 end = (end || end === 0) ? end : rs.length-1;
13861 for(var i = start; i <= end; i++){
13862 v += (rs[i].data[property] || 0);
13868 * Filter the records by a specified property.
13869 * @param {String} field A field on your records
13870 * @param {String/RegExp} value Either a string that the field
13871 * should start with or a RegExp to test against the field
13872 * @param {Boolean} anyMatch True to match any part not just the beginning
13874 filter : function(property, value, anyMatch){
13875 var fn = this.createFilterFn(property, value, anyMatch);
13876 return fn ? this.filterBy(fn) : this.clearFilter();
13880 * Filter by a function. The specified function will be called with each
13881 * record in this data source. If the function returns true the record is included,
13882 * otherwise it is filtered.
13883 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13884 * @param {Object} scope (optional) The scope of the function (defaults to this)
13886 filterBy : function(fn, scope){
13887 this.snapshot = this.snapshot || this.data;
13888 this.data = this.queryBy(fn, scope||this);
13889 this.fireEvent("datachanged", this);
13893 * Query the records by a specified property.
13894 * @param {String} field A field on your records
13895 * @param {String/RegExp} value Either a string that the field
13896 * should start with or a RegExp to test against the field
13897 * @param {Boolean} anyMatch True to match any part not just the beginning
13898 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13900 query : function(property, value, anyMatch){
13901 var fn = this.createFilterFn(property, value, anyMatch);
13902 return fn ? this.queryBy(fn) : this.data.clone();
13906 * Query by a function. The specified function will be called with each
13907 * record in this data source. If the function returns true the record is included
13909 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13910 * @param {Object} scope (optional) The scope of the function (defaults to this)
13911 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13913 queryBy : function(fn, scope){
13914 var data = this.snapshot || this.data;
13915 return data.filterBy(fn, scope||this);
13919 * Collects unique values for a particular dataIndex from this store.
13920 * @param {String} dataIndex The property to collect
13921 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13922 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13923 * @return {Array} An array of the unique values
13925 collect : function(dataIndex, allowNull, bypassFilter){
13926 var d = (bypassFilter === true && this.snapshot) ?
13927 this.snapshot.items : this.data.items;
13928 var v, sv, r = [], l = {};
13929 for(var i = 0, len = d.length; i < len; i++){
13930 v = d[i].data[dataIndex];
13932 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13941 * Revert to a view of the Record cache with no filtering applied.
13942 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13944 clearFilter : function(suppressEvent){
13945 if(this.snapshot && this.snapshot != this.data){
13946 this.data = this.snapshot;
13947 delete this.snapshot;
13948 if(suppressEvent !== true){
13949 this.fireEvent("datachanged", this);
13955 afterEdit : function(record){
13956 if(this.modified.indexOf(record) == -1){
13957 this.modified.push(record);
13959 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13963 afterReject : function(record){
13964 this.modified.remove(record);
13965 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13969 afterCommit : function(record){
13970 this.modified.remove(record);
13971 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13975 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13976 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13978 commitChanges : function(){
13979 var m = this.modified.slice(0);
13980 this.modified = [];
13981 for(var i = 0, len = m.length; i < len; i++){
13987 * Cancel outstanding changes on all changed records.
13989 rejectChanges : function(){
13990 var m = this.modified.slice(0);
13991 this.modified = [];
13992 for(var i = 0, len = m.length; i < len; i++){
13997 onMetaChange : function(meta, rtype, o){
13998 this.recordType = rtype;
13999 this.fields = rtype.prototype.fields;
14000 delete this.snapshot;
14001 this.sortInfo = meta.sortInfo || this.sortInfo;
14002 this.modified = [];
14003 this.fireEvent('metachange', this, this.reader.meta);
14006 moveIndex : function(data, type)
14008 var index = this.indexOf(data);
14010 var newIndex = index + type;
14014 this.insert(newIndex, data);
14019 * Ext JS Library 1.1.1
14020 * Copyright(c) 2006-2007, Ext JS, LLC.
14022 * Originally Released Under LGPL - original licence link has changed is not relivant.
14025 * <script type="text/javascript">
14029 * @class Roo.data.SimpleStore
14030 * @extends Roo.data.Store
14031 * Small helper class to make creating Stores from Array data easier.
14032 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14033 * @cfg {Array} fields An array of field definition objects, or field name strings.
14034 * @cfg {Object} an existing reader (eg. copied from another store)
14035 * @cfg {Array} data The multi-dimensional array of data
14037 * @param {Object} config
14039 Roo.data.SimpleStore = function(config)
14041 Roo.data.SimpleStore.superclass.constructor.call(this, {
14043 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14046 Roo.data.Record.create(config.fields)
14048 proxy : new Roo.data.MemoryProxy(config.data)
14052 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14054 * Ext JS Library 1.1.1
14055 * Copyright(c) 2006-2007, Ext JS, LLC.
14057 * Originally Released Under LGPL - original licence link has changed is not relivant.
14060 * <script type="text/javascript">
14065 * @extends Roo.data.Store
14066 * @class Roo.data.JsonStore
14067 * Small helper class to make creating Stores for JSON data easier. <br/>
14069 var store = new Roo.data.JsonStore({
14070 url: 'get-images.php',
14072 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14075 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14076 * JsonReader and HttpProxy (unless inline data is provided).</b>
14077 * @cfg {Array} fields An array of field definition objects, or field name strings.
14079 * @param {Object} config
14081 Roo.data.JsonStore = function(c){
14082 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14083 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14084 reader: new Roo.data.JsonReader(c, c.fields)
14087 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14089 * Ext JS Library 1.1.1
14090 * Copyright(c) 2006-2007, Ext JS, LLC.
14092 * Originally Released Under LGPL - original licence link has changed is not relivant.
14095 * <script type="text/javascript">
14099 Roo.data.Field = function(config){
14100 if(typeof config == "string"){
14101 config = {name: config};
14103 Roo.apply(this, config);
14106 this.type = "auto";
14109 var st = Roo.data.SortTypes;
14110 // named sortTypes are supported, here we look them up
14111 if(typeof this.sortType == "string"){
14112 this.sortType = st[this.sortType];
14115 // set default sortType for strings and dates
14116 if(!this.sortType){
14119 this.sortType = st.asUCString;
14122 this.sortType = st.asDate;
14125 this.sortType = st.none;
14130 var stripRe = /[\$,%]/g;
14132 // prebuilt conversion function for this field, instead of
14133 // switching every time we're reading a value
14135 var cv, dateFormat = this.dateFormat;
14140 cv = function(v){ return v; };
14143 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14147 return v !== undefined && v !== null && v !== '' ?
14148 parseInt(String(v).replace(stripRe, ""), 10) : '';
14153 return v !== undefined && v !== null && v !== '' ?
14154 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14159 cv = function(v){ return v === true || v === "true" || v == 1; };
14166 if(v instanceof Date){
14170 if(dateFormat == "timestamp"){
14171 return new Date(v*1000);
14173 return Date.parseDate(v, dateFormat);
14175 var parsed = Date.parse(v);
14176 return parsed ? new Date(parsed) : null;
14185 Roo.data.Field.prototype = {
14193 * Ext JS Library 1.1.1
14194 * Copyright(c) 2006-2007, Ext JS, LLC.
14196 * Originally Released Under LGPL - original licence link has changed is not relivant.
14199 * <script type="text/javascript">
14202 // Base class for reading structured data from a data source. This class is intended to be
14203 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14206 * @class Roo.data.DataReader
14207 * Base class for reading structured data from a data source. This class is intended to be
14208 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14211 Roo.data.DataReader = function(meta, recordType){
14215 this.recordType = recordType instanceof Array ?
14216 Roo.data.Record.create(recordType) : recordType;
14219 Roo.data.DataReader.prototype = {
14222 readerType : 'Data',
14224 * Create an empty record
14225 * @param {Object} data (optional) - overlay some values
14226 * @return {Roo.data.Record} record created.
14228 newRow : function(d) {
14230 this.recordType.prototype.fields.each(function(c) {
14232 case 'int' : da[c.name] = 0; break;
14233 case 'date' : da[c.name] = new Date(); break;
14234 case 'float' : da[c.name] = 0.0; break;
14235 case 'boolean' : da[c.name] = false; break;
14236 default : da[c.name] = ""; break;
14240 return new this.recordType(Roo.apply(da, d));
14246 * Ext JS Library 1.1.1
14247 * Copyright(c) 2006-2007, Ext JS, LLC.
14249 * Originally Released Under LGPL - original licence link has changed is not relivant.
14252 * <script type="text/javascript">
14256 * @class Roo.data.DataProxy
14257 * @extends Roo.data.Observable
14258 * This class is an abstract base class for implementations which provide retrieval of
14259 * unformatted data objects.<br>
14261 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14262 * (of the appropriate type which knows how to parse the data object) to provide a block of
14263 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14265 * Custom implementations must implement the load method as described in
14266 * {@link Roo.data.HttpProxy#load}.
14268 Roo.data.DataProxy = function(){
14271 * @event beforeload
14272 * Fires before a network request is made to retrieve a data object.
14273 * @param {Object} This DataProxy object.
14274 * @param {Object} params The params parameter to the load function.
14279 * Fires before the load method's callback is called.
14280 * @param {Object} This DataProxy object.
14281 * @param {Object} o The data object.
14282 * @param {Object} arg The callback argument object passed to the load function.
14286 * @event loadexception
14287 * Fires if an Exception occurs during data retrieval.
14288 * @param {Object} This DataProxy object.
14289 * @param {Object} o The data object.
14290 * @param {Object} arg The callback argument object passed to the load function.
14291 * @param {Object} e The Exception.
14293 loadexception : true
14295 Roo.data.DataProxy.superclass.constructor.call(this);
14298 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14301 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14305 * Ext JS Library 1.1.1
14306 * Copyright(c) 2006-2007, Ext JS, LLC.
14308 * Originally Released Under LGPL - original licence link has changed is not relivant.
14311 * <script type="text/javascript">
14314 * @class Roo.data.MemoryProxy
14315 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14316 * to the Reader when its load method is called.
14318 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14320 Roo.data.MemoryProxy = function(data){
14324 Roo.data.MemoryProxy.superclass.constructor.call(this);
14328 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14331 * Load data from the requested source (in this case an in-memory
14332 * data object passed to the constructor), read the data object into
14333 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14334 * process that block using the passed callback.
14335 * @param {Object} params This parameter is not used by the MemoryProxy class.
14336 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14337 * object into a block of Roo.data.Records.
14338 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14339 * The function must be passed <ul>
14340 * <li>The Record block object</li>
14341 * <li>The "arg" argument from the load function</li>
14342 * <li>A boolean success indicator</li>
14344 * @param {Object} scope The scope in which to call the callback
14345 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14347 load : function(params, reader, callback, scope, arg){
14348 params = params || {};
14351 result = reader.readRecords(params.data ? params.data :this.data);
14353 this.fireEvent("loadexception", this, arg, null, e);
14354 callback.call(scope, null, arg, false);
14357 callback.call(scope, result, arg, true);
14361 update : function(params, records){
14366 * Ext JS Library 1.1.1
14367 * Copyright(c) 2006-2007, Ext JS, LLC.
14369 * Originally Released Under LGPL - original licence link has changed is not relivant.
14372 * <script type="text/javascript">
14375 * @class Roo.data.HttpProxy
14376 * @extends Roo.data.DataProxy
14377 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14378 * configured to reference a certain URL.<br><br>
14380 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14381 * from which the running page was served.<br><br>
14383 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14385 * Be aware that to enable the browser to parse an XML document, the server must set
14386 * the Content-Type header in the HTTP response to "text/xml".
14388 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14389 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14390 * will be used to make the request.
14392 Roo.data.HttpProxy = function(conn){
14393 Roo.data.HttpProxy.superclass.constructor.call(this);
14394 // is conn a conn config or a real conn?
14396 this.useAjax = !conn || !conn.events;
14400 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14401 // thse are take from connection...
14404 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14407 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14408 * extra parameters to each request made by this object. (defaults to undefined)
14411 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14412 * to each request made by this object. (defaults to undefined)
14415 * @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)
14418 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14421 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14427 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14431 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14432 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14433 * a finer-grained basis than the DataProxy events.
14435 getConnection : function(){
14436 return this.useAjax ? Roo.Ajax : this.conn;
14440 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14441 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14442 * process that block using the passed callback.
14443 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14444 * for the request to the remote server.
14445 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14446 * object into a block of Roo.data.Records.
14447 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14448 * The function must be passed <ul>
14449 * <li>The Record block object</li>
14450 * <li>The "arg" argument from the load function</li>
14451 * <li>A boolean success indicator</li>
14453 * @param {Object} scope The scope in which to call the callback
14454 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14456 load : function(params, reader, callback, scope, arg){
14457 if(this.fireEvent("beforeload", this, params) !== false){
14459 params : params || {},
14461 callback : callback,
14466 callback : this.loadResponse,
14470 Roo.applyIf(o, this.conn);
14471 if(this.activeRequest){
14472 Roo.Ajax.abort(this.activeRequest);
14474 this.activeRequest = Roo.Ajax.request(o);
14476 this.conn.request(o);
14479 callback.call(scope||this, null, arg, false);
14484 loadResponse : function(o, success, response){
14485 delete this.activeRequest;
14487 this.fireEvent("loadexception", this, o, response);
14488 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14493 result = o.reader.read(response);
14495 this.fireEvent("loadexception", this, o, response, e);
14496 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14500 this.fireEvent("load", this, o, o.request.arg);
14501 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14505 update : function(dataSet){
14510 updateResponse : function(dataSet){
14515 * Ext JS Library 1.1.1
14516 * Copyright(c) 2006-2007, Ext JS, LLC.
14518 * Originally Released Under LGPL - original licence link has changed is not relivant.
14521 * <script type="text/javascript">
14525 * @class Roo.data.ScriptTagProxy
14526 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14527 * other than the originating domain of the running page.<br><br>
14529 * <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
14530 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14532 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14533 * source code that is used as the source inside a <script> tag.<br><br>
14535 * In order for the browser to process the returned data, the server must wrap the data object
14536 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14537 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14538 * depending on whether the callback name was passed:
14541 boolean scriptTag = false;
14542 String cb = request.getParameter("callback");
14545 response.setContentType("text/javascript");
14547 response.setContentType("application/x-json");
14549 Writer out = response.getWriter();
14551 out.write(cb + "(");
14553 out.print(dataBlock.toJsonString());
14560 * @param {Object} config A configuration object.
14562 Roo.data.ScriptTagProxy = function(config){
14563 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14564 Roo.apply(this, config);
14565 this.head = document.getElementsByTagName("head")[0];
14568 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14570 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14572 * @cfg {String} url The URL from which to request the data object.
14575 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14579 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14580 * the server the name of the callback function set up by the load call to process the returned data object.
14581 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14582 * javascript output which calls this named function passing the data object as its only parameter.
14584 callbackParam : "callback",
14586 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14587 * name to the request.
14592 * Load data from the configured URL, read the data object into
14593 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14594 * process that block using the passed callback.
14595 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14596 * for the request to the remote server.
14597 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14598 * object into a block of Roo.data.Records.
14599 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14600 * The function must be passed <ul>
14601 * <li>The Record block object</li>
14602 * <li>The "arg" argument from the load function</li>
14603 * <li>A boolean success indicator</li>
14605 * @param {Object} scope The scope in which to call the callback
14606 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14608 load : function(params, reader, callback, scope, arg){
14609 if(this.fireEvent("beforeload", this, params) !== false){
14611 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14613 var url = this.url;
14614 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14616 url += "&_dc=" + (new Date().getTime());
14618 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14621 cb : "stcCallback"+transId,
14622 scriptId : "stcScript"+transId,
14626 callback : callback,
14632 window[trans.cb] = function(o){
14633 conn.handleResponse(o, trans);
14636 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14638 if(this.autoAbort !== false){
14642 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14644 var script = document.createElement("script");
14645 script.setAttribute("src", url);
14646 script.setAttribute("type", "text/javascript");
14647 script.setAttribute("id", trans.scriptId);
14648 this.head.appendChild(script);
14650 this.trans = trans;
14652 callback.call(scope||this, null, arg, false);
14657 isLoading : function(){
14658 return this.trans ? true : false;
14662 * Abort the current server request.
14664 abort : function(){
14665 if(this.isLoading()){
14666 this.destroyTrans(this.trans);
14671 destroyTrans : function(trans, isLoaded){
14672 this.head.removeChild(document.getElementById(trans.scriptId));
14673 clearTimeout(trans.timeoutId);
14675 window[trans.cb] = undefined;
14677 delete window[trans.cb];
14680 // if hasn't been loaded, wait for load to remove it to prevent script error
14681 window[trans.cb] = function(){
14682 window[trans.cb] = undefined;
14684 delete window[trans.cb];
14691 handleResponse : function(o, trans){
14692 this.trans = false;
14693 this.destroyTrans(trans, true);
14696 result = trans.reader.readRecords(o);
14698 this.fireEvent("loadexception", this, o, trans.arg, e);
14699 trans.callback.call(trans.scope||window, null, trans.arg, false);
14702 this.fireEvent("load", this, o, trans.arg);
14703 trans.callback.call(trans.scope||window, result, trans.arg, true);
14707 handleFailure : function(trans){
14708 this.trans = false;
14709 this.destroyTrans(trans, false);
14710 this.fireEvent("loadexception", this, null, trans.arg);
14711 trans.callback.call(trans.scope||window, null, trans.arg, false);
14715 * Ext JS Library 1.1.1
14716 * Copyright(c) 2006-2007, Ext JS, LLC.
14718 * Originally Released Under LGPL - original licence link has changed is not relivant.
14721 * <script type="text/javascript">
14725 * @class Roo.data.JsonReader
14726 * @extends Roo.data.DataReader
14727 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14728 * based on mappings in a provided Roo.data.Record constructor.
14730 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14731 * in the reply previously.
14736 var RecordDef = Roo.data.Record.create([
14737 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14738 {name: 'occupation'} // This field will use "occupation" as the mapping.
14740 var myReader = new Roo.data.JsonReader({
14741 totalProperty: "results", // The property which contains the total dataset size (optional)
14742 root: "rows", // The property which contains an Array of row objects
14743 id: "id" // The property within each row object that provides an ID for the record (optional)
14747 * This would consume a JSON file like this:
14749 { 'results': 2, 'rows': [
14750 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14751 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14754 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14755 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14756 * paged from the remote server.
14757 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14758 * @cfg {String} root name of the property which contains the Array of row objects.
14759 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14760 * @cfg {Array} fields Array of field definition objects
14762 * Create a new JsonReader
14763 * @param {Object} meta Metadata configuration options
14764 * @param {Object} recordType Either an Array of field definition objects,
14765 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14767 Roo.data.JsonReader = function(meta, recordType){
14770 // set some defaults:
14771 Roo.applyIf(meta, {
14772 totalProperty: 'total',
14773 successProperty : 'success',
14778 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14780 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14782 readerType : 'Json',
14785 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14786 * Used by Store query builder to append _requestMeta to params.
14789 metaFromRemote : false,
14791 * This method is only used by a DataProxy which has retrieved data from a remote server.
14792 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14793 * @return {Object} data A data block which is used by an Roo.data.Store object as
14794 * a cache of Roo.data.Records.
14796 read : function(response){
14797 var json = response.responseText;
14799 var o = /* eval:var:o */ eval("("+json+")");
14801 throw {message: "JsonReader.read: Json object not found"};
14807 this.metaFromRemote = true;
14808 this.meta = o.metaData;
14809 this.recordType = Roo.data.Record.create(o.metaData.fields);
14810 this.onMetaChange(this.meta, this.recordType, o);
14812 return this.readRecords(o);
14815 // private function a store will implement
14816 onMetaChange : function(meta, recordType, o){
14823 simpleAccess: function(obj, subsc) {
14830 getJsonAccessor: function(){
14832 return function(expr) {
14834 return(re.test(expr))
14835 ? new Function("obj", "return obj." + expr)
14840 return Roo.emptyFn;
14845 * Create a data block containing Roo.data.Records from an XML document.
14846 * @param {Object} o An object which contains an Array of row objects in the property specified
14847 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14848 * which contains the total size of the dataset.
14849 * @return {Object} data A data block which is used by an Roo.data.Store object as
14850 * a cache of Roo.data.Records.
14852 readRecords : function(o){
14854 * After any data loads, the raw JSON data is available for further custom processing.
14858 var s = this.meta, Record = this.recordType,
14859 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14861 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14863 if(s.totalProperty) {
14864 this.getTotal = this.getJsonAccessor(s.totalProperty);
14866 if(s.successProperty) {
14867 this.getSuccess = this.getJsonAccessor(s.successProperty);
14869 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14871 var g = this.getJsonAccessor(s.id);
14872 this.getId = function(rec) {
14874 return (r === undefined || r === "") ? null : r;
14877 this.getId = function(){return null;};
14880 for(var jj = 0; jj < fl; jj++){
14882 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14883 this.ef[jj] = this.getJsonAccessor(map);
14887 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14888 if(s.totalProperty){
14889 var vt = parseInt(this.getTotal(o), 10);
14894 if(s.successProperty){
14895 var vs = this.getSuccess(o);
14896 if(vs === false || vs === 'false'){
14901 for(var i = 0; i < c; i++){
14904 var id = this.getId(n);
14905 for(var j = 0; j < fl; j++){
14907 var v = this.ef[j](n);
14909 Roo.log('missing convert for ' + f.name);
14913 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14915 var record = new Record(values, id);
14917 records[i] = record;
14923 totalRecords : totalRecords
14926 // used when loading children.. @see loadDataFromChildren
14927 toLoadData: function(rec)
14929 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14930 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14931 return { data : data, total : data.length };
14936 * Ext JS Library 1.1.1
14937 * Copyright(c) 2006-2007, Ext JS, LLC.
14939 * Originally Released Under LGPL - original licence link has changed is not relivant.
14942 * <script type="text/javascript">
14946 * @class Roo.data.ArrayReader
14947 * @extends Roo.data.DataReader
14948 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14949 * Each element of that Array represents a row of data fields. The
14950 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14951 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14955 var RecordDef = Roo.data.Record.create([
14956 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14957 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14959 var myReader = new Roo.data.ArrayReader({
14960 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14964 * This would consume an Array like this:
14966 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14970 * Create a new JsonReader
14971 * @param {Object} meta Metadata configuration options.
14972 * @param {Object|Array} recordType Either an Array of field definition objects
14974 * @cfg {Array} fields Array of field definition objects
14975 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14976 * as specified to {@link Roo.data.Record#create},
14977 * or an {@link Roo.data.Record} object
14980 * created using {@link Roo.data.Record#create}.
14982 Roo.data.ArrayReader = function(meta, recordType)
14984 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14987 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14990 * Create a data block containing Roo.data.Records from an XML document.
14991 * @param {Object} o An Array of row objects which represents the dataset.
14992 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14993 * a cache of Roo.data.Records.
14995 readRecords : function(o)
14997 var sid = this.meta ? this.meta.id : null;
14998 var recordType = this.recordType, fields = recordType.prototype.fields;
15001 for(var i = 0; i < root.length; i++){
15004 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15005 for(var j = 0, jlen = fields.length; j < jlen; j++){
15006 var f = fields.items[j];
15007 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15008 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15010 values[f.name] = v;
15012 var record = new recordType(values, id);
15014 records[records.length] = record;
15018 totalRecords : records.length
15021 // used when loading children.. @see loadDataFromChildren
15022 toLoadData: function(rec)
15024 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15025 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15036 * @class Roo.bootstrap.ComboBox
15037 * @extends Roo.bootstrap.TriggerField
15038 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15039 * @cfg {Boolean} append (true|false) default false
15040 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15041 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15042 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15043 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15044 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15045 * @cfg {Boolean} animate default true
15046 * @cfg {Boolean} emptyResultText only for touch device
15047 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15048 * @cfg {String} emptyTitle default ''
15049 * @cfg {Number} width fixed with? experimental
15051 * Create a new ComboBox.
15052 * @param {Object} config Configuration options
15054 Roo.bootstrap.ComboBox = function(config){
15055 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15059 * Fires when the dropdown list is expanded
15060 * @param {Roo.bootstrap.ComboBox} combo This combo box
15065 * Fires when the dropdown list is collapsed
15066 * @param {Roo.bootstrap.ComboBox} combo This combo box
15070 * @event beforeselect
15071 * Fires before a list item is selected. Return false to cancel the selection.
15072 * @param {Roo.bootstrap.ComboBox} combo This combo box
15073 * @param {Roo.data.Record} record The data record returned from the underlying store
15074 * @param {Number} index The index of the selected item in the dropdown list
15076 'beforeselect' : true,
15079 * Fires when a list item is selected
15080 * @param {Roo.bootstrap.ComboBox} combo This combo box
15081 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15082 * @param {Number} index The index of the selected item in the dropdown list
15086 * @event beforequery
15087 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15088 * The event object passed has these properties:
15089 * @param {Roo.bootstrap.ComboBox} combo This combo box
15090 * @param {String} query The query
15091 * @param {Boolean} forceAll true to force "all" query
15092 * @param {Boolean} cancel true to cancel the query
15093 * @param {Object} e The query event object
15095 'beforequery': true,
15098 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15099 * @param {Roo.bootstrap.ComboBox} combo This combo box
15104 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15105 * @param {Roo.bootstrap.ComboBox} combo This combo box
15106 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15111 * Fires when the remove value from the combobox array
15112 * @param {Roo.bootstrap.ComboBox} combo This combo box
15116 * @event afterremove
15117 * Fires when the remove value from the combobox array
15118 * @param {Roo.bootstrap.ComboBox} combo This combo box
15120 'afterremove' : true,
15122 * @event specialfilter
15123 * Fires when specialfilter
15124 * @param {Roo.bootstrap.ComboBox} combo This combo box
15126 'specialfilter' : true,
15129 * Fires when tick the element
15130 * @param {Roo.bootstrap.ComboBox} combo This combo box
15134 * @event touchviewdisplay
15135 * Fires when touch view require special display (default is using displayField)
15136 * @param {Roo.bootstrap.ComboBox} combo This combo box
15137 * @param {Object} cfg set html .
15139 'touchviewdisplay' : true
15144 this.tickItems = [];
15146 this.selectedIndex = -1;
15147 if(this.mode == 'local'){
15148 if(config.queryDelay === undefined){
15149 this.queryDelay = 10;
15151 if(config.minChars === undefined){
15157 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15160 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15161 * rendering into an Roo.Editor, defaults to false)
15164 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15165 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15168 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15171 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15172 * the dropdown list (defaults to undefined, with no header element)
15176 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15180 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15182 listWidth: undefined,
15184 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15185 * mode = 'remote' or 'text' if mode = 'local')
15187 displayField: undefined,
15190 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15191 * mode = 'remote' or 'value' if mode = 'local').
15192 * Note: use of a valueField requires the user make a selection
15193 * in order for a value to be mapped.
15195 valueField: undefined,
15197 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15202 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15203 * field's data value (defaults to the underlying DOM element's name)
15205 hiddenName: undefined,
15207 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15211 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15213 selectedClass: 'active',
15216 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15220 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15221 * anchor positions (defaults to 'tl-bl')
15223 listAlign: 'tl-bl?',
15225 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15229 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15230 * query specified by the allQuery config option (defaults to 'query')
15232 triggerAction: 'query',
15234 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15235 * (defaults to 4, does not apply if editable = false)
15239 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15240 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15244 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15245 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15249 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15250 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15254 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15255 * when editable = true (defaults to false)
15257 selectOnFocus:false,
15259 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15261 queryParam: 'query',
15263 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15264 * when mode = 'remote' (defaults to 'Loading...')
15266 loadingText: 'Loading...',
15268 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15272 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15276 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15277 * traditional select (defaults to true)
15281 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15285 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15289 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15290 * listWidth has a higher value)
15294 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15295 * allow the user to set arbitrary text into the field (defaults to false)
15297 forceSelection:false,
15299 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15300 * if typeAhead = true (defaults to 250)
15302 typeAheadDelay : 250,
15304 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15305 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15307 valueNotFoundText : undefined,
15309 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15311 blockFocus : false,
15314 * @cfg {Boolean} disableClear Disable showing of clear button.
15316 disableClear : false,
15318 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15320 alwaysQuery : false,
15323 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15328 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15330 invalidClass : "has-warning",
15333 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15335 validClass : "has-success",
15338 * @cfg {Boolean} specialFilter (true|false) special filter default false
15340 specialFilter : false,
15343 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15345 mobileTouchView : true,
15348 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15350 useNativeIOS : false,
15353 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15355 mobile_restrict_height : false,
15357 ios_options : false,
15369 btnPosition : 'right',
15370 triggerList : true,
15371 showToggleBtn : true,
15373 emptyResultText: 'Empty',
15374 triggerText : 'Select',
15378 // element that contains real text value.. (when hidden is used..)
15380 getAutoCreate : function()
15385 * Render classic select for iso
15388 if(Roo.isIOS && this.useNativeIOS){
15389 cfg = this.getAutoCreateNativeIOS();
15397 if(Roo.isTouch && this.mobileTouchView){
15398 cfg = this.getAutoCreateTouchView();
15405 if(!this.tickable){
15406 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15411 * ComboBox with tickable selections
15414 var align = this.labelAlign || this.parentLabelAlign();
15417 cls : 'form-group roo-combobox-tickable' //input-group
15420 var btn_text_select = '';
15421 var btn_text_done = '';
15422 var btn_text_cancel = '';
15424 if (this.btn_text_show) {
15425 btn_text_select = 'Select';
15426 btn_text_done = 'Done';
15427 btn_text_cancel = 'Cancel';
15432 cls : 'tickable-buttons',
15437 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15438 //html : this.triggerText
15439 html: btn_text_select
15445 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15447 html: btn_text_done
15453 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15455 html: btn_text_cancel
15461 buttons.cn.unshift({
15463 cls: 'roo-select2-search-field-input'
15469 Roo.each(buttons.cn, function(c){
15471 c.cls += ' btn-' + _this.size;
15474 if (_this.disabled) {
15481 style : 'display: contents',
15486 cls: 'form-hidden-field'
15490 cls: 'roo-select2-choices',
15494 cls: 'roo-select2-search-field',
15505 cls: 'roo-select2-container input-group roo-select2-container-multi',
15511 // cls: 'typeahead typeahead-long dropdown-menu',
15512 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15517 if(this.hasFeedback && !this.allowBlank){
15521 cls: 'glyphicon form-control-feedback'
15524 combobox.cn.push(feedback);
15531 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15532 tooltip : 'This field is required'
15534 if (Roo.bootstrap.version == 4) {
15537 style : 'display:none'
15540 if (align ==='left' && this.fieldLabel.length) {
15542 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15549 cls : 'control-label col-form-label',
15550 html : this.fieldLabel
15562 var labelCfg = cfg.cn[1];
15563 var contentCfg = cfg.cn[2];
15566 if(this.indicatorpos == 'right'){
15572 cls : 'control-label col-form-label',
15576 html : this.fieldLabel
15592 labelCfg = cfg.cn[0];
15593 contentCfg = cfg.cn[1];
15597 if(this.labelWidth > 12){
15598 labelCfg.style = "width: " + this.labelWidth + 'px';
15600 if(this.width * 1 > 0){
15601 contentCfg.style = "width: " + this.width + 'px';
15603 if(this.labelWidth < 13 && this.labelmd == 0){
15604 this.labelmd = this.labelWidth;
15607 if(this.labellg > 0){
15608 labelCfg.cls += ' col-lg-' + this.labellg;
15609 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15612 if(this.labelmd > 0){
15613 labelCfg.cls += ' col-md-' + this.labelmd;
15614 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15617 if(this.labelsm > 0){
15618 labelCfg.cls += ' col-sm-' + this.labelsm;
15619 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15622 if(this.labelxs > 0){
15623 labelCfg.cls += ' col-xs-' + this.labelxs;
15624 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15628 } else if ( this.fieldLabel.length) {
15629 // Roo.log(" label");
15634 //cls : 'input-group-addon',
15635 html : this.fieldLabel
15640 if(this.indicatorpos == 'right'){
15644 //cls : 'input-group-addon',
15645 html : this.fieldLabel
15655 // Roo.log(" no label && no align");
15662 ['xs','sm','md','lg'].map(function(size){
15663 if (settings[size]) {
15664 cfg.cls += ' col-' + size + '-' + settings[size];
15672 _initEventsCalled : false,
15675 initEvents: function()
15677 if (this._initEventsCalled) { // as we call render... prevent looping...
15680 this._initEventsCalled = true;
15683 throw "can not find store for combo";
15686 this.indicator = this.indicatorEl();
15688 this.store = Roo.factory(this.store, Roo.data);
15689 this.store.parent = this;
15691 // if we are building from html. then this element is so complex, that we can not really
15692 // use the rendered HTML.
15693 // so we have to trash and replace the previous code.
15694 if (Roo.XComponent.build_from_html) {
15695 // remove this element....
15696 var e = this.el.dom, k=0;
15697 while (e ) { e = e.previousSibling; ++k;}
15702 this.rendered = false;
15704 this.render(this.parent().getChildContainer(true), k);
15707 if(Roo.isIOS && this.useNativeIOS){
15708 this.initIOSView();
15716 if(Roo.isTouch && this.mobileTouchView){
15717 this.initTouchView();
15722 this.initTickableEvents();
15726 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15728 if(this.hiddenName){
15730 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15732 this.hiddenField.dom.value =
15733 this.hiddenValue !== undefined ? this.hiddenValue :
15734 this.value !== undefined ? this.value : '';
15736 // prevent input submission
15737 this.el.dom.removeAttribute('name');
15738 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15743 // this.el.dom.setAttribute('autocomplete', 'off');
15746 var cls = 'x-combo-list';
15748 //this.list = new Roo.Layer({
15749 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15755 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15756 _this.list.setWidth(lw);
15759 this.list.on('mouseover', this.onViewOver, this);
15760 this.list.on('mousemove', this.onViewMove, this);
15761 this.list.on('scroll', this.onViewScroll, this);
15764 this.list.swallowEvent('mousewheel');
15765 this.assetHeight = 0;
15768 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15769 this.assetHeight += this.header.getHeight();
15772 this.innerList = this.list.createChild({cls:cls+'-inner'});
15773 this.innerList.on('mouseover', this.onViewOver, this);
15774 this.innerList.on('mousemove', this.onViewMove, this);
15775 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15777 if(this.allowBlank && !this.pageSize && !this.disableClear){
15778 this.footer = this.list.createChild({cls:cls+'-ft'});
15779 this.pageTb = new Roo.Toolbar(this.footer);
15783 this.footer = this.list.createChild({cls:cls+'-ft'});
15784 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15785 {pageSize: this.pageSize});
15789 if (this.pageTb && this.allowBlank && !this.disableClear) {
15791 this.pageTb.add(new Roo.Toolbar.Fill(), {
15792 cls: 'x-btn-icon x-btn-clear',
15794 handler: function()
15797 _this.clearValue();
15798 _this.onSelect(false, -1);
15803 this.assetHeight += this.footer.getHeight();
15808 this.tpl = Roo.bootstrap.version == 4 ?
15809 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15810 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15813 this.view = new Roo.View(this.list, this.tpl, {
15814 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15816 //this.view.wrapEl.setDisplayed(false);
15817 this.view.on('click', this.onViewClick, this);
15820 this.store.on('beforeload', this.onBeforeLoad, this);
15821 this.store.on('load', this.onLoad, this);
15822 this.store.on('loadexception', this.onLoadException, this);
15824 if(this.resizable){
15825 this.resizer = new Roo.Resizable(this.list, {
15826 pinned:true, handles:'se'
15828 this.resizer.on('resize', function(r, w, h){
15829 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15830 this.listWidth = w;
15831 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15832 this.restrictHeight();
15834 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15837 if(!this.editable){
15838 this.editable = true;
15839 this.setEditable(false);
15844 if (typeof(this.events.add.listeners) != 'undefined') {
15846 this.addicon = this.wrap.createChild(
15847 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15849 this.addicon.on('click', function(e) {
15850 this.fireEvent('add', this);
15853 if (typeof(this.events.edit.listeners) != 'undefined') {
15855 this.editicon = this.wrap.createChild(
15856 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15857 if (this.addicon) {
15858 this.editicon.setStyle('margin-left', '40px');
15860 this.editicon.on('click', function(e) {
15862 // we fire even if inothing is selected..
15863 this.fireEvent('edit', this, this.lastData );
15869 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15870 "up" : function(e){
15871 this.inKeyMode = true;
15875 "down" : function(e){
15876 if(!this.isExpanded()){
15877 this.onTriggerClick();
15879 this.inKeyMode = true;
15884 "enter" : function(e){
15885 // this.onViewClick();
15889 if(this.fireEvent("specialkey", this, e)){
15890 this.onViewClick(false);
15896 "esc" : function(e){
15900 "tab" : function(e){
15903 if(this.fireEvent("specialkey", this, e)){
15904 this.onViewClick(false);
15912 doRelay : function(foo, bar, hname){
15913 if(hname == 'down' || this.scope.isExpanded()){
15914 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15923 this.queryDelay = Math.max(this.queryDelay || 10,
15924 this.mode == 'local' ? 10 : 250);
15927 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15929 if(this.typeAhead){
15930 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15932 if(this.editable !== false){
15933 this.inputEl().on("keyup", this.onKeyUp, this);
15935 if(this.forceSelection){
15936 this.inputEl().on('blur', this.doForce, this);
15940 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15941 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15945 initTickableEvents: function()
15949 if(this.hiddenName){
15951 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15953 this.hiddenField.dom.value =
15954 this.hiddenValue !== undefined ? this.hiddenValue :
15955 this.value !== undefined ? this.value : '';
15957 // prevent input submission
15958 this.el.dom.removeAttribute('name');
15959 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15964 // this.list = this.el.select('ul.dropdown-menu',true).first();
15966 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15967 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15968 if(this.triggerList){
15969 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15972 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15973 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15975 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15976 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15978 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15979 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15981 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15982 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15983 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15986 this.cancelBtn.hide();
15991 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15992 _this.list.setWidth(lw);
15995 this.list.on('mouseover', this.onViewOver, this);
15996 this.list.on('mousemove', this.onViewMove, this);
15998 this.list.on('scroll', this.onViewScroll, this);
16001 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16002 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16005 this.view = new Roo.View(this.list, this.tpl, {
16010 selectedClass: this.selectedClass
16013 //this.view.wrapEl.setDisplayed(false);
16014 this.view.on('click', this.onViewClick, this);
16018 this.store.on('beforeload', this.onBeforeLoad, this);
16019 this.store.on('load', this.onLoad, this);
16020 this.store.on('loadexception', this.onLoadException, this);
16023 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16024 "up" : function(e){
16025 this.inKeyMode = true;
16029 "down" : function(e){
16030 this.inKeyMode = true;
16034 "enter" : function(e){
16035 if(this.fireEvent("specialkey", this, e)){
16036 this.onViewClick(false);
16042 "esc" : function(e){
16043 this.onTickableFooterButtonClick(e, false, false);
16046 "tab" : function(e){
16047 this.fireEvent("specialkey", this, e);
16049 this.onTickableFooterButtonClick(e, false, false);
16056 doRelay : function(e, fn, key){
16057 if(this.scope.isExpanded()){
16058 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16067 this.queryDelay = Math.max(this.queryDelay || 10,
16068 this.mode == 'local' ? 10 : 250);
16071 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16073 if(this.typeAhead){
16074 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16077 if(this.editable !== false){
16078 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16081 this.indicator = this.indicatorEl();
16083 if(this.indicator){
16084 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16085 this.indicator.hide();
16090 onDestroy : function(){
16092 this.view.setStore(null);
16093 this.view.el.removeAllListeners();
16094 this.view.el.remove();
16095 this.view.purgeListeners();
16098 this.list.dom.innerHTML = '';
16102 this.store.un('beforeload', this.onBeforeLoad, this);
16103 this.store.un('load', this.onLoad, this);
16104 this.store.un('loadexception', this.onLoadException, this);
16106 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16110 fireKey : function(e){
16111 if(e.isNavKeyPress() && !this.list.isVisible()){
16112 this.fireEvent("specialkey", this, e);
16117 onResize: function(w, h)
16121 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16123 // if(typeof w != 'number'){
16124 // // we do not handle it!?!?
16127 // var tw = this.trigger.getWidth();
16128 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16129 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16131 // this.inputEl().setWidth( this.adjustWidth('input', x));
16133 // //this.trigger.setStyle('left', x+'px');
16135 // if(this.list && this.listWidth === undefined){
16136 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16137 // this.list.setWidth(lw);
16138 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16146 * Allow or prevent the user from directly editing the field text. If false is passed,
16147 * the user will only be able to select from the items defined in the dropdown list. This method
16148 * is the runtime equivalent of setting the 'editable' config option at config time.
16149 * @param {Boolean} value True to allow the user to directly edit the field text
16151 setEditable : function(value){
16152 if(value == this.editable){
16155 this.editable = value;
16157 this.inputEl().dom.setAttribute('readOnly', true);
16158 this.inputEl().on('mousedown', this.onTriggerClick, this);
16159 this.inputEl().addClass('x-combo-noedit');
16161 this.inputEl().dom.setAttribute('readOnly', false);
16162 this.inputEl().un('mousedown', this.onTriggerClick, this);
16163 this.inputEl().removeClass('x-combo-noedit');
16169 onBeforeLoad : function(combo,opts){
16170 if(!this.hasFocus){
16174 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16176 this.restrictHeight();
16177 this.selectedIndex = -1;
16181 onLoad : function(){
16183 this.hasQuery = false;
16185 if(!this.hasFocus){
16189 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16190 this.loading.hide();
16193 if(this.store.getCount() > 0){
16196 this.restrictHeight();
16197 if(this.lastQuery == this.allQuery){
16198 if(this.editable && !this.tickable){
16199 this.inputEl().dom.select();
16203 !this.selectByValue(this.value, true) &&
16206 !this.store.lastOptions ||
16207 typeof(this.store.lastOptions.add) == 'undefined' ||
16208 this.store.lastOptions.add != true
16211 this.select(0, true);
16214 if(this.autoFocus){
16217 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16218 this.taTask.delay(this.typeAheadDelay);
16222 this.onEmptyResults();
16228 onLoadException : function()
16230 this.hasQuery = false;
16232 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16233 this.loading.hide();
16236 if(this.tickable && this.editable){
16241 // only causes errors at present
16242 //Roo.log(this.store.reader.jsonData);
16243 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16245 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16251 onTypeAhead : function(){
16252 if(this.store.getCount() > 0){
16253 var r = this.store.getAt(0);
16254 var newValue = r.data[this.displayField];
16255 var len = newValue.length;
16256 var selStart = this.getRawValue().length;
16258 if(selStart != len){
16259 this.setRawValue(newValue);
16260 this.selectText(selStart, newValue.length);
16266 onSelect : function(record, index){
16268 if(this.fireEvent('beforeselect', this, record, index) !== false){
16270 this.setFromData(index > -1 ? record.data : false);
16273 this.fireEvent('select', this, record, index);
16278 * Returns the currently selected field value or empty string if no value is set.
16279 * @return {String} value The selected value
16281 getValue : function()
16283 if(Roo.isIOS && this.useNativeIOS){
16284 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16288 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16291 if(this.valueField){
16292 return typeof this.value != 'undefined' ? this.value : '';
16294 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16298 getRawValue : function()
16300 if(Roo.isIOS && this.useNativeIOS){
16301 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16304 var v = this.inputEl().getValue();
16310 * Clears any text/value currently set in the field
16312 clearValue : function(){
16314 if(this.hiddenField){
16315 this.hiddenField.dom.value = '';
16318 this.setRawValue('');
16319 this.lastSelectionText = '';
16320 this.lastData = false;
16322 var close = this.closeTriggerEl();
16333 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16334 * will be displayed in the field. If the value does not match the data value of an existing item,
16335 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16336 * Otherwise the field will be blank (although the value will still be set).
16337 * @param {String} value The value to match
16339 setValue : function(v)
16341 if(Roo.isIOS && this.useNativeIOS){
16342 this.setIOSValue(v);
16352 if(this.valueField){
16353 var r = this.findRecord(this.valueField, v);
16355 text = r.data[this.displayField];
16356 }else if(this.valueNotFoundText !== undefined){
16357 text = this.valueNotFoundText;
16360 this.lastSelectionText = text;
16361 if(this.hiddenField){
16362 this.hiddenField.dom.value = v;
16364 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16367 var close = this.closeTriggerEl();
16370 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16376 * @property {Object} the last set data for the element
16381 * Sets the value of the field based on a object which is related to the record format for the store.
16382 * @param {Object} value the value to set as. or false on reset?
16384 setFromData : function(o){
16391 var dv = ''; // display value
16392 var vv = ''; // value value..
16394 if (this.displayField) {
16395 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16397 // this is an error condition!!!
16398 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16401 if(this.valueField){
16402 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16405 var close = this.closeTriggerEl();
16408 if(dv.length || vv * 1 > 0){
16410 this.blockFocus=true;
16416 if(this.hiddenField){
16417 this.hiddenField.dom.value = vv;
16419 this.lastSelectionText = dv;
16420 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16424 // no hidden field.. - we store the value in 'value', but still display
16425 // display field!!!!
16426 this.lastSelectionText = dv;
16427 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16434 reset : function(){
16435 // overridden so that last data is reset..
16442 this.setValue(this.originalValue);
16443 //this.clearInvalid();
16444 this.lastData = false;
16446 this.view.clearSelections();
16452 findRecord : function(prop, value){
16454 if(this.store.getCount() > 0){
16455 this.store.each(function(r){
16456 if(r.data[prop] == value){
16466 getName: function()
16468 // returns hidden if it's set..
16469 if (!this.rendered) {return ''};
16470 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16474 onViewMove : function(e, t){
16475 this.inKeyMode = false;
16479 onViewOver : function(e, t){
16480 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16483 var item = this.view.findItemFromChild(t);
16486 var index = this.view.indexOf(item);
16487 this.select(index, false);
16492 onViewClick : function(view, doFocus, el, e)
16494 var index = this.view.getSelectedIndexes()[0];
16496 var r = this.store.getAt(index);
16500 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16507 Roo.each(this.tickItems, function(v,k){
16509 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16511 _this.tickItems.splice(k, 1);
16513 if(typeof(e) == 'undefined' && view == false){
16514 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16526 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16527 this.tickItems.push(r.data);
16530 if(typeof(e) == 'undefined' && view == false){
16531 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16538 this.onSelect(r, index);
16540 if(doFocus !== false && !this.blockFocus){
16541 this.inputEl().focus();
16546 restrictHeight : function(){
16547 //this.innerList.dom.style.height = '';
16548 //var inner = this.innerList.dom;
16549 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16550 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16551 //this.list.beginUpdate();
16552 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16553 this.list.alignTo(this.inputEl(), this.listAlign);
16554 this.list.alignTo(this.inputEl(), this.listAlign);
16555 //this.list.endUpdate();
16559 onEmptyResults : function(){
16561 if(this.tickable && this.editable){
16562 this.hasFocus = false;
16563 this.restrictHeight();
16571 * Returns true if the dropdown list is expanded, else false.
16573 isExpanded : function(){
16574 return this.list.isVisible();
16578 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16579 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16580 * @param {String} value The data value of the item to select
16581 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16582 * selected item if it is not currently in view (defaults to true)
16583 * @return {Boolean} True if the value matched an item in the list, else false
16585 selectByValue : function(v, scrollIntoView){
16586 if(v !== undefined && v !== null){
16587 var r = this.findRecord(this.valueField || this.displayField, v);
16589 this.select(this.store.indexOf(r), scrollIntoView);
16597 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16598 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16599 * @param {Number} index The zero-based index of the list item to select
16600 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16601 * selected item if it is not currently in view (defaults to true)
16603 select : function(index, scrollIntoView){
16604 this.selectedIndex = index;
16605 this.view.select(index);
16606 if(scrollIntoView !== false){
16607 var el = this.view.getNode(index);
16609 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16612 this.list.scrollChildIntoView(el, false);
16618 selectNext : function(){
16619 var ct = this.store.getCount();
16621 if(this.selectedIndex == -1){
16623 }else if(this.selectedIndex < ct-1){
16624 this.select(this.selectedIndex+1);
16630 selectPrev : function(){
16631 var ct = this.store.getCount();
16633 if(this.selectedIndex == -1){
16635 }else if(this.selectedIndex != 0){
16636 this.select(this.selectedIndex-1);
16642 onKeyUp : function(e){
16643 if(this.editable !== false && !e.isSpecialKey()){
16644 this.lastKey = e.getKey();
16645 this.dqTask.delay(this.queryDelay);
16650 validateBlur : function(){
16651 return !this.list || !this.list.isVisible();
16655 initQuery : function(){
16657 var v = this.getRawValue();
16659 if(this.tickable && this.editable){
16660 v = this.tickableInputEl().getValue();
16667 doForce : function(){
16668 if(this.inputEl().dom.value.length > 0){
16669 this.inputEl().dom.value =
16670 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16676 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16677 * query allowing the query action to be canceled if needed.
16678 * @param {String} query The SQL query to execute
16679 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16680 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16681 * saved in the current store (defaults to false)
16683 doQuery : function(q, forceAll){
16685 if(q === undefined || q === null){
16690 forceAll: forceAll,
16694 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16699 forceAll = qe.forceAll;
16700 if(forceAll === true || (q.length >= this.minChars)){
16702 this.hasQuery = true;
16704 if(this.lastQuery != q || this.alwaysQuery){
16705 this.lastQuery = q;
16706 if(this.mode == 'local'){
16707 this.selectedIndex = -1;
16709 this.store.clearFilter();
16712 if(this.specialFilter){
16713 this.fireEvent('specialfilter', this);
16718 this.store.filter(this.displayField, q);
16721 this.store.fireEvent("datachanged", this.store);
16728 this.store.baseParams[this.queryParam] = q;
16730 var options = {params : this.getParams(q)};
16733 options.add = true;
16734 options.params.start = this.page * this.pageSize;
16737 this.store.load(options);
16740 * this code will make the page width larger, at the beginning, the list not align correctly,
16741 * we should expand the list on onLoad
16742 * so command out it
16747 this.selectedIndex = -1;
16752 this.loadNext = false;
16756 getParams : function(q){
16758 //p[this.queryParam] = q;
16762 p.limit = this.pageSize;
16768 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16770 collapse : function(){
16771 if(!this.isExpanded()){
16777 this.hasFocus = false;
16781 this.cancelBtn.hide();
16782 this.trigger.show();
16785 this.tickableInputEl().dom.value = '';
16786 this.tickableInputEl().blur();
16791 Roo.get(document).un('mousedown', this.collapseIf, this);
16792 Roo.get(document).un('mousewheel', this.collapseIf, this);
16793 if (!this.editable) {
16794 Roo.get(document).un('keydown', this.listKeyPress, this);
16796 this.fireEvent('collapse', this);
16802 collapseIf : function(e){
16803 var in_combo = e.within(this.el);
16804 var in_list = e.within(this.list);
16805 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16807 if (in_combo || in_list || is_list) {
16808 //e.stopPropagation();
16813 this.onTickableFooterButtonClick(e, false, false);
16821 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16823 expand : function(){
16825 if(this.isExpanded() || !this.hasFocus){
16829 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16830 this.list.setWidth(lw);
16836 this.restrictHeight();
16840 this.tickItems = Roo.apply([], this.item);
16843 this.cancelBtn.show();
16844 this.trigger.hide();
16847 this.tickableInputEl().focus();
16852 Roo.get(document).on('mousedown', this.collapseIf, this);
16853 Roo.get(document).on('mousewheel', this.collapseIf, this);
16854 if (!this.editable) {
16855 Roo.get(document).on('keydown', this.listKeyPress, this);
16858 this.fireEvent('expand', this);
16862 // Implements the default empty TriggerField.onTriggerClick function
16863 onTriggerClick : function(e)
16865 Roo.log('trigger click');
16867 if(this.disabled || !this.triggerList){
16872 this.loadNext = false;
16874 if(this.isExpanded()){
16876 if (!this.blockFocus) {
16877 this.inputEl().focus();
16881 this.hasFocus = true;
16882 if(this.triggerAction == 'all') {
16883 this.doQuery(this.allQuery, true);
16885 this.doQuery(this.getRawValue());
16887 if (!this.blockFocus) {
16888 this.inputEl().focus();
16893 onTickableTriggerClick : function(e)
16900 this.loadNext = false;
16901 this.hasFocus = true;
16903 if(this.triggerAction == 'all') {
16904 this.doQuery(this.allQuery, true);
16906 this.doQuery(this.getRawValue());
16910 onSearchFieldClick : function(e)
16912 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16913 this.onTickableFooterButtonClick(e, false, false);
16917 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16922 this.loadNext = false;
16923 this.hasFocus = true;
16925 if(this.triggerAction == 'all') {
16926 this.doQuery(this.allQuery, true);
16928 this.doQuery(this.getRawValue());
16932 listKeyPress : function(e)
16934 //Roo.log('listkeypress');
16935 // scroll to first matching element based on key pres..
16936 if (e.isSpecialKey()) {
16939 var k = String.fromCharCode(e.getKey()).toUpperCase();
16942 var csel = this.view.getSelectedNodes();
16943 var cselitem = false;
16945 var ix = this.view.indexOf(csel[0]);
16946 cselitem = this.store.getAt(ix);
16947 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16953 this.store.each(function(v) {
16955 // start at existing selection.
16956 if (cselitem.id == v.id) {
16962 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16963 match = this.store.indexOf(v);
16969 if (match === false) {
16970 return true; // no more action?
16973 this.view.select(match);
16974 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16975 sn.scrollIntoView(sn.dom.parentNode, false);
16978 onViewScroll : function(e, t){
16980 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){
16984 this.hasQuery = true;
16986 this.loading = this.list.select('.loading', true).first();
16988 if(this.loading === null){
16989 this.list.createChild({
16991 cls: 'loading roo-select2-more-results roo-select2-active',
16992 html: 'Loading more results...'
16995 this.loading = this.list.select('.loading', true).first();
16997 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16999 this.loading.hide();
17002 this.loading.show();
17007 this.loadNext = true;
17009 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17014 addItem : function(o)
17016 var dv = ''; // display value
17018 if (this.displayField) {
17019 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17021 // this is an error condition!!!
17022 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17029 var choice = this.choices.createChild({
17031 cls: 'roo-select2-search-choice',
17040 cls: 'roo-select2-search-choice-close fa fa-times',
17045 }, this.searchField);
17047 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17049 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17057 this.inputEl().dom.value = '';
17062 onRemoveItem : function(e, _self, o)
17064 e.preventDefault();
17066 this.lastItem = Roo.apply([], this.item);
17068 var index = this.item.indexOf(o.data) * 1;
17071 Roo.log('not this item?!');
17075 this.item.splice(index, 1);
17080 this.fireEvent('remove', this, e);
17086 syncValue : function()
17088 if(!this.item.length){
17095 Roo.each(this.item, function(i){
17096 if(_this.valueField){
17097 value.push(i[_this.valueField]);
17104 this.value = value.join(',');
17106 if(this.hiddenField){
17107 this.hiddenField.dom.value = this.value;
17110 this.store.fireEvent("datachanged", this.store);
17115 clearItem : function()
17117 if(!this.multiple){
17123 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17131 if(this.tickable && !Roo.isTouch){
17132 this.view.refresh();
17136 inputEl: function ()
17138 if(Roo.isIOS && this.useNativeIOS){
17139 return this.el.select('select.roo-ios-select', true).first();
17142 if(Roo.isTouch && this.mobileTouchView){
17143 return this.el.select('input.form-control',true).first();
17147 return this.searchField;
17150 return this.el.select('input.form-control',true).first();
17153 onTickableFooterButtonClick : function(e, btn, el)
17155 e.preventDefault();
17157 this.lastItem = Roo.apply([], this.item);
17159 if(btn && btn.name == 'cancel'){
17160 this.tickItems = Roo.apply([], this.item);
17169 Roo.each(this.tickItems, function(o){
17177 validate : function()
17179 if(this.getVisibilityEl().hasClass('hidden')){
17183 var v = this.getRawValue();
17186 v = this.getValue();
17189 if(this.disabled || this.allowBlank || v.length){
17194 this.markInvalid();
17198 tickableInputEl : function()
17200 if(!this.tickable || !this.editable){
17201 return this.inputEl();
17204 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17208 getAutoCreateTouchView : function()
17213 cls: 'form-group' //input-group
17219 type : this.inputType,
17220 cls : 'form-control x-combo-noedit',
17221 autocomplete: 'new-password',
17222 placeholder : this.placeholder || '',
17227 input.name = this.name;
17231 input.cls += ' input-' + this.size;
17234 if (this.disabled) {
17235 input.disabled = true;
17239 cls : 'roo-combobox-wrap',
17246 inputblock.cls += ' input-group';
17248 inputblock.cn.unshift({
17250 cls : 'input-group-addon input-group-prepend input-group-text',
17255 if(this.removable && !this.multiple){
17256 inputblock.cls += ' roo-removable';
17258 inputblock.cn.push({
17261 cls : 'roo-combo-removable-btn close'
17265 if(this.hasFeedback && !this.allowBlank){
17267 inputblock.cls += ' has-feedback';
17269 inputblock.cn.push({
17271 cls: 'glyphicon form-control-feedback'
17278 inputblock.cls += (this.before) ? '' : ' input-group';
17280 inputblock.cn.push({
17282 cls : 'input-group-addon input-group-append input-group-text',
17288 var ibwrap = inputblock;
17293 cls: 'roo-select2-choices',
17297 cls: 'roo-select2-search-field',
17310 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17315 cls: 'form-hidden-field'
17321 if(!this.multiple && this.showToggleBtn){
17327 if (this.caret != false) {
17330 cls: 'fa fa-' + this.caret
17337 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17339 Roo.bootstrap.version == 3 ? caret : '',
17342 cls: 'combobox-clear',
17356 combobox.cls += ' roo-select2-container-multi';
17359 var align = this.labelAlign || this.parentLabelAlign();
17361 if (align ==='left' && this.fieldLabel.length) {
17366 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17367 tooltip : 'This field is required'
17371 cls : 'control-label col-form-label',
17372 html : this.fieldLabel
17376 cls : 'roo-combobox-wrap ',
17383 var labelCfg = cfg.cn[1];
17384 var contentCfg = cfg.cn[2];
17387 if(this.indicatorpos == 'right'){
17392 cls : 'control-label col-form-label',
17396 html : this.fieldLabel
17400 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17401 tooltip : 'This field is required'
17406 cls : "roo-combobox-wrap ",
17414 labelCfg = cfg.cn[0];
17415 contentCfg = cfg.cn[1];
17420 if(this.labelWidth > 12){
17421 labelCfg.style = "width: " + this.labelWidth + 'px';
17424 if(this.labelWidth < 13 && this.labelmd == 0){
17425 this.labelmd = this.labelWidth;
17428 if(this.labellg > 0){
17429 labelCfg.cls += ' col-lg-' + this.labellg;
17430 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17433 if(this.labelmd > 0){
17434 labelCfg.cls += ' col-md-' + this.labelmd;
17435 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17438 if(this.labelsm > 0){
17439 labelCfg.cls += ' col-sm-' + this.labelsm;
17440 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17443 if(this.labelxs > 0){
17444 labelCfg.cls += ' col-xs-' + this.labelxs;
17445 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17449 } else if ( this.fieldLabel.length) {
17453 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17454 tooltip : 'This field is required'
17458 cls : 'control-label',
17459 html : this.fieldLabel
17470 if(this.indicatorpos == 'right'){
17474 cls : 'control-label',
17475 html : this.fieldLabel,
17479 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17480 tooltip : 'This field is required'
17497 var settings = this;
17499 ['xs','sm','md','lg'].map(function(size){
17500 if (settings[size]) {
17501 cfg.cls += ' col-' + size + '-' + settings[size];
17508 initTouchView : function()
17510 this.renderTouchView();
17512 this.touchViewEl.on('scroll', function(){
17513 this.el.dom.scrollTop = 0;
17516 this.originalValue = this.getValue();
17518 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17520 this.inputEl().on("click", this.showTouchView, this);
17521 if (this.triggerEl) {
17522 this.triggerEl.on("click", this.showTouchView, this);
17526 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17527 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17529 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17531 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17532 this.store.on('load', this.onTouchViewLoad, this);
17533 this.store.on('loadexception', this.onTouchViewLoadException, this);
17535 if(this.hiddenName){
17537 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17539 this.hiddenField.dom.value =
17540 this.hiddenValue !== undefined ? this.hiddenValue :
17541 this.value !== undefined ? this.value : '';
17543 this.el.dom.removeAttribute('name');
17544 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17548 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17549 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17552 if(this.removable && !this.multiple){
17553 var close = this.closeTriggerEl();
17555 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17556 close.on('click', this.removeBtnClick, this, close);
17560 * fix the bug in Safari iOS8
17562 this.inputEl().on("focus", function(e){
17563 document.activeElement.blur();
17566 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17573 renderTouchView : function()
17575 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17576 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17578 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17579 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17581 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17582 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17583 this.touchViewBodyEl.setStyle('overflow', 'auto');
17585 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17586 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17588 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17589 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17593 showTouchView : function()
17599 this.touchViewHeaderEl.hide();
17601 if(this.modalTitle.length){
17602 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17603 this.touchViewHeaderEl.show();
17606 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17607 this.touchViewEl.show();
17609 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17611 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17612 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17614 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17616 if(this.modalTitle.length){
17617 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17620 this.touchViewBodyEl.setHeight(bodyHeight);
17624 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17626 this.touchViewEl.addClass(['in','show']);
17629 if(this._touchViewMask){
17630 Roo.get(document.body).addClass("x-body-masked");
17631 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17632 this._touchViewMask.setStyle('z-index', 10000);
17633 this._touchViewMask.addClass('show');
17636 this.doTouchViewQuery();
17640 hideTouchView : function()
17642 this.touchViewEl.removeClass(['in','show']);
17646 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17648 this.touchViewEl.setStyle('display', 'none');
17651 if(this._touchViewMask){
17652 this._touchViewMask.removeClass('show');
17653 Roo.get(document.body).removeClass("x-body-masked");
17657 setTouchViewValue : function()
17664 Roo.each(this.tickItems, function(o){
17669 this.hideTouchView();
17672 doTouchViewQuery : function()
17681 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17685 if(!this.alwaysQuery || this.mode == 'local'){
17686 this.onTouchViewLoad();
17693 onTouchViewBeforeLoad : function(combo,opts)
17699 onTouchViewLoad : function()
17701 if(this.store.getCount() < 1){
17702 this.onTouchViewEmptyResults();
17706 this.clearTouchView();
17708 var rawValue = this.getRawValue();
17710 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17712 this.tickItems = [];
17714 this.store.data.each(function(d, rowIndex){
17715 var row = this.touchViewListGroup.createChild(template);
17717 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17718 row.addClass(d.data.cls);
17721 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17724 html : d.data[this.displayField]
17727 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17728 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17731 row.removeClass('selected');
17732 if(!this.multiple && this.valueField &&
17733 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17736 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17737 row.addClass('selected');
17740 if(this.multiple && this.valueField &&
17741 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17745 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17746 this.tickItems.push(d.data);
17749 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17753 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17755 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17757 if(this.modalTitle.length){
17758 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17761 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17763 if(this.mobile_restrict_height && listHeight < bodyHeight){
17764 this.touchViewBodyEl.setHeight(listHeight);
17769 if(firstChecked && listHeight > bodyHeight){
17770 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17775 onTouchViewLoadException : function()
17777 this.hideTouchView();
17780 onTouchViewEmptyResults : function()
17782 this.clearTouchView();
17784 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17786 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17790 clearTouchView : function()
17792 this.touchViewListGroup.dom.innerHTML = '';
17795 onTouchViewClick : function(e, el, o)
17797 e.preventDefault();
17800 var rowIndex = o.rowIndex;
17802 var r = this.store.getAt(rowIndex);
17804 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17806 if(!this.multiple){
17807 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17808 c.dom.removeAttribute('checked');
17811 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17813 this.setFromData(r.data);
17815 var close = this.closeTriggerEl();
17821 this.hideTouchView();
17823 this.fireEvent('select', this, r, rowIndex);
17828 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17829 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17830 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17834 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17835 this.addItem(r.data);
17836 this.tickItems.push(r.data);
17840 getAutoCreateNativeIOS : function()
17843 cls: 'form-group' //input-group,
17848 cls : 'roo-ios-select'
17852 combobox.name = this.name;
17855 if (this.disabled) {
17856 combobox.disabled = true;
17859 var settings = this;
17861 ['xs','sm','md','lg'].map(function(size){
17862 if (settings[size]) {
17863 cfg.cls += ' col-' + size + '-' + settings[size];
17873 initIOSView : function()
17875 this.store.on('load', this.onIOSViewLoad, this);
17880 onIOSViewLoad : function()
17882 if(this.store.getCount() < 1){
17886 this.clearIOSView();
17888 if(this.allowBlank) {
17890 var default_text = '-- SELECT --';
17892 if(this.placeholder.length){
17893 default_text = this.placeholder;
17896 if(this.emptyTitle.length){
17897 default_text += ' - ' + this.emptyTitle + ' -';
17900 var opt = this.inputEl().createChild({
17903 html : default_text
17907 o[this.valueField] = 0;
17908 o[this.displayField] = default_text;
17910 this.ios_options.push({
17917 this.store.data.each(function(d, rowIndex){
17921 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17922 html = d.data[this.displayField];
17927 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17928 value = d.data[this.valueField];
17937 if(this.value == d.data[this.valueField]){
17938 option['selected'] = true;
17941 var opt = this.inputEl().createChild(option);
17943 this.ios_options.push({
17950 this.inputEl().on('change', function(){
17951 this.fireEvent('select', this);
17956 clearIOSView: function()
17958 this.inputEl().dom.innerHTML = '';
17960 this.ios_options = [];
17963 setIOSValue: function(v)
17967 if(!this.ios_options){
17971 Roo.each(this.ios_options, function(opts){
17973 opts.el.dom.removeAttribute('selected');
17975 if(opts.data[this.valueField] != v){
17979 opts.el.dom.setAttribute('selected', true);
17985 * @cfg {Boolean} grow
17989 * @cfg {Number} growMin
17993 * @cfg {Number} growMax
18002 Roo.apply(Roo.bootstrap.ComboBox, {
18006 cls: 'modal-header',
18028 cls: 'list-group-item',
18032 cls: 'roo-combobox-list-group-item-value'
18036 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18050 listItemCheckbox : {
18052 cls: 'list-group-item',
18056 cls: 'roo-combobox-list-group-item-value'
18060 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18076 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18081 cls: 'modal-footer',
18089 cls: 'col-xs-6 text-left',
18092 cls: 'btn btn-danger roo-touch-view-cancel',
18098 cls: 'col-xs-6 text-right',
18101 cls: 'btn btn-success roo-touch-view-ok',
18112 Roo.apply(Roo.bootstrap.ComboBox, {
18114 touchViewTemplate : {
18116 cls: 'modal fade roo-combobox-touch-view',
18120 cls: 'modal-dialog',
18121 style : 'position:fixed', // we have to fix position....
18125 cls: 'modal-content',
18127 Roo.bootstrap.ComboBox.header,
18128 Roo.bootstrap.ComboBox.body,
18129 Roo.bootstrap.ComboBox.footer
18138 * Ext JS Library 1.1.1
18139 * Copyright(c) 2006-2007, Ext JS, LLC.
18141 * Originally Released Under LGPL - original licence link has changed is not relivant.
18144 * <script type="text/javascript">
18149 * @extends Roo.util.Observable
18150 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18151 * This class also supports single and multi selection modes. <br>
18152 * Create a data model bound view:
18154 var store = new Roo.data.Store(...);
18156 var view = new Roo.View({
18158 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18160 singleSelect: true,
18161 selectedClass: "ydataview-selected",
18165 // listen for node click?
18166 view.on("click", function(vw, index, node, e){
18167 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18171 dataModel.load("foobar.xml");
18173 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18175 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18176 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18178 * Note: old style constructor is still suported (container, template, config)
18181 * Create a new View
18182 * @param {Object} config The config object
18185 Roo.View = function(config, depreciated_tpl, depreciated_config){
18187 this.parent = false;
18189 if (typeof(depreciated_tpl) == 'undefined') {
18190 // new way.. - universal constructor.
18191 Roo.apply(this, config);
18192 this.el = Roo.get(this.el);
18195 this.el = Roo.get(config);
18196 this.tpl = depreciated_tpl;
18197 Roo.apply(this, depreciated_config);
18199 this.wrapEl = this.el.wrap().wrap();
18200 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18203 if(typeof(this.tpl) == "string"){
18204 this.tpl = new Roo.Template(this.tpl);
18206 // support xtype ctors..
18207 this.tpl = new Roo.factory(this.tpl, Roo);
18211 this.tpl.compile();
18216 * @event beforeclick
18217 * Fires before a click is processed. Returns false to cancel the default action.
18218 * @param {Roo.View} this
18219 * @param {Number} index The index of the target node
18220 * @param {HTMLElement} node The target node
18221 * @param {Roo.EventObject} e The raw event object
18223 "beforeclick" : true,
18226 * Fires when a template node is clicked.
18227 * @param {Roo.View} this
18228 * @param {Number} index The index of the target node
18229 * @param {HTMLElement} node The target node
18230 * @param {Roo.EventObject} e The raw event object
18235 * Fires when a template node is double clicked.
18236 * @param {Roo.View} this
18237 * @param {Number} index The index of the target node
18238 * @param {HTMLElement} node The target node
18239 * @param {Roo.EventObject} e The raw event object
18243 * @event contextmenu
18244 * Fires when a template node is right clicked.
18245 * @param {Roo.View} this
18246 * @param {Number} index The index of the target node
18247 * @param {HTMLElement} node The target node
18248 * @param {Roo.EventObject} e The raw event object
18250 "contextmenu" : true,
18252 * @event selectionchange
18253 * Fires when the selected nodes change.
18254 * @param {Roo.View} this
18255 * @param {Array} selections Array of the selected nodes
18257 "selectionchange" : true,
18260 * @event beforeselect
18261 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18262 * @param {Roo.View} this
18263 * @param {HTMLElement} node The node to be selected
18264 * @param {Array} selections Array of currently selected nodes
18266 "beforeselect" : true,
18268 * @event preparedata
18269 * Fires on every row to render, to allow you to change the data.
18270 * @param {Roo.View} this
18271 * @param {Object} data to be rendered (change this)
18273 "preparedata" : true
18281 "click": this.onClick,
18282 "dblclick": this.onDblClick,
18283 "contextmenu": this.onContextMenu,
18287 this.selections = [];
18289 this.cmp = new Roo.CompositeElementLite([]);
18291 this.store = Roo.factory(this.store, Roo.data);
18292 this.setStore(this.store, true);
18295 if ( this.footer && this.footer.xtype) {
18297 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18299 this.footer.dataSource = this.store;
18300 this.footer.container = fctr;
18301 this.footer = Roo.factory(this.footer, Roo);
18302 fctr.insertFirst(this.el);
18304 // this is a bit insane - as the paging toolbar seems to detach the el..
18305 // dom.parentNode.parentNode.parentNode
18306 // they get detached?
18310 Roo.View.superclass.constructor.call(this);
18315 Roo.extend(Roo.View, Roo.util.Observable, {
18318 * @cfg {Roo.data.Store} store Data store to load data from.
18323 * @cfg {String|Roo.Element} el The container element.
18328 * @cfg {String|Roo.Template} tpl The template used by this View
18332 * @cfg {String} dataName the named area of the template to use as the data area
18333 * Works with domtemplates roo-name="name"
18337 * @cfg {String} selectedClass The css class to add to selected nodes
18339 selectedClass : "x-view-selected",
18341 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18346 * @cfg {String} text to display on mask (default Loading)
18350 * @cfg {Boolean} multiSelect Allow multiple selection
18352 multiSelect : false,
18354 * @cfg {Boolean} singleSelect Allow single selection
18356 singleSelect: false,
18359 * @cfg {Boolean} toggleSelect - selecting
18361 toggleSelect : false,
18364 * @cfg {Boolean} tickable - selecting
18369 * Returns the element this view is bound to.
18370 * @return {Roo.Element}
18372 getEl : function(){
18373 return this.wrapEl;
18379 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18381 refresh : function(){
18382 //Roo.log('refresh');
18385 // if we are using something like 'domtemplate', then
18386 // the what gets used is:
18387 // t.applySubtemplate(NAME, data, wrapping data..)
18388 // the outer template then get' applied with
18389 // the store 'extra data'
18390 // and the body get's added to the
18391 // roo-name="data" node?
18392 // <span class='roo-tpl-{name}'></span> ?????
18396 this.clearSelections();
18397 this.el.update("");
18399 var records = this.store.getRange();
18400 if(records.length < 1) {
18402 // is this valid?? = should it render a template??
18404 this.el.update(this.emptyText);
18408 if (this.dataName) {
18409 this.el.update(t.apply(this.store.meta)); //????
18410 el = this.el.child('.roo-tpl-' + this.dataName);
18413 for(var i = 0, len = records.length; i < len; i++){
18414 var data = this.prepareData(records[i].data, i, records[i]);
18415 this.fireEvent("preparedata", this, data, i, records[i]);
18417 var d = Roo.apply({}, data);
18420 Roo.apply(d, {'roo-id' : Roo.id()});
18424 Roo.each(this.parent.item, function(item){
18425 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18428 Roo.apply(d, {'roo-data-checked' : 'checked'});
18432 html[html.length] = Roo.util.Format.trim(
18434 t.applySubtemplate(this.dataName, d, this.store.meta) :
18441 el.update(html.join(""));
18442 this.nodes = el.dom.childNodes;
18443 this.updateIndexes(0);
18448 * Function to override to reformat the data that is sent to
18449 * the template for each node.
18450 * DEPRICATED - use the preparedata event handler.
18451 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18452 * a JSON object for an UpdateManager bound view).
18454 prepareData : function(data, index, record)
18456 this.fireEvent("preparedata", this, data, index, record);
18460 onUpdate : function(ds, record){
18461 // Roo.log('on update');
18462 this.clearSelections();
18463 var index = this.store.indexOf(record);
18464 var n = this.nodes[index];
18465 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18466 n.parentNode.removeChild(n);
18467 this.updateIndexes(index, index);
18473 onAdd : function(ds, records, index)
18475 //Roo.log(['on Add', ds, records, index] );
18476 this.clearSelections();
18477 if(this.nodes.length == 0){
18481 var n = this.nodes[index];
18482 for(var i = 0, len = records.length; i < len; i++){
18483 var d = this.prepareData(records[i].data, i, records[i]);
18485 this.tpl.insertBefore(n, d);
18488 this.tpl.append(this.el, d);
18491 this.updateIndexes(index);
18494 onRemove : function(ds, record, index){
18495 // Roo.log('onRemove');
18496 this.clearSelections();
18497 var el = this.dataName ?
18498 this.el.child('.roo-tpl-' + this.dataName) :
18501 el.dom.removeChild(this.nodes[index]);
18502 this.updateIndexes(index);
18506 * Refresh an individual node.
18507 * @param {Number} index
18509 refreshNode : function(index){
18510 this.onUpdate(this.store, this.store.getAt(index));
18513 updateIndexes : function(startIndex, endIndex){
18514 var ns = this.nodes;
18515 startIndex = startIndex || 0;
18516 endIndex = endIndex || ns.length - 1;
18517 for(var i = startIndex; i <= endIndex; i++){
18518 ns[i].nodeIndex = i;
18523 * Changes the data store this view uses and refresh the view.
18524 * @param {Store} store
18526 setStore : function(store, initial){
18527 if(!initial && this.store){
18528 this.store.un("datachanged", this.refresh);
18529 this.store.un("add", this.onAdd);
18530 this.store.un("remove", this.onRemove);
18531 this.store.un("update", this.onUpdate);
18532 this.store.un("clear", this.refresh);
18533 this.store.un("beforeload", this.onBeforeLoad);
18534 this.store.un("load", this.onLoad);
18535 this.store.un("loadexception", this.onLoad);
18539 store.on("datachanged", this.refresh, this);
18540 store.on("add", this.onAdd, this);
18541 store.on("remove", this.onRemove, this);
18542 store.on("update", this.onUpdate, this);
18543 store.on("clear", this.refresh, this);
18544 store.on("beforeload", this.onBeforeLoad, this);
18545 store.on("load", this.onLoad, this);
18546 store.on("loadexception", this.onLoad, this);
18554 * onbeforeLoad - masks the loading area.
18557 onBeforeLoad : function(store,opts)
18559 //Roo.log('onBeforeLoad');
18561 this.el.update("");
18563 this.el.mask(this.mask ? this.mask : "Loading" );
18565 onLoad : function ()
18572 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18573 * @param {HTMLElement} node
18574 * @return {HTMLElement} The template node
18576 findItemFromChild : function(node){
18577 var el = this.dataName ?
18578 this.el.child('.roo-tpl-' + this.dataName,true) :
18581 if(!node || node.parentNode == el){
18584 var p = node.parentNode;
18585 while(p && p != el){
18586 if(p.parentNode == el){
18595 onClick : function(e){
18596 var item = this.findItemFromChild(e.getTarget());
18598 var index = this.indexOf(item);
18599 if(this.onItemClick(item, index, e) !== false){
18600 this.fireEvent("click", this, index, item, e);
18603 this.clearSelections();
18608 onContextMenu : function(e){
18609 var item = this.findItemFromChild(e.getTarget());
18611 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18616 onDblClick : function(e){
18617 var item = this.findItemFromChild(e.getTarget());
18619 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18623 onItemClick : function(item, index, e)
18625 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18628 if (this.toggleSelect) {
18629 var m = this.isSelected(item) ? 'unselect' : 'select';
18632 _t[m](item, true, false);
18635 if(this.multiSelect || this.singleSelect){
18636 if(this.multiSelect && e.shiftKey && this.lastSelection){
18637 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18639 this.select(item, this.multiSelect && e.ctrlKey);
18640 this.lastSelection = item;
18643 if(!this.tickable){
18644 e.preventDefault();
18652 * Get the number of selected nodes.
18655 getSelectionCount : function(){
18656 return this.selections.length;
18660 * Get the currently selected nodes.
18661 * @return {Array} An array of HTMLElements
18663 getSelectedNodes : function(){
18664 return this.selections;
18668 * Get the indexes of the selected nodes.
18671 getSelectedIndexes : function(){
18672 var indexes = [], s = this.selections;
18673 for(var i = 0, len = s.length; i < len; i++){
18674 indexes.push(s[i].nodeIndex);
18680 * Clear all selections
18681 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18683 clearSelections : function(suppressEvent){
18684 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18685 this.cmp.elements = this.selections;
18686 this.cmp.removeClass(this.selectedClass);
18687 this.selections = [];
18688 if(!suppressEvent){
18689 this.fireEvent("selectionchange", this, this.selections);
18695 * Returns true if the passed node is selected
18696 * @param {HTMLElement/Number} node The node or node index
18697 * @return {Boolean}
18699 isSelected : function(node){
18700 var s = this.selections;
18704 node = this.getNode(node);
18705 return s.indexOf(node) !== -1;
18710 * @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
18711 * @param {Boolean} keepExisting (optional) true to keep existing selections
18712 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18714 select : function(nodeInfo, keepExisting, suppressEvent){
18715 if(nodeInfo instanceof Array){
18717 this.clearSelections(true);
18719 for(var i = 0, len = nodeInfo.length; i < len; i++){
18720 this.select(nodeInfo[i], true, true);
18724 var node = this.getNode(nodeInfo);
18725 if(!node || this.isSelected(node)){
18726 return; // already selected.
18729 this.clearSelections(true);
18732 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18733 Roo.fly(node).addClass(this.selectedClass);
18734 this.selections.push(node);
18735 if(!suppressEvent){
18736 this.fireEvent("selectionchange", this, this.selections);
18744 * @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
18745 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18746 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18748 unselect : function(nodeInfo, keepExisting, suppressEvent)
18750 if(nodeInfo instanceof Array){
18751 Roo.each(this.selections, function(s) {
18752 this.unselect(s, nodeInfo);
18756 var node = this.getNode(nodeInfo);
18757 if(!node || !this.isSelected(node)){
18758 //Roo.log("not selected");
18759 return; // not selected.
18763 Roo.each(this.selections, function(s) {
18765 Roo.fly(node).removeClass(this.selectedClass);
18772 this.selections= ns;
18773 this.fireEvent("selectionchange", this, this.selections);
18777 * Gets a template node.
18778 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18779 * @return {HTMLElement} The node or null if it wasn't found
18781 getNode : function(nodeInfo){
18782 if(typeof nodeInfo == "string"){
18783 return document.getElementById(nodeInfo);
18784 }else if(typeof nodeInfo == "number"){
18785 return this.nodes[nodeInfo];
18791 * Gets a range template nodes.
18792 * @param {Number} startIndex
18793 * @param {Number} endIndex
18794 * @return {Array} An array of nodes
18796 getNodes : function(start, end){
18797 var ns = this.nodes;
18798 start = start || 0;
18799 end = typeof end == "undefined" ? ns.length - 1 : end;
18802 for(var i = start; i <= end; i++){
18806 for(var i = start; i >= end; i--){
18814 * Finds the index of the passed node
18815 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18816 * @return {Number} The index of the node or -1
18818 indexOf : function(node){
18819 node = this.getNode(node);
18820 if(typeof node.nodeIndex == "number"){
18821 return node.nodeIndex;
18823 var ns = this.nodes;
18824 for(var i = 0, len = ns.length; i < len; i++){
18835 * based on jquery fullcalendar
18839 Roo.bootstrap = Roo.bootstrap || {};
18841 * @class Roo.bootstrap.Calendar
18842 * @extends Roo.bootstrap.Component
18843 * Bootstrap Calendar class
18844 * @cfg {Boolean} loadMask (true|false) default false
18845 * @cfg {Object} header generate the user specific header of the calendar, default false
18848 * Create a new Container
18849 * @param {Object} config The config object
18854 Roo.bootstrap.Calendar = function(config){
18855 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18859 * Fires when a date is selected
18860 * @param {DatePicker} this
18861 * @param {Date} date The selected date
18865 * @event monthchange
18866 * Fires when the displayed month changes
18867 * @param {DatePicker} this
18868 * @param {Date} date The selected month
18870 'monthchange': true,
18872 * @event evententer
18873 * Fires when mouse over an event
18874 * @param {Calendar} this
18875 * @param {event} Event
18877 'evententer': true,
18879 * @event eventleave
18880 * Fires when the mouse leaves an
18881 * @param {Calendar} this
18884 'eventleave': true,
18886 * @event eventclick
18887 * Fires when the mouse click an
18888 * @param {Calendar} this
18897 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18900 * @cfg {Number} startDay
18901 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18909 getAutoCreate : function(){
18912 var fc_button = function(name, corner, style, content ) {
18913 return Roo.apply({},{
18915 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18917 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18920 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18931 style : 'width:100%',
18938 cls : 'fc-header-left',
18940 fc_button('prev', 'left', 'arrow', '‹' ),
18941 fc_button('next', 'right', 'arrow', '›' ),
18942 { tag: 'span', cls: 'fc-header-space' },
18943 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18951 cls : 'fc-header-center',
18955 cls: 'fc-header-title',
18958 html : 'month / year'
18966 cls : 'fc-header-right',
18968 /* fc_button('month', 'left', '', 'month' ),
18969 fc_button('week', '', '', 'week' ),
18970 fc_button('day', 'right', '', 'day' )
18982 header = this.header;
18985 var cal_heads = function() {
18987 // fixme - handle this.
18989 for (var i =0; i < Date.dayNames.length; i++) {
18990 var d = Date.dayNames[i];
18993 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18994 html : d.substring(0,3)
18998 ret[0].cls += ' fc-first';
18999 ret[6].cls += ' fc-last';
19002 var cal_cell = function(n) {
19005 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19010 cls: 'fc-day-number',
19014 cls: 'fc-day-content',
19018 style: 'position: relative;' // height: 17px;
19030 var cal_rows = function() {
19033 for (var r = 0; r < 6; r++) {
19040 for (var i =0; i < Date.dayNames.length; i++) {
19041 var d = Date.dayNames[i];
19042 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19045 row.cn[0].cls+=' fc-first';
19046 row.cn[0].cn[0].style = 'min-height:90px';
19047 row.cn[6].cls+=' fc-last';
19051 ret[0].cls += ' fc-first';
19052 ret[4].cls += ' fc-prev-last';
19053 ret[5].cls += ' fc-last';
19060 cls: 'fc-border-separate',
19061 style : 'width:100%',
19069 cls : 'fc-first fc-last',
19087 cls : 'fc-content',
19088 style : "position: relative;",
19091 cls : 'fc-view fc-view-month fc-grid',
19092 style : 'position: relative',
19093 unselectable : 'on',
19096 cls : 'fc-event-container',
19097 style : 'position:absolute;z-index:8;top:0;left:0;'
19115 initEvents : function()
19118 throw "can not find store for calendar";
19124 style: "text-align:center",
19128 style: "background-color:white;width:50%;margin:250 auto",
19132 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19143 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19145 var size = this.el.select('.fc-content', true).first().getSize();
19146 this.maskEl.setSize(size.width, size.height);
19147 this.maskEl.enableDisplayMode("block");
19148 if(!this.loadMask){
19149 this.maskEl.hide();
19152 this.store = Roo.factory(this.store, Roo.data);
19153 this.store.on('load', this.onLoad, this);
19154 this.store.on('beforeload', this.onBeforeLoad, this);
19158 this.cells = this.el.select('.fc-day',true);
19159 //Roo.log(this.cells);
19160 this.textNodes = this.el.query('.fc-day-number');
19161 this.cells.addClassOnOver('fc-state-hover');
19163 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19164 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19165 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19166 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19168 this.on('monthchange', this.onMonthChange, this);
19170 this.update(new Date().clearTime());
19173 resize : function() {
19174 var sz = this.el.getSize();
19176 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19177 this.el.select('.fc-day-content div',true).setHeight(34);
19182 showPrevMonth : function(e){
19183 this.update(this.activeDate.add("mo", -1));
19185 showToday : function(e){
19186 this.update(new Date().clearTime());
19189 showNextMonth : function(e){
19190 this.update(this.activeDate.add("mo", 1));
19194 showPrevYear : function(){
19195 this.update(this.activeDate.add("y", -1));
19199 showNextYear : function(){
19200 this.update(this.activeDate.add("y", 1));
19205 update : function(date)
19207 var vd = this.activeDate;
19208 this.activeDate = date;
19209 // if(vd && this.el){
19210 // var t = date.getTime();
19211 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19212 // Roo.log('using add remove');
19214 // this.fireEvent('monthchange', this, date);
19216 // this.cells.removeClass("fc-state-highlight");
19217 // this.cells.each(function(c){
19218 // if(c.dateValue == t){
19219 // c.addClass("fc-state-highlight");
19220 // setTimeout(function(){
19221 // try{c.dom.firstChild.focus();}catch(e){}
19231 var days = date.getDaysInMonth();
19233 var firstOfMonth = date.getFirstDateOfMonth();
19234 var startingPos = firstOfMonth.getDay()-this.startDay;
19236 if(startingPos < this.startDay){
19240 var pm = date.add(Date.MONTH, -1);
19241 var prevStart = pm.getDaysInMonth()-startingPos;
19243 this.cells = this.el.select('.fc-day',true);
19244 this.textNodes = this.el.query('.fc-day-number');
19245 this.cells.addClassOnOver('fc-state-hover');
19247 var cells = this.cells.elements;
19248 var textEls = this.textNodes;
19250 Roo.each(cells, function(cell){
19251 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19254 days += startingPos;
19256 // convert everything to numbers so it's fast
19257 var day = 86400000;
19258 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19261 //Roo.log(prevStart);
19263 var today = new Date().clearTime().getTime();
19264 var sel = date.clearTime().getTime();
19265 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19266 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19267 var ddMatch = this.disabledDatesRE;
19268 var ddText = this.disabledDatesText;
19269 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19270 var ddaysText = this.disabledDaysText;
19271 var format = this.format;
19273 var setCellClass = function(cal, cell){
19277 //Roo.log('set Cell Class');
19279 var t = d.getTime();
19283 cell.dateValue = t;
19285 cell.className += " fc-today";
19286 cell.className += " fc-state-highlight";
19287 cell.title = cal.todayText;
19290 // disable highlight in other month..
19291 //cell.className += " fc-state-highlight";
19296 cell.className = " fc-state-disabled";
19297 cell.title = cal.minText;
19301 cell.className = " fc-state-disabled";
19302 cell.title = cal.maxText;
19306 if(ddays.indexOf(d.getDay()) != -1){
19307 cell.title = ddaysText;
19308 cell.className = " fc-state-disabled";
19311 if(ddMatch && format){
19312 var fvalue = d.dateFormat(format);
19313 if(ddMatch.test(fvalue)){
19314 cell.title = ddText.replace("%0", fvalue);
19315 cell.className = " fc-state-disabled";
19319 if (!cell.initialClassName) {
19320 cell.initialClassName = cell.dom.className;
19323 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19328 for(; i < startingPos; i++) {
19329 textEls[i].innerHTML = (++prevStart);
19330 d.setDate(d.getDate()+1);
19332 cells[i].className = "fc-past fc-other-month";
19333 setCellClass(this, cells[i]);
19338 for(; i < days; i++){
19339 intDay = i - startingPos + 1;
19340 textEls[i].innerHTML = (intDay);
19341 d.setDate(d.getDate()+1);
19343 cells[i].className = ''; // "x-date-active";
19344 setCellClass(this, cells[i]);
19348 for(; i < 42; i++) {
19349 textEls[i].innerHTML = (++extraDays);
19350 d.setDate(d.getDate()+1);
19352 cells[i].className = "fc-future fc-other-month";
19353 setCellClass(this, cells[i]);
19356 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19358 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19360 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19361 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19363 if(totalRows != 6){
19364 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19365 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19368 this.fireEvent('monthchange', this, date);
19372 if(!this.internalRender){
19373 var main = this.el.dom.firstChild;
19374 var w = main.offsetWidth;
19375 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19376 Roo.fly(main).setWidth(w);
19377 this.internalRender = true;
19378 // opera does not respect the auto grow header center column
19379 // then, after it gets a width opera refuses to recalculate
19380 // without a second pass
19381 if(Roo.isOpera && !this.secondPass){
19382 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19383 this.secondPass = true;
19384 this.update.defer(10, this, [date]);
19391 findCell : function(dt) {
19392 dt = dt.clearTime().getTime();
19394 this.cells.each(function(c){
19395 //Roo.log("check " +c.dateValue + '?=' + dt);
19396 if(c.dateValue == dt){
19406 findCells : function(ev) {
19407 var s = ev.start.clone().clearTime().getTime();
19409 var e= ev.end.clone().clearTime().getTime();
19412 this.cells.each(function(c){
19413 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19415 if(c.dateValue > e){
19418 if(c.dateValue < s){
19427 // findBestRow: function(cells)
19431 // for (var i =0 ; i < cells.length;i++) {
19432 // ret = Math.max(cells[i].rows || 0,ret);
19439 addItem : function(ev)
19441 // look for vertical location slot in
19442 var cells = this.findCells(ev);
19444 // ev.row = this.findBestRow(cells);
19446 // work out the location.
19450 for(var i =0; i < cells.length; i++) {
19452 cells[i].row = cells[0].row;
19455 cells[i].row = cells[i].row + 1;
19465 if (crow.start.getY() == cells[i].getY()) {
19467 crow.end = cells[i];
19484 cells[0].events.push(ev);
19486 this.calevents.push(ev);
19489 clearEvents: function() {
19491 if(!this.calevents){
19495 Roo.each(this.cells.elements, function(c){
19501 Roo.each(this.calevents, function(e) {
19502 Roo.each(e.els, function(el) {
19503 el.un('mouseenter' ,this.onEventEnter, this);
19504 el.un('mouseleave' ,this.onEventLeave, this);
19509 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19515 renderEvents: function()
19519 this.cells.each(function(c) {
19528 if(c.row != c.events.length){
19529 r = 4 - (4 - (c.row - c.events.length));
19532 c.events = ev.slice(0, r);
19533 c.more = ev.slice(r);
19535 if(c.more.length && c.more.length == 1){
19536 c.events.push(c.more.pop());
19539 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19543 this.cells.each(function(c) {
19545 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19548 for (var e = 0; e < c.events.length; e++){
19549 var ev = c.events[e];
19550 var rows = ev.rows;
19552 for(var i = 0; i < rows.length; i++) {
19554 // how many rows should it span..
19557 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19558 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19560 unselectable : "on",
19563 cls: 'fc-event-inner',
19567 // cls: 'fc-event-time',
19568 // html : cells.length > 1 ? '' : ev.time
19572 cls: 'fc-event-title',
19573 html : String.format('{0}', ev.title)
19580 cls: 'ui-resizable-handle ui-resizable-e',
19581 html : '  '
19588 cfg.cls += ' fc-event-start';
19590 if ((i+1) == rows.length) {
19591 cfg.cls += ' fc-event-end';
19594 var ctr = _this.el.select('.fc-event-container',true).first();
19595 var cg = ctr.createChild(cfg);
19597 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19598 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19600 var r = (c.more.length) ? 1 : 0;
19601 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19602 cg.setWidth(ebox.right - sbox.x -2);
19604 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19605 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19606 cg.on('click', _this.onEventClick, _this, ev);
19617 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19618 style : 'position: absolute',
19619 unselectable : "on",
19622 cls: 'fc-event-inner',
19626 cls: 'fc-event-title',
19634 cls: 'ui-resizable-handle ui-resizable-e',
19635 html : '  '
19641 var ctr = _this.el.select('.fc-event-container',true).first();
19642 var cg = ctr.createChild(cfg);
19644 var sbox = c.select('.fc-day-content',true).first().getBox();
19645 var ebox = c.select('.fc-day-content',true).first().getBox();
19647 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19648 cg.setWidth(ebox.right - sbox.x -2);
19650 cg.on('click', _this.onMoreEventClick, _this, c.more);
19660 onEventEnter: function (e, el,event,d) {
19661 this.fireEvent('evententer', this, el, event);
19664 onEventLeave: function (e, el,event,d) {
19665 this.fireEvent('eventleave', this, el, event);
19668 onEventClick: function (e, el,event,d) {
19669 this.fireEvent('eventclick', this, el, event);
19672 onMonthChange: function () {
19676 onMoreEventClick: function(e, el, more)
19680 this.calpopover.placement = 'right';
19681 this.calpopover.setTitle('More');
19683 this.calpopover.setContent('');
19685 var ctr = this.calpopover.el.select('.popover-content', true).first();
19687 Roo.each(more, function(m){
19689 cls : 'fc-event-hori fc-event-draggable',
19692 var cg = ctr.createChild(cfg);
19694 cg.on('click', _this.onEventClick, _this, m);
19697 this.calpopover.show(el);
19702 onLoad: function ()
19704 this.calevents = [];
19707 if(this.store.getCount() > 0){
19708 this.store.data.each(function(d){
19711 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19712 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19713 time : d.data.start_time,
19714 title : d.data.title,
19715 description : d.data.description,
19716 venue : d.data.venue
19721 this.renderEvents();
19723 if(this.calevents.length && this.loadMask){
19724 this.maskEl.hide();
19728 onBeforeLoad: function()
19730 this.clearEvents();
19732 this.maskEl.show();
19746 * @class Roo.bootstrap.Popover
19747 * @extends Roo.bootstrap.Component
19748 * Bootstrap Popover class
19749 * @cfg {String} html contents of the popover (or false to use children..)
19750 * @cfg {String} title of popover (or false to hide)
19751 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19752 * @cfg {String} trigger click || hover (or false to trigger manually)
19753 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19754 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19755 * - if false and it has a 'parent' then it will be automatically added to that element
19756 * - if string - Roo.get will be called
19757 * @cfg {Number} delay - delay before showing
19760 * Create a new Popover
19761 * @param {Object} config The config object
19764 Roo.bootstrap.Popover = function(config){
19765 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19771 * After the popover show
19773 * @param {Roo.bootstrap.Popover} this
19778 * After the popover hide
19780 * @param {Roo.bootstrap.Popover} this
19786 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19791 placement : 'right',
19792 trigger : 'hover', // hover
19798 can_build_overlaid : false,
19800 maskEl : false, // the mask element
19803 alignEl : false, // when show is called with an element - this get's stored.
19805 getChildContainer : function()
19807 return this.contentEl;
19810 getPopoverHeader : function()
19812 this.title = true; // flag not to hide it..
19813 this.headerEl.addClass('p-0');
19814 return this.headerEl
19818 getAutoCreate : function(){
19821 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19822 style: 'display:block',
19828 cls : 'popover-inner ',
19832 cls: 'popover-title popover-header',
19833 html : this.title === false ? '' : this.title
19836 cls : 'popover-content popover-body ' + (this.cls || ''),
19837 html : this.html || ''
19848 * @param {string} the title
19850 setTitle: function(str)
19854 this.headerEl.dom.innerHTML = str;
19859 * @param {string} the body content
19861 setContent: function(str)
19864 if (this.contentEl) {
19865 this.contentEl.dom.innerHTML = str;
19869 // as it get's added to the bottom of the page.
19870 onRender : function(ct, position)
19872 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19877 var cfg = Roo.apply({}, this.getAutoCreate());
19881 cfg.cls += ' ' + this.cls;
19884 cfg.style = this.style;
19886 //Roo.log("adding to ");
19887 this.el = Roo.get(document.body).createChild(cfg, position);
19888 // Roo.log(this.el);
19891 this.contentEl = this.el.select('.popover-content',true).first();
19892 this.headerEl = this.el.select('.popover-title',true).first();
19895 if(typeof(this.items) != 'undefined'){
19896 var items = this.items;
19899 for(var i =0;i < items.length;i++) {
19900 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19904 this.items = nitems;
19906 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19907 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19914 resizeMask : function()
19916 this.maskEl.setSize(
19917 Roo.lib.Dom.getViewWidth(true),
19918 Roo.lib.Dom.getViewHeight(true)
19922 initEvents : function()
19926 Roo.bootstrap.Popover.register(this);
19929 this.arrowEl = this.el.select('.arrow',true).first();
19930 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19931 this.el.enableDisplayMode('block');
19935 if (this.over === false && !this.parent()) {
19938 if (this.triggers === false) {
19943 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19944 var triggers = this.trigger ? this.trigger.split(' ') : [];
19945 Roo.each(triggers, function(trigger) {
19947 if (trigger == 'click') {
19948 on_el.on('click', this.toggle, this);
19949 } else if (trigger != 'manual') {
19950 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19951 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19953 on_el.on(eventIn ,this.enter, this);
19954 on_el.on(eventOut, this.leave, this);
19964 toggle : function () {
19965 this.hoverState == 'in' ? this.leave() : this.enter();
19968 enter : function () {
19970 clearTimeout(this.timeout);
19972 this.hoverState = 'in';
19974 if (!this.delay || !this.delay.show) {
19979 this.timeout = setTimeout(function () {
19980 if (_t.hoverState == 'in') {
19983 }, this.delay.show)
19986 leave : function() {
19987 clearTimeout(this.timeout);
19989 this.hoverState = 'out';
19991 if (!this.delay || !this.delay.hide) {
19996 this.timeout = setTimeout(function () {
19997 if (_t.hoverState == 'out') {
20000 }, this.delay.hide)
20004 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20005 * @param {string} (left|right|top|bottom) position
20007 show : function (on_el, placement)
20009 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20010 on_el = on_el || false; // default to false
20013 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20014 on_el = this.parent().el;
20015 } else if (this.over) {
20016 Roo.get(this.over);
20021 this.alignEl = Roo.get( on_el );
20024 this.render(document.body);
20030 if (this.title === false) {
20031 this.headerEl.hide();
20036 this.el.dom.style.display = 'block';
20039 if (this.alignEl) {
20040 this.updatePosition(this.placement, true);
20043 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20044 var es = this.el.getSize();
20045 var x = Roo.lib.Dom.getViewWidth()/2;
20046 var y = Roo.lib.Dom.getViewHeight()/2;
20047 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20052 //var arrow = this.el.select('.arrow',true).first();
20053 //arrow.set(align[2],
20055 this.el.addClass('in');
20059 this.hoverState = 'in';
20062 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20063 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20064 this.maskEl.dom.style.display = 'block';
20065 this.maskEl.addClass('show');
20067 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20069 this.fireEvent('show', this);
20073 * fire this manually after loading a grid in the table for example
20074 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20075 * @param {Boolean} try and move it if we cant get right position.
20077 updatePosition : function(placement, try_move)
20079 // allow for calling with no parameters
20080 placement = placement ? placement : this.placement;
20081 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20083 this.el.removeClass([
20084 'fade','top','bottom', 'left', 'right','in',
20085 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20087 this.el.addClass(placement + ' bs-popover-' + placement);
20089 if (!this.alignEl ) {
20093 switch (placement) {
20095 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20096 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20097 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20098 //normal display... or moved up/down.
20099 this.el.setXY(offset);
20100 var xy = this.alignEl.getAnchorXY('tr', false);
20102 this.arrowEl.setXY(xy);
20105 // continue through...
20106 return this.updatePosition('left', false);
20110 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20111 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20112 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20113 //normal display... or moved up/down.
20114 this.el.setXY(offset);
20115 var xy = this.alignEl.getAnchorXY('tl', false);
20116 xy[0]-=10;xy[1]+=5; // << fix me
20117 this.arrowEl.setXY(xy);
20121 return this.updatePosition('right', false);
20124 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20125 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20126 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20127 //normal display... or moved up/down.
20128 this.el.setXY(offset);
20129 var xy = this.alignEl.getAnchorXY('t', false);
20130 xy[1]-=10; // << fix me
20131 this.arrowEl.setXY(xy);
20135 return this.updatePosition('bottom', false);
20138 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20139 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20140 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20141 //normal display... or moved up/down.
20142 this.el.setXY(offset);
20143 var xy = this.alignEl.getAnchorXY('b', false);
20144 xy[1]+=2; // << fix me
20145 this.arrowEl.setXY(xy);
20149 return this.updatePosition('top', false);
20160 this.el.setXY([0,0]);
20161 this.el.removeClass('in');
20163 this.hoverState = null;
20164 this.maskEl.hide(); // always..
20165 this.fireEvent('hide', this);
20171 Roo.apply(Roo.bootstrap.Popover, {
20174 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20175 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20176 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20177 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20182 clickHander : false,
20185 onMouseDown : function(e)
20187 if (!e.getTarget(".roo-popover")) {
20195 register : function(popup)
20197 if (!Roo.bootstrap.Popover.clickHandler) {
20198 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20200 // hide other popups.
20202 this.popups.push(popup);
20204 hideAll : function()
20206 this.popups.forEach(function(p) {
20214 * Card header - holder for the card header elements.
20219 * @class Roo.bootstrap.PopoverNav
20220 * @extends Roo.bootstrap.NavGroup
20221 * Bootstrap Popover header navigation class
20223 * Create a new Popover Header Navigation
20224 * @param {Object} config The config object
20227 Roo.bootstrap.PopoverNav = function(config){
20228 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20231 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20234 container_method : 'getPopoverHeader'
20252 * @class Roo.bootstrap.Progress
20253 * @extends Roo.bootstrap.Component
20254 * Bootstrap Progress class
20255 * @cfg {Boolean} striped striped of the progress bar
20256 * @cfg {Boolean} active animated of the progress bar
20260 * Create a new Progress
20261 * @param {Object} config The config object
20264 Roo.bootstrap.Progress = function(config){
20265 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20268 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20273 getAutoCreate : function(){
20281 cfg.cls += ' progress-striped';
20285 cfg.cls += ' active';
20304 * @class Roo.bootstrap.ProgressBar
20305 * @extends Roo.bootstrap.Component
20306 * Bootstrap ProgressBar class
20307 * @cfg {Number} aria_valuenow aria-value now
20308 * @cfg {Number} aria_valuemin aria-value min
20309 * @cfg {Number} aria_valuemax aria-value max
20310 * @cfg {String} label label for the progress bar
20311 * @cfg {String} panel (success | info | warning | danger )
20312 * @cfg {String} role role of the progress bar
20313 * @cfg {String} sr_only text
20317 * Create a new ProgressBar
20318 * @param {Object} config The config object
20321 Roo.bootstrap.ProgressBar = function(config){
20322 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20325 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20329 aria_valuemax : 100,
20335 getAutoCreate : function()
20340 cls: 'progress-bar',
20341 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20353 cfg.role = this.role;
20356 if(this.aria_valuenow){
20357 cfg['aria-valuenow'] = this.aria_valuenow;
20360 if(this.aria_valuemin){
20361 cfg['aria-valuemin'] = this.aria_valuemin;
20364 if(this.aria_valuemax){
20365 cfg['aria-valuemax'] = this.aria_valuemax;
20368 if(this.label && !this.sr_only){
20369 cfg.html = this.label;
20373 cfg.cls += ' progress-bar-' + this.panel;
20379 update : function(aria_valuenow)
20381 this.aria_valuenow = aria_valuenow;
20383 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20398 * @class Roo.bootstrap.TabGroup
20399 * @extends Roo.bootstrap.Column
20400 * Bootstrap Column class
20401 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20402 * @cfg {Boolean} carousel true to make the group behave like a carousel
20403 * @cfg {Boolean} bullets show bullets for the panels
20404 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20405 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20406 * @cfg {Boolean} showarrow (true|false) show arrow default true
20409 * Create a new TabGroup
20410 * @param {Object} config The config object
20413 Roo.bootstrap.TabGroup = function(config){
20414 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20416 this.navId = Roo.id();
20419 Roo.bootstrap.TabGroup.register(this);
20423 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20426 transition : false,
20431 slideOnTouch : false,
20434 getAutoCreate : function()
20436 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20438 cfg.cls += ' tab-content';
20440 if (this.carousel) {
20441 cfg.cls += ' carousel slide';
20444 cls : 'carousel-inner',
20448 if(this.bullets && !Roo.isTouch){
20451 cls : 'carousel-bullets',
20455 if(this.bullets_cls){
20456 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20463 cfg.cn[0].cn.push(bullets);
20466 if(this.showarrow){
20467 cfg.cn[0].cn.push({
20469 class : 'carousel-arrow',
20473 class : 'carousel-prev',
20477 class : 'fa fa-chevron-left'
20483 class : 'carousel-next',
20487 class : 'fa fa-chevron-right'
20500 initEvents: function()
20502 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20503 // this.el.on("touchstart", this.onTouchStart, this);
20506 if(this.autoslide){
20509 this.slideFn = window.setInterval(function() {
20510 _this.showPanelNext();
20514 if(this.showarrow){
20515 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20516 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20522 // onTouchStart : function(e, el, o)
20524 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20528 // this.showPanelNext();
20532 getChildContainer : function()
20534 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20538 * register a Navigation item
20539 * @param {Roo.bootstrap.NavItem} the navitem to add
20541 register : function(item)
20543 this.tabs.push( item);
20544 item.navId = this.navId; // not really needed..
20549 getActivePanel : function()
20552 Roo.each(this.tabs, function(t) {
20562 getPanelByName : function(n)
20565 Roo.each(this.tabs, function(t) {
20566 if (t.tabId == n) {
20574 indexOfPanel : function(p)
20577 Roo.each(this.tabs, function(t,i) {
20578 if (t.tabId == p.tabId) {
20587 * show a specific panel
20588 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20589 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20591 showPanel : function (pan)
20593 if(this.transition || typeof(pan) == 'undefined'){
20594 Roo.log("waiting for the transitionend");
20598 if (typeof(pan) == 'number') {
20599 pan = this.tabs[pan];
20602 if (typeof(pan) == 'string') {
20603 pan = this.getPanelByName(pan);
20606 var cur = this.getActivePanel();
20609 Roo.log('pan or acitve pan is undefined');
20613 if (pan.tabId == this.getActivePanel().tabId) {
20617 if (false === cur.fireEvent('beforedeactivate')) {
20621 if(this.bullets > 0 && !Roo.isTouch){
20622 this.setActiveBullet(this.indexOfPanel(pan));
20625 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20627 //class="carousel-item carousel-item-next carousel-item-left"
20629 this.transition = true;
20630 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20631 var lr = dir == 'next' ? 'left' : 'right';
20632 pan.el.addClass(dir); // or prev
20633 pan.el.addClass('carousel-item-' + dir); // or prev
20634 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20635 cur.el.addClass(lr); // or right
20636 pan.el.addClass(lr);
20637 cur.el.addClass('carousel-item-' +lr); // or right
20638 pan.el.addClass('carousel-item-' +lr);
20642 cur.el.on('transitionend', function() {
20643 Roo.log("trans end?");
20645 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20646 pan.setActive(true);
20648 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20649 cur.setActive(false);
20651 _this.transition = false;
20653 }, this, { single: true } );
20658 cur.setActive(false);
20659 pan.setActive(true);
20664 showPanelNext : function()
20666 var i = this.indexOfPanel(this.getActivePanel());
20668 if (i >= this.tabs.length - 1 && !this.autoslide) {
20672 if (i >= this.tabs.length - 1 && this.autoslide) {
20676 this.showPanel(this.tabs[i+1]);
20679 showPanelPrev : function()
20681 var i = this.indexOfPanel(this.getActivePanel());
20683 if (i < 1 && !this.autoslide) {
20687 if (i < 1 && this.autoslide) {
20688 i = this.tabs.length;
20691 this.showPanel(this.tabs[i-1]);
20695 addBullet: function()
20697 if(!this.bullets || Roo.isTouch){
20700 var ctr = this.el.select('.carousel-bullets',true).first();
20701 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20702 var bullet = ctr.createChild({
20703 cls : 'bullet bullet-' + i
20704 },ctr.dom.lastChild);
20709 bullet.on('click', (function(e, el, o, ii, t){
20711 e.preventDefault();
20713 this.showPanel(ii);
20715 if(this.autoslide && this.slideFn){
20716 clearInterval(this.slideFn);
20717 this.slideFn = window.setInterval(function() {
20718 _this.showPanelNext();
20722 }).createDelegate(this, [i, bullet], true));
20727 setActiveBullet : function(i)
20733 Roo.each(this.el.select('.bullet', true).elements, function(el){
20734 el.removeClass('selected');
20737 var bullet = this.el.select('.bullet-' + i, true).first();
20743 bullet.addClass('selected');
20754 Roo.apply(Roo.bootstrap.TabGroup, {
20758 * register a Navigation Group
20759 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20761 register : function(navgrp)
20763 this.groups[navgrp.navId] = navgrp;
20767 * fetch a Navigation Group based on the navigation ID
20768 * if one does not exist , it will get created.
20769 * @param {string} the navgroup to add
20770 * @returns {Roo.bootstrap.NavGroup} the navgroup
20772 get: function(navId) {
20773 if (typeof(this.groups[navId]) == 'undefined') {
20774 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20776 return this.groups[navId] ;
20791 * @class Roo.bootstrap.TabPanel
20792 * @extends Roo.bootstrap.Component
20793 * Bootstrap TabPanel class
20794 * @cfg {Boolean} active panel active
20795 * @cfg {String} html panel content
20796 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20797 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20798 * @cfg {String} href click to link..
20799 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20803 * Create a new TabPanel
20804 * @param {Object} config The config object
20807 Roo.bootstrap.TabPanel = function(config){
20808 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20812 * Fires when the active status changes
20813 * @param {Roo.bootstrap.TabPanel} this
20814 * @param {Boolean} state the new state
20819 * @event beforedeactivate
20820 * Fires before a tab is de-activated - can be used to do validation on a form.
20821 * @param {Roo.bootstrap.TabPanel} this
20822 * @return {Boolean} false if there is an error
20825 'beforedeactivate': true
20828 this.tabId = this.tabId || Roo.id();
20832 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20839 touchSlide : false,
20840 getAutoCreate : function(){
20845 // item is needed for carousel - not sure if it has any effect otherwise
20846 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20847 html: this.html || ''
20851 cfg.cls += ' active';
20855 cfg.tabId = this.tabId;
20863 initEvents: function()
20865 var p = this.parent();
20867 this.navId = this.navId || p.navId;
20869 if (typeof(this.navId) != 'undefined') {
20870 // not really needed.. but just in case.. parent should be a NavGroup.
20871 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20875 var i = tg.tabs.length - 1;
20877 if(this.active && tg.bullets > 0 && i < tg.bullets){
20878 tg.setActiveBullet(i);
20882 this.el.on('click', this.onClick, this);
20884 if(Roo.isTouch && this.touchSlide){
20885 this.el.on("touchstart", this.onTouchStart, this);
20886 this.el.on("touchmove", this.onTouchMove, this);
20887 this.el.on("touchend", this.onTouchEnd, this);
20892 onRender : function(ct, position)
20894 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20897 setActive : function(state)
20899 Roo.log("panel - set active " + this.tabId + "=" + state);
20901 this.active = state;
20903 this.el.removeClass('active');
20905 } else if (!this.el.hasClass('active')) {
20906 this.el.addClass('active');
20909 this.fireEvent('changed', this, state);
20912 onClick : function(e)
20914 e.preventDefault();
20916 if(!this.href.length){
20920 window.location.href = this.href;
20929 onTouchStart : function(e)
20931 this.swiping = false;
20933 this.startX = e.browserEvent.touches[0].clientX;
20934 this.startY = e.browserEvent.touches[0].clientY;
20937 onTouchMove : function(e)
20939 this.swiping = true;
20941 this.endX = e.browserEvent.touches[0].clientX;
20942 this.endY = e.browserEvent.touches[0].clientY;
20945 onTouchEnd : function(e)
20952 var tabGroup = this.parent();
20954 if(this.endX > this.startX){ // swiping right
20955 tabGroup.showPanelPrev();
20959 if(this.startX > this.endX){ // swiping left
20960 tabGroup.showPanelNext();
20979 * @class Roo.bootstrap.DateField
20980 * @extends Roo.bootstrap.Input
20981 * Bootstrap DateField class
20982 * @cfg {Number} weekStart default 0
20983 * @cfg {String} viewMode default empty, (months|years)
20984 * @cfg {String} minViewMode default empty, (months|years)
20985 * @cfg {Number} startDate default -Infinity
20986 * @cfg {Number} endDate default Infinity
20987 * @cfg {Boolean} todayHighlight default false
20988 * @cfg {Boolean} todayBtn default false
20989 * @cfg {Boolean} calendarWeeks default false
20990 * @cfg {Object} daysOfWeekDisabled default empty
20991 * @cfg {Boolean} singleMode default false (true | false)
20993 * @cfg {Boolean} keyboardNavigation default true
20994 * @cfg {String} language default en
20997 * Create a new DateField
20998 * @param {Object} config The config object
21001 Roo.bootstrap.DateField = function(config){
21002 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21006 * Fires when this field show.
21007 * @param {Roo.bootstrap.DateField} this
21008 * @param {Mixed} date The date value
21013 * Fires when this field hide.
21014 * @param {Roo.bootstrap.DateField} this
21015 * @param {Mixed} date The date value
21020 * Fires when select a date.
21021 * @param {Roo.bootstrap.DateField} this
21022 * @param {Mixed} date The date value
21026 * @event beforeselect
21027 * Fires when before select a date.
21028 * @param {Roo.bootstrap.DateField} this
21029 * @param {Mixed} date The date value
21031 beforeselect : true
21035 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21038 * @cfg {String} format
21039 * The default date format string which can be overriden for localization support. The format must be
21040 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21044 * @cfg {String} altFormats
21045 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21046 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21048 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21056 todayHighlight : false,
21062 keyboardNavigation: true,
21064 calendarWeeks: false,
21066 startDate: -Infinity,
21070 daysOfWeekDisabled: [],
21074 singleMode : false,
21076 UTCDate: function()
21078 return new Date(Date.UTC.apply(Date, arguments));
21081 UTCToday: function()
21083 var today = new Date();
21084 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21087 getDate: function() {
21088 var d = this.getUTCDate();
21089 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21092 getUTCDate: function() {
21096 setDate: function(d) {
21097 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21100 setUTCDate: function(d) {
21102 this.setValue(this.formatDate(this.date));
21105 onRender: function(ct, position)
21108 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21110 this.language = this.language || 'en';
21111 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21112 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21114 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21115 this.format = this.format || 'm/d/y';
21116 this.isInline = false;
21117 this.isInput = true;
21118 this.component = this.el.select('.add-on', true).first() || false;
21119 this.component = (this.component && this.component.length === 0) ? false : this.component;
21120 this.hasInput = this.component && this.inputEl().length;
21122 if (typeof(this.minViewMode === 'string')) {
21123 switch (this.minViewMode) {
21125 this.minViewMode = 1;
21128 this.minViewMode = 2;
21131 this.minViewMode = 0;
21136 if (typeof(this.viewMode === 'string')) {
21137 switch (this.viewMode) {
21150 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21152 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21154 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21156 this.picker().on('mousedown', this.onMousedown, this);
21157 this.picker().on('click', this.onClick, this);
21159 this.picker().addClass('datepicker-dropdown');
21161 this.startViewMode = this.viewMode;
21163 if(this.singleMode){
21164 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21165 v.setVisibilityMode(Roo.Element.DISPLAY);
21169 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21170 v.setStyle('width', '189px');
21174 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21175 if(!this.calendarWeeks){
21180 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21181 v.attr('colspan', function(i, val){
21182 return parseInt(val) + 1;
21187 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21189 this.setStartDate(this.startDate);
21190 this.setEndDate(this.endDate);
21192 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21199 if(this.isInline) {
21204 picker : function()
21206 return this.pickerEl;
21207 // return this.el.select('.datepicker', true).first();
21210 fillDow: function()
21212 var dowCnt = this.weekStart;
21221 if(this.calendarWeeks){
21229 while (dowCnt < this.weekStart + 7) {
21233 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21237 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21240 fillMonths: function()
21243 var months = this.picker().select('>.datepicker-months td', true).first();
21245 months.dom.innerHTML = '';
21251 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21254 months.createChild(month);
21261 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;
21263 if (this.date < this.startDate) {
21264 this.viewDate = new Date(this.startDate);
21265 } else if (this.date > this.endDate) {
21266 this.viewDate = new Date(this.endDate);
21268 this.viewDate = new Date(this.date);
21276 var d = new Date(this.viewDate),
21277 year = d.getUTCFullYear(),
21278 month = d.getUTCMonth(),
21279 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21280 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21281 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21282 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21283 currentDate = this.date && this.date.valueOf(),
21284 today = this.UTCToday();
21286 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21288 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21290 // this.picker.select('>tfoot th.today').
21291 // .text(dates[this.language].today)
21292 // .toggle(this.todayBtn !== false);
21294 this.updateNavArrows();
21297 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21299 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21301 prevMonth.setUTCDate(day);
21303 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21305 var nextMonth = new Date(prevMonth);
21307 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21309 nextMonth = nextMonth.valueOf();
21311 var fillMonths = false;
21313 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21315 while(prevMonth.valueOf() <= nextMonth) {
21318 if (prevMonth.getUTCDay() === this.weekStart) {
21320 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21328 if(this.calendarWeeks){
21329 // ISO 8601: First week contains first thursday.
21330 // ISO also states week starts on Monday, but we can be more abstract here.
21332 // Start of current week: based on weekstart/current date
21333 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21334 // Thursday of this week
21335 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21336 // First Thursday of year, year from thursday
21337 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21338 // Calendar week: ms between thursdays, div ms per day, div 7 days
21339 calWeek = (th - yth) / 864e5 / 7 + 1;
21341 fillMonths.cn.push({
21349 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21351 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21354 if (this.todayHighlight &&
21355 prevMonth.getUTCFullYear() == today.getFullYear() &&
21356 prevMonth.getUTCMonth() == today.getMonth() &&
21357 prevMonth.getUTCDate() == today.getDate()) {
21358 clsName += ' today';
21361 if (currentDate && prevMonth.valueOf() === currentDate) {
21362 clsName += ' active';
21365 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21366 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21367 clsName += ' disabled';
21370 fillMonths.cn.push({
21372 cls: 'day ' + clsName,
21373 html: prevMonth.getDate()
21376 prevMonth.setDate(prevMonth.getDate()+1);
21379 var currentYear = this.date && this.date.getUTCFullYear();
21380 var currentMonth = this.date && this.date.getUTCMonth();
21382 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21384 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21385 v.removeClass('active');
21387 if(currentYear === year && k === currentMonth){
21388 v.addClass('active');
21391 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21392 v.addClass('disabled');
21398 year = parseInt(year/10, 10) * 10;
21400 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21402 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21405 for (var i = -1; i < 11; i++) {
21406 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21408 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21416 showMode: function(dir)
21419 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21422 Roo.each(this.picker().select('>div',true).elements, function(v){
21423 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21426 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21431 if(this.isInline) {
21435 this.picker().removeClass(['bottom', 'top']);
21437 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21439 * place to the top of element!
21443 this.picker().addClass('top');
21444 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21449 this.picker().addClass('bottom');
21451 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21454 parseDate : function(value)
21456 if(!value || value instanceof Date){
21459 var v = Date.parseDate(value, this.format);
21460 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21461 v = Date.parseDate(value, 'Y-m-d');
21463 if(!v && this.altFormats){
21464 if(!this.altFormatsArray){
21465 this.altFormatsArray = this.altFormats.split("|");
21467 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21468 v = Date.parseDate(value, this.altFormatsArray[i]);
21474 formatDate : function(date, fmt)
21476 return (!date || !(date instanceof Date)) ?
21477 date : date.dateFormat(fmt || this.format);
21480 onFocus : function()
21482 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21486 onBlur : function()
21488 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21490 var d = this.inputEl().getValue();
21497 showPopup : function()
21499 this.picker().show();
21503 this.fireEvent('showpopup', this, this.date);
21506 hidePopup : function()
21508 if(this.isInline) {
21511 this.picker().hide();
21512 this.viewMode = this.startViewMode;
21515 this.fireEvent('hidepopup', this, this.date);
21519 onMousedown: function(e)
21521 e.stopPropagation();
21522 e.preventDefault();
21527 Roo.bootstrap.DateField.superclass.keyup.call(this);
21531 setValue: function(v)
21533 if(this.fireEvent('beforeselect', this, v) !== false){
21534 var d = new Date(this.parseDate(v) ).clearTime();
21536 if(isNaN(d.getTime())){
21537 this.date = this.viewDate = '';
21538 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21542 v = this.formatDate(d);
21544 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21546 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21550 this.fireEvent('select', this, this.date);
21554 getValue: function()
21556 return this.formatDate(this.date);
21559 fireKey: function(e)
21561 if (!this.picker().isVisible()){
21562 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21568 var dateChanged = false,
21570 newDate, newViewDate;
21575 e.preventDefault();
21579 if (!this.keyboardNavigation) {
21582 dir = e.keyCode == 37 ? -1 : 1;
21585 newDate = this.moveYear(this.date, dir);
21586 newViewDate = this.moveYear(this.viewDate, dir);
21587 } else if (e.shiftKey){
21588 newDate = this.moveMonth(this.date, dir);
21589 newViewDate = this.moveMonth(this.viewDate, dir);
21591 newDate = new Date(this.date);
21592 newDate.setUTCDate(this.date.getUTCDate() + dir);
21593 newViewDate = new Date(this.viewDate);
21594 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21596 if (this.dateWithinRange(newDate)){
21597 this.date = newDate;
21598 this.viewDate = newViewDate;
21599 this.setValue(this.formatDate(this.date));
21601 e.preventDefault();
21602 dateChanged = true;
21607 if (!this.keyboardNavigation) {
21610 dir = e.keyCode == 38 ? -1 : 1;
21612 newDate = this.moveYear(this.date, dir);
21613 newViewDate = this.moveYear(this.viewDate, dir);
21614 } else if (e.shiftKey){
21615 newDate = this.moveMonth(this.date, dir);
21616 newViewDate = this.moveMonth(this.viewDate, dir);
21618 newDate = new Date(this.date);
21619 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21620 newViewDate = new Date(this.viewDate);
21621 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21623 if (this.dateWithinRange(newDate)){
21624 this.date = newDate;
21625 this.viewDate = newViewDate;
21626 this.setValue(this.formatDate(this.date));
21628 e.preventDefault();
21629 dateChanged = true;
21633 this.setValue(this.formatDate(this.date));
21635 e.preventDefault();
21638 this.setValue(this.formatDate(this.date));
21652 onClick: function(e)
21654 e.stopPropagation();
21655 e.preventDefault();
21657 var target = e.getTarget();
21659 if(target.nodeName.toLowerCase() === 'i'){
21660 target = Roo.get(target).dom.parentNode;
21663 var nodeName = target.nodeName;
21664 var className = target.className;
21665 var html = target.innerHTML;
21666 //Roo.log(nodeName);
21668 switch(nodeName.toLowerCase()) {
21670 switch(className) {
21676 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21677 switch(this.viewMode){
21679 this.viewDate = this.moveMonth(this.viewDate, dir);
21683 this.viewDate = this.moveYear(this.viewDate, dir);
21689 var date = new Date();
21690 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21692 this.setValue(this.formatDate(this.date));
21699 if (className.indexOf('disabled') < 0) {
21700 this.viewDate.setUTCDate(1);
21701 if (className.indexOf('month') > -1) {
21702 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21704 var year = parseInt(html, 10) || 0;
21705 this.viewDate.setUTCFullYear(year);
21709 if(this.singleMode){
21710 this.setValue(this.formatDate(this.viewDate));
21721 //Roo.log(className);
21722 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21723 var day = parseInt(html, 10) || 1;
21724 var year = (this.viewDate || new Date()).getUTCFullYear(),
21725 month = (this.viewDate || new Date()).getUTCMonth();
21727 if (className.indexOf('old') > -1) {
21734 } else if (className.indexOf('new') > -1) {
21742 //Roo.log([year,month,day]);
21743 this.date = this.UTCDate(year, month, day,0,0,0,0);
21744 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21746 //Roo.log(this.formatDate(this.date));
21747 this.setValue(this.formatDate(this.date));
21754 setStartDate: function(startDate)
21756 this.startDate = startDate || -Infinity;
21757 if (this.startDate !== -Infinity) {
21758 this.startDate = this.parseDate(this.startDate);
21761 this.updateNavArrows();
21764 setEndDate: function(endDate)
21766 this.endDate = endDate || Infinity;
21767 if (this.endDate !== Infinity) {
21768 this.endDate = this.parseDate(this.endDate);
21771 this.updateNavArrows();
21774 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21776 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21777 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21778 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21780 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21781 return parseInt(d, 10);
21784 this.updateNavArrows();
21787 updateNavArrows: function()
21789 if(this.singleMode){
21793 var d = new Date(this.viewDate),
21794 year = d.getUTCFullYear(),
21795 month = d.getUTCMonth();
21797 Roo.each(this.picker().select('.prev', true).elements, function(v){
21799 switch (this.viewMode) {
21802 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21808 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21815 Roo.each(this.picker().select('.next', true).elements, function(v){
21817 switch (this.viewMode) {
21820 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21826 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21834 moveMonth: function(date, dir)
21839 var new_date = new Date(date.valueOf()),
21840 day = new_date.getUTCDate(),
21841 month = new_date.getUTCMonth(),
21842 mag = Math.abs(dir),
21844 dir = dir > 0 ? 1 : -1;
21847 // If going back one month, make sure month is not current month
21848 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21850 return new_date.getUTCMonth() == month;
21852 // If going forward one month, make sure month is as expected
21853 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21855 return new_date.getUTCMonth() != new_month;
21857 new_month = month + dir;
21858 new_date.setUTCMonth(new_month);
21859 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21860 if (new_month < 0 || new_month > 11) {
21861 new_month = (new_month + 12) % 12;
21864 // For magnitudes >1, move one month at a time...
21865 for (var i=0; i<mag; i++) {
21866 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21867 new_date = this.moveMonth(new_date, dir);
21869 // ...then reset the day, keeping it in the new month
21870 new_month = new_date.getUTCMonth();
21871 new_date.setUTCDate(day);
21873 return new_month != new_date.getUTCMonth();
21876 // Common date-resetting loop -- if date is beyond end of month, make it
21879 new_date.setUTCDate(--day);
21880 new_date.setUTCMonth(new_month);
21885 moveYear: function(date, dir)
21887 return this.moveMonth(date, dir*12);
21890 dateWithinRange: function(date)
21892 return date >= this.startDate && date <= this.endDate;
21898 this.picker().remove();
21901 validateValue : function(value)
21903 if(this.getVisibilityEl().hasClass('hidden')){
21907 if(value.length < 1) {
21908 if(this.allowBlank){
21914 if(value.length < this.minLength){
21917 if(value.length > this.maxLength){
21921 var vt = Roo.form.VTypes;
21922 if(!vt[this.vtype](value, this)){
21926 if(typeof this.validator == "function"){
21927 var msg = this.validator(value);
21933 if(this.regex && !this.regex.test(value)){
21937 if(typeof(this.parseDate(value)) == 'undefined'){
21941 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21945 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21955 this.date = this.viewDate = '';
21957 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21962 Roo.apply(Roo.bootstrap.DateField, {
21973 html: '<i class="fa fa-arrow-left"/>'
21983 html: '<i class="fa fa-arrow-right"/>'
22025 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22026 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22027 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22028 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22029 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22042 navFnc: 'FullYear',
22047 navFnc: 'FullYear',
22052 Roo.apply(Roo.bootstrap.DateField, {
22056 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22060 cls: 'datepicker-days',
22064 cls: 'table-condensed',
22066 Roo.bootstrap.DateField.head,
22070 Roo.bootstrap.DateField.footer
22077 cls: 'datepicker-months',
22081 cls: 'table-condensed',
22083 Roo.bootstrap.DateField.head,
22084 Roo.bootstrap.DateField.content,
22085 Roo.bootstrap.DateField.footer
22092 cls: 'datepicker-years',
22096 cls: 'table-condensed',
22098 Roo.bootstrap.DateField.head,
22099 Roo.bootstrap.DateField.content,
22100 Roo.bootstrap.DateField.footer
22119 * @class Roo.bootstrap.TimeField
22120 * @extends Roo.bootstrap.Input
22121 * Bootstrap DateField class
22125 * Create a new TimeField
22126 * @param {Object} config The config object
22129 Roo.bootstrap.TimeField = function(config){
22130 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22134 * Fires when this field show.
22135 * @param {Roo.bootstrap.DateField} thisthis
22136 * @param {Mixed} date The date value
22141 * Fires when this field hide.
22142 * @param {Roo.bootstrap.DateField} this
22143 * @param {Mixed} date The date value
22148 * Fires when select a date.
22149 * @param {Roo.bootstrap.DateField} this
22150 * @param {Mixed} date The date value
22156 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22159 * @cfg {String} format
22160 * The default time format string which can be overriden for localization support. The format must be
22161 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22165 getAutoCreate : function()
22167 this.after = '<i class="fa far fa-clock"></i>';
22168 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22172 onRender: function(ct, position)
22175 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22177 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22179 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22181 this.pop = this.picker().select('>.datepicker-time',true).first();
22182 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22184 this.picker().on('mousedown', this.onMousedown, this);
22185 this.picker().on('click', this.onClick, this);
22187 this.picker().addClass('datepicker-dropdown');
22192 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22193 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22194 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22195 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22196 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22197 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22201 fireKey: function(e){
22202 if (!this.picker().isVisible()){
22203 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22209 e.preventDefault();
22217 this.onTogglePeriod();
22220 this.onIncrementMinutes();
22223 this.onDecrementMinutes();
22232 onClick: function(e) {
22233 e.stopPropagation();
22234 e.preventDefault();
22237 picker : function()
22239 return this.pickerEl;
22242 fillTime: function()
22244 var time = this.pop.select('tbody', true).first();
22246 time.dom.innerHTML = '';
22261 cls: 'hours-up fa fas fa-chevron-up'
22281 cls: 'minutes-up fa fas fa-chevron-up'
22302 cls: 'timepicker-hour',
22317 cls: 'timepicker-minute',
22332 cls: 'btn btn-primary period',
22354 cls: 'hours-down fa fas fa-chevron-down'
22374 cls: 'minutes-down fa fas fa-chevron-down'
22392 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22399 var hours = this.time.getHours();
22400 var minutes = this.time.getMinutes();
22413 hours = hours - 12;
22417 hours = '0' + hours;
22421 minutes = '0' + minutes;
22424 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22425 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22426 this.pop.select('button', true).first().dom.innerHTML = period;
22432 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22434 var cls = ['bottom'];
22436 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22443 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22447 //this.picker().setXY(20000,20000);
22448 this.picker().addClass(cls.join('-'));
22452 Roo.each(cls, function(c){
22457 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22458 //_this.picker().setTop(_this.inputEl().getHeight());
22462 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22464 //_this.picker().setTop(0 - _this.picker().getHeight());
22469 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22473 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22481 onFocus : function()
22483 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22487 onBlur : function()
22489 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22495 this.picker().show();
22500 this.fireEvent('show', this, this.date);
22505 this.picker().hide();
22508 this.fireEvent('hide', this, this.date);
22511 setTime : function()
22514 this.setValue(this.time.format(this.format));
22516 this.fireEvent('select', this, this.date);
22521 onMousedown: function(e){
22522 e.stopPropagation();
22523 e.preventDefault();
22526 onIncrementHours: function()
22528 Roo.log('onIncrementHours');
22529 this.time = this.time.add(Date.HOUR, 1);
22534 onDecrementHours: function()
22536 Roo.log('onDecrementHours');
22537 this.time = this.time.add(Date.HOUR, -1);
22541 onIncrementMinutes: function()
22543 Roo.log('onIncrementMinutes');
22544 this.time = this.time.add(Date.MINUTE, 1);
22548 onDecrementMinutes: function()
22550 Roo.log('onDecrementMinutes');
22551 this.time = this.time.add(Date.MINUTE, -1);
22555 onTogglePeriod: function()
22557 Roo.log('onTogglePeriod');
22558 this.time = this.time.add(Date.HOUR, 12);
22566 Roo.apply(Roo.bootstrap.TimeField, {
22570 cls: 'datepicker dropdown-menu',
22574 cls: 'datepicker-time',
22578 cls: 'table-condensed',
22607 cls: 'btn btn-info ok',
22635 * @class Roo.bootstrap.MonthField
22636 * @extends Roo.bootstrap.Input
22637 * Bootstrap MonthField class
22639 * @cfg {String} language default en
22642 * Create a new MonthField
22643 * @param {Object} config The config object
22646 Roo.bootstrap.MonthField = function(config){
22647 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22652 * Fires when this field show.
22653 * @param {Roo.bootstrap.MonthField} this
22654 * @param {Mixed} date The date value
22659 * Fires when this field hide.
22660 * @param {Roo.bootstrap.MonthField} this
22661 * @param {Mixed} date The date value
22666 * Fires when select a date.
22667 * @param {Roo.bootstrap.MonthField} this
22668 * @param {String} oldvalue The old value
22669 * @param {String} newvalue The new value
22675 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22677 onRender: function(ct, position)
22680 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22682 this.language = this.language || 'en';
22683 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22684 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22686 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22687 this.isInline = false;
22688 this.isInput = true;
22689 this.component = this.el.select('.add-on', true).first() || false;
22690 this.component = (this.component && this.component.length === 0) ? false : this.component;
22691 this.hasInput = this.component && this.inputEL().length;
22693 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22695 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22697 this.picker().on('mousedown', this.onMousedown, this);
22698 this.picker().on('click', this.onClick, this);
22700 this.picker().addClass('datepicker-dropdown');
22702 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22703 v.setStyle('width', '189px');
22710 if(this.isInline) {
22716 setValue: function(v, suppressEvent)
22718 var o = this.getValue();
22720 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22724 if(suppressEvent !== true){
22725 this.fireEvent('select', this, o, v);
22730 getValue: function()
22735 onClick: function(e)
22737 e.stopPropagation();
22738 e.preventDefault();
22740 var target = e.getTarget();
22742 if(target.nodeName.toLowerCase() === 'i'){
22743 target = Roo.get(target).dom.parentNode;
22746 var nodeName = target.nodeName;
22747 var className = target.className;
22748 var html = target.innerHTML;
22750 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22754 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22756 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22762 picker : function()
22764 return this.pickerEl;
22767 fillMonths: function()
22770 var months = this.picker().select('>.datepicker-months td', true).first();
22772 months.dom.innerHTML = '';
22778 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22781 months.createChild(month);
22790 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22791 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22794 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22795 e.removeClass('active');
22797 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22798 e.addClass('active');
22805 if(this.isInline) {
22809 this.picker().removeClass(['bottom', 'top']);
22811 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22813 * place to the top of element!
22817 this.picker().addClass('top');
22818 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22823 this.picker().addClass('bottom');
22825 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22828 onFocus : function()
22830 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22834 onBlur : function()
22836 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22838 var d = this.inputEl().getValue();
22847 this.picker().show();
22848 this.picker().select('>.datepicker-months', true).first().show();
22852 this.fireEvent('show', this, this.date);
22857 if(this.isInline) {
22860 this.picker().hide();
22861 this.fireEvent('hide', this, this.date);
22865 onMousedown: function(e)
22867 e.stopPropagation();
22868 e.preventDefault();
22873 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22877 fireKey: function(e)
22879 if (!this.picker().isVisible()){
22880 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22891 e.preventDefault();
22895 dir = e.keyCode == 37 ? -1 : 1;
22897 this.vIndex = this.vIndex + dir;
22899 if(this.vIndex < 0){
22903 if(this.vIndex > 11){
22907 if(isNaN(this.vIndex)){
22911 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22917 dir = e.keyCode == 38 ? -1 : 1;
22919 this.vIndex = this.vIndex + dir * 4;
22921 if(this.vIndex < 0){
22925 if(this.vIndex > 11){
22929 if(isNaN(this.vIndex)){
22933 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22938 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22939 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22943 e.preventDefault();
22946 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22947 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22963 this.picker().remove();
22968 Roo.apply(Roo.bootstrap.MonthField, {
22987 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22988 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22993 Roo.apply(Roo.bootstrap.MonthField, {
22997 cls: 'datepicker dropdown-menu roo-dynamic',
23001 cls: 'datepicker-months',
23005 cls: 'table-condensed',
23007 Roo.bootstrap.DateField.content
23027 * @class Roo.bootstrap.CheckBox
23028 * @extends Roo.bootstrap.Input
23029 * Bootstrap CheckBox class
23031 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23032 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23033 * @cfg {String} boxLabel The text that appears beside the checkbox
23034 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23035 * @cfg {Boolean} checked initnal the element
23036 * @cfg {Boolean} inline inline the element (default false)
23037 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23038 * @cfg {String} tooltip label tooltip
23041 * Create a new CheckBox
23042 * @param {Object} config The config object
23045 Roo.bootstrap.CheckBox = function(config){
23046 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23051 * Fires when the element is checked or unchecked.
23052 * @param {Roo.bootstrap.CheckBox} this This input
23053 * @param {Boolean} checked The new checked value
23058 * Fires when the element is click.
23059 * @param {Roo.bootstrap.CheckBox} this This input
23066 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23068 inputType: 'checkbox',
23077 // checkbox success does not make any sense really..
23082 getAutoCreate : function()
23084 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23090 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23093 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23099 type : this.inputType,
23100 value : this.inputValue,
23101 cls : 'roo-' + this.inputType, //'form-box',
23102 placeholder : this.placeholder || ''
23106 if(this.inputType != 'radio'){
23110 cls : 'roo-hidden-value',
23111 value : this.checked ? this.inputValue : this.valueOff
23116 if (this.weight) { // Validity check?
23117 cfg.cls += " " + this.inputType + "-" + this.weight;
23120 if (this.disabled) {
23121 input.disabled=true;
23125 input.checked = this.checked;
23130 input.name = this.name;
23132 if(this.inputType != 'radio'){
23133 hidden.name = this.name;
23134 input.name = '_hidden_' + this.name;
23139 input.cls += ' input-' + this.size;
23144 ['xs','sm','md','lg'].map(function(size){
23145 if (settings[size]) {
23146 cfg.cls += ' col-' + size + '-' + settings[size];
23150 var inputblock = input;
23152 if (this.before || this.after) {
23155 cls : 'input-group',
23160 inputblock.cn.push({
23162 cls : 'input-group-addon',
23167 inputblock.cn.push(input);
23169 if(this.inputType != 'radio'){
23170 inputblock.cn.push(hidden);
23174 inputblock.cn.push({
23176 cls : 'input-group-addon',
23182 var boxLabelCfg = false;
23188 //'for': id, // box label is handled by onclick - so no for...
23190 html: this.boxLabel
23193 boxLabelCfg.tooltip = this.tooltip;
23199 if (align ==='left' && this.fieldLabel.length) {
23200 // Roo.log("left and has label");
23205 cls : 'control-label',
23206 html : this.fieldLabel
23217 cfg.cn[1].cn.push(boxLabelCfg);
23220 if(this.labelWidth > 12){
23221 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23224 if(this.labelWidth < 13 && this.labelmd == 0){
23225 this.labelmd = this.labelWidth;
23228 if(this.labellg > 0){
23229 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23230 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23233 if(this.labelmd > 0){
23234 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23235 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23238 if(this.labelsm > 0){
23239 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23240 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23243 if(this.labelxs > 0){
23244 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23245 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23248 } else if ( this.fieldLabel.length) {
23249 // Roo.log(" label");
23253 tag: this.boxLabel ? 'span' : 'label',
23255 cls: 'control-label box-input-label',
23256 //cls : 'input-group-addon',
23257 html : this.fieldLabel
23264 cfg.cn.push(boxLabelCfg);
23269 // Roo.log(" no label && no align");
23270 cfg.cn = [ inputblock ] ;
23272 cfg.cn.push(boxLabelCfg);
23280 if(this.inputType != 'radio'){
23281 cfg.cn.push(hidden);
23289 * return the real input element.
23291 inputEl: function ()
23293 return this.el.select('input.roo-' + this.inputType,true).first();
23295 hiddenEl: function ()
23297 return this.el.select('input.roo-hidden-value',true).first();
23300 labelEl: function()
23302 return this.el.select('label.control-label',true).first();
23304 /* depricated... */
23308 return this.labelEl();
23311 boxLabelEl: function()
23313 return this.el.select('label.box-label',true).first();
23316 initEvents : function()
23318 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23320 this.inputEl().on('click', this.onClick, this);
23322 if (this.boxLabel) {
23323 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23326 this.startValue = this.getValue();
23329 Roo.bootstrap.CheckBox.register(this);
23333 onClick : function(e)
23335 if(this.fireEvent('click', this, e) !== false){
23336 this.setChecked(!this.checked);
23341 setChecked : function(state,suppressEvent)
23343 this.startValue = this.getValue();
23345 if(this.inputType == 'radio'){
23347 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23348 e.dom.checked = false;
23351 this.inputEl().dom.checked = true;
23353 this.inputEl().dom.value = this.inputValue;
23355 if(suppressEvent !== true){
23356 this.fireEvent('check', this, true);
23364 this.checked = state;
23366 this.inputEl().dom.checked = state;
23369 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23371 if(suppressEvent !== true){
23372 this.fireEvent('check', this, state);
23378 getValue : function()
23380 if(this.inputType == 'radio'){
23381 return this.getGroupValue();
23384 return this.hiddenEl().dom.value;
23388 getGroupValue : function()
23390 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23394 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23397 setValue : function(v,suppressEvent)
23399 if(this.inputType == 'radio'){
23400 this.setGroupValue(v, suppressEvent);
23404 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23409 setGroupValue : function(v, suppressEvent)
23411 this.startValue = this.getValue();
23413 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23414 e.dom.checked = false;
23416 if(e.dom.value == v){
23417 e.dom.checked = true;
23421 if(suppressEvent !== true){
23422 this.fireEvent('check', this, true);
23430 validate : function()
23432 if(this.getVisibilityEl().hasClass('hidden')){
23438 (this.inputType == 'radio' && this.validateRadio()) ||
23439 (this.inputType == 'checkbox' && this.validateCheckbox())
23445 this.markInvalid();
23449 validateRadio : function()
23451 if(this.getVisibilityEl().hasClass('hidden')){
23455 if(this.allowBlank){
23461 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23462 if(!e.dom.checked){
23474 validateCheckbox : function()
23477 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23478 //return (this.getValue() == this.inputValue) ? true : false;
23481 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23489 for(var i in group){
23490 if(group[i].el.isVisible(true)){
23498 for(var i in group){
23503 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23510 * Mark this field as valid
23512 markValid : function()
23516 this.fireEvent('valid', this);
23518 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23521 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23528 if(this.inputType == 'radio'){
23529 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23530 var fg = e.findParent('.form-group', false, true);
23531 if (Roo.bootstrap.version == 3) {
23532 fg.removeClass([_this.invalidClass, _this.validClass]);
23533 fg.addClass(_this.validClass);
23535 fg.removeClass(['is-valid', 'is-invalid']);
23536 fg.addClass('is-valid');
23544 var fg = this.el.findParent('.form-group', false, true);
23545 if (Roo.bootstrap.version == 3) {
23546 fg.removeClass([this.invalidClass, this.validClass]);
23547 fg.addClass(this.validClass);
23549 fg.removeClass(['is-valid', 'is-invalid']);
23550 fg.addClass('is-valid');
23555 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23561 for(var i in group){
23562 var fg = group[i].el.findParent('.form-group', false, true);
23563 if (Roo.bootstrap.version == 3) {
23564 fg.removeClass([this.invalidClass, this.validClass]);
23565 fg.addClass(this.validClass);
23567 fg.removeClass(['is-valid', 'is-invalid']);
23568 fg.addClass('is-valid');
23574 * Mark this field as invalid
23575 * @param {String} msg The validation message
23577 markInvalid : function(msg)
23579 if(this.allowBlank){
23585 this.fireEvent('invalid', this, msg);
23587 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23590 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23594 label.markInvalid();
23597 if(this.inputType == 'radio'){
23599 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23600 var fg = e.findParent('.form-group', false, true);
23601 if (Roo.bootstrap.version == 3) {
23602 fg.removeClass([_this.invalidClass, _this.validClass]);
23603 fg.addClass(_this.invalidClass);
23605 fg.removeClass(['is-invalid', 'is-valid']);
23606 fg.addClass('is-invalid');
23614 var fg = this.el.findParent('.form-group', false, true);
23615 if (Roo.bootstrap.version == 3) {
23616 fg.removeClass([_this.invalidClass, _this.validClass]);
23617 fg.addClass(_this.invalidClass);
23619 fg.removeClass(['is-invalid', 'is-valid']);
23620 fg.addClass('is-invalid');
23625 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23631 for(var i in group){
23632 var fg = group[i].el.findParent('.form-group', false, true);
23633 if (Roo.bootstrap.version == 3) {
23634 fg.removeClass([_this.invalidClass, _this.validClass]);
23635 fg.addClass(_this.invalidClass);
23637 fg.removeClass(['is-invalid', 'is-valid']);
23638 fg.addClass('is-invalid');
23644 clearInvalid : function()
23646 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23648 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23650 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23652 if (label && label.iconEl) {
23653 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23654 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23658 disable : function()
23660 if(this.inputType != 'radio'){
23661 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23668 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23669 _this.getActionEl().addClass(this.disabledClass);
23670 e.dom.disabled = true;
23674 this.disabled = true;
23675 this.fireEvent("disable", this);
23679 enable : function()
23681 if(this.inputType != 'radio'){
23682 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23689 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23690 _this.getActionEl().removeClass(this.disabledClass);
23691 e.dom.disabled = false;
23695 this.disabled = false;
23696 this.fireEvent("enable", this);
23700 setBoxLabel : function(v)
23705 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23711 Roo.apply(Roo.bootstrap.CheckBox, {
23716 * register a CheckBox Group
23717 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23719 register : function(checkbox)
23721 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23722 this.groups[checkbox.groupId] = {};
23725 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23729 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23733 * fetch a CheckBox Group based on the group ID
23734 * @param {string} the group ID
23735 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23737 get: function(groupId) {
23738 if (typeof(this.groups[groupId]) == 'undefined') {
23742 return this.groups[groupId] ;
23755 * @class Roo.bootstrap.Radio
23756 * @extends Roo.bootstrap.Component
23757 * Bootstrap Radio class
23758 * @cfg {String} boxLabel - the label associated
23759 * @cfg {String} value - the value of radio
23762 * Create a new Radio
23763 * @param {Object} config The config object
23765 Roo.bootstrap.Radio = function(config){
23766 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23770 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23776 getAutoCreate : function()
23780 cls : 'form-group radio',
23785 html : this.boxLabel
23793 initEvents : function()
23795 this.parent().register(this);
23797 this.el.on('click', this.onClick, this);
23801 onClick : function(e)
23803 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23804 this.setChecked(true);
23808 setChecked : function(state, suppressEvent)
23810 this.parent().setValue(this.value, suppressEvent);
23814 setBoxLabel : function(v)
23819 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23834 * @class Roo.bootstrap.SecurePass
23835 * @extends Roo.bootstrap.Input
23836 * Bootstrap SecurePass class
23840 * Create a new SecurePass
23841 * @param {Object} config The config object
23844 Roo.bootstrap.SecurePass = function (config) {
23845 // these go here, so the translation tool can replace them..
23847 PwdEmpty: "Please type a password, and then retype it to confirm.",
23848 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23849 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23850 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23851 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23852 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23853 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23854 TooWeak: "Your password is Too Weak."
23856 this.meterLabel = "Password strength:";
23857 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23858 this.meterClass = [
23859 "roo-password-meter-tooweak",
23860 "roo-password-meter-weak",
23861 "roo-password-meter-medium",
23862 "roo-password-meter-strong",
23863 "roo-password-meter-grey"
23868 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23871 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23873 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23875 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23876 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23877 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23878 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23879 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23880 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23881 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23891 * @cfg {String/Object} Label for the strength meter (defaults to
23892 * 'Password strength:')
23897 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23898 * ['Weak', 'Medium', 'Strong'])
23901 pwdStrengths: false,
23914 initEvents: function ()
23916 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23918 if (this.el.is('input[type=password]') && Roo.isSafari) {
23919 this.el.on('keydown', this.SafariOnKeyDown, this);
23922 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23925 onRender: function (ct, position)
23927 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23928 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23929 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23931 this.trigger.createChild({
23936 cls: 'roo-password-meter-grey col-xs-12',
23939 //width: this.meterWidth + 'px'
23943 cls: 'roo-password-meter-text'
23949 if (this.hideTrigger) {
23950 this.trigger.setDisplayed(false);
23952 this.setSize(this.width || '', this.height || '');
23955 onDestroy: function ()
23957 if (this.trigger) {
23958 this.trigger.removeAllListeners();
23959 this.trigger.remove();
23962 this.wrap.remove();
23964 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23967 checkStrength: function ()
23969 var pwd = this.inputEl().getValue();
23970 if (pwd == this._lastPwd) {
23975 if (this.ClientSideStrongPassword(pwd)) {
23977 } else if (this.ClientSideMediumPassword(pwd)) {
23979 } else if (this.ClientSideWeakPassword(pwd)) {
23985 Roo.log('strength1: ' + strength);
23987 //var pm = this.trigger.child('div/div/div').dom;
23988 var pm = this.trigger.child('div/div');
23989 pm.removeClass(this.meterClass);
23990 pm.addClass(this.meterClass[strength]);
23993 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23995 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23997 this._lastPwd = pwd;
24001 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24003 this._lastPwd = '';
24005 var pm = this.trigger.child('div/div');
24006 pm.removeClass(this.meterClass);
24007 pm.addClass('roo-password-meter-grey');
24010 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24013 this.inputEl().dom.type='password';
24016 validateValue: function (value)
24018 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24021 if (value.length == 0) {
24022 if (this.allowBlank) {
24023 this.clearInvalid();
24027 this.markInvalid(this.errors.PwdEmpty);
24028 this.errorMsg = this.errors.PwdEmpty;
24036 if (!value.match(/[\x21-\x7e]+/)) {
24037 this.markInvalid(this.errors.PwdBadChar);
24038 this.errorMsg = this.errors.PwdBadChar;
24041 if (value.length < 6) {
24042 this.markInvalid(this.errors.PwdShort);
24043 this.errorMsg = this.errors.PwdShort;
24046 if (value.length > 16) {
24047 this.markInvalid(this.errors.PwdLong);
24048 this.errorMsg = this.errors.PwdLong;
24052 if (this.ClientSideStrongPassword(value)) {
24054 } else if (this.ClientSideMediumPassword(value)) {
24056 } else if (this.ClientSideWeakPassword(value)) {
24063 if (strength < 2) {
24064 //this.markInvalid(this.errors.TooWeak);
24065 this.errorMsg = this.errors.TooWeak;
24070 console.log('strength2: ' + strength);
24072 //var pm = this.trigger.child('div/div/div').dom;
24074 var pm = this.trigger.child('div/div');
24075 pm.removeClass(this.meterClass);
24076 pm.addClass(this.meterClass[strength]);
24078 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24080 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24082 this.errorMsg = '';
24086 CharacterSetChecks: function (type)
24089 this.fResult = false;
24092 isctype: function (character, type)
24095 case this.kCapitalLetter:
24096 if (character >= 'A' && character <= 'Z') {
24101 case this.kSmallLetter:
24102 if (character >= 'a' && character <= 'z') {
24108 if (character >= '0' && character <= '9') {
24113 case this.kPunctuation:
24114 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24125 IsLongEnough: function (pwd, size)
24127 return !(pwd == null || isNaN(size) || pwd.length < size);
24130 SpansEnoughCharacterSets: function (word, nb)
24132 if (!this.IsLongEnough(word, nb))
24137 var characterSetChecks = new Array(
24138 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24139 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24142 for (var index = 0; index < word.length; ++index) {
24143 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24144 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24145 characterSetChecks[nCharSet].fResult = true;
24152 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24153 if (characterSetChecks[nCharSet].fResult) {
24158 if (nCharSets < nb) {
24164 ClientSideStrongPassword: function (pwd)
24166 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24169 ClientSideMediumPassword: function (pwd)
24171 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24174 ClientSideWeakPassword: function (pwd)
24176 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24179 })//<script type="text/javascript">
24182 * Based Ext JS Library 1.1.1
24183 * Copyright(c) 2006-2007, Ext JS, LLC.
24189 * @class Roo.HtmlEditorCore
24190 * @extends Roo.Component
24191 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24193 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24196 Roo.HtmlEditorCore = function(config){
24199 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24204 * @event initialize
24205 * Fires when the editor is fully initialized (including the iframe)
24206 * @param {Roo.HtmlEditorCore} this
24211 * Fires when the editor is first receives the focus. Any insertion must wait
24212 * until after this event.
24213 * @param {Roo.HtmlEditorCore} this
24217 * @event beforesync
24218 * Fires before the textarea is updated with content from the editor iframe. Return false
24219 * to cancel the sync.
24220 * @param {Roo.HtmlEditorCore} this
24221 * @param {String} html
24225 * @event beforepush
24226 * Fires before the iframe editor is updated with content from the textarea. Return false
24227 * to cancel the push.
24228 * @param {Roo.HtmlEditorCore} this
24229 * @param {String} html
24234 * Fires when the textarea is updated with content from the editor iframe.
24235 * @param {Roo.HtmlEditorCore} this
24236 * @param {String} html
24241 * Fires when the iframe editor is updated with content from the textarea.
24242 * @param {Roo.HtmlEditorCore} this
24243 * @param {String} html
24248 * @event editorevent
24249 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24250 * @param {Roo.HtmlEditorCore} this
24256 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24258 // defaults : white / black...
24259 this.applyBlacklists();
24266 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24270 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24276 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24281 * @cfg {Number} height (in pixels)
24285 * @cfg {Number} width (in pixels)
24290 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24293 stylesheets: false,
24298 // private properties
24299 validationEvent : false,
24301 initialized : false,
24303 sourceEditMode : false,
24304 onFocus : Roo.emptyFn,
24306 hideMode:'offsets',
24310 // blacklist + whitelisted elements..
24317 * Protected method that will not generally be called directly. It
24318 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24319 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24321 getDocMarkup : function(){
24325 // inherit styels from page...??
24326 if (this.stylesheets === false) {
24328 Roo.get(document.head).select('style').each(function(node) {
24329 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24332 Roo.get(document.head).select('link').each(function(node) {
24333 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24336 } else if (!this.stylesheets.length) {
24338 st = '<style type="text/css">' +
24339 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24342 for (var i in this.stylesheets) {
24343 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24348 st += '<style type="text/css">' +
24349 'IMG { cursor: pointer } ' +
24352 var cls = 'roo-htmleditor-body';
24354 if(this.bodyCls.length){
24355 cls += ' ' + this.bodyCls;
24358 return '<html><head>' + st +
24359 //<style type="text/css">' +
24360 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24362 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24366 onRender : function(ct, position)
24369 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24370 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24373 this.el.dom.style.border = '0 none';
24374 this.el.dom.setAttribute('tabIndex', -1);
24375 this.el.addClass('x-hidden hide');
24379 if(Roo.isIE){ // fix IE 1px bogus margin
24380 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24384 this.frameId = Roo.id();
24388 var iframe = this.owner.wrap.createChild({
24390 cls: 'form-control', // bootstrap..
24392 name: this.frameId,
24393 frameBorder : 'no',
24394 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24399 this.iframe = iframe.dom;
24401 this.assignDocWin();
24403 this.doc.designMode = 'on';
24406 this.doc.write(this.getDocMarkup());
24410 var task = { // must defer to wait for browser to be ready
24412 //console.log("run task?" + this.doc.readyState);
24413 this.assignDocWin();
24414 if(this.doc.body || this.doc.readyState == 'complete'){
24416 this.doc.designMode="on";
24420 Roo.TaskMgr.stop(task);
24421 this.initEditor.defer(10, this);
24428 Roo.TaskMgr.start(task);
24433 onResize : function(w, h)
24435 Roo.log('resize: ' +w + ',' + h );
24436 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24440 if(typeof w == 'number'){
24442 this.iframe.style.width = w + 'px';
24444 if(typeof h == 'number'){
24446 this.iframe.style.height = h + 'px';
24448 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24455 * Toggles the editor between standard and source edit mode.
24456 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24458 toggleSourceEdit : function(sourceEditMode){
24460 this.sourceEditMode = sourceEditMode === true;
24462 if(this.sourceEditMode){
24464 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24467 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24468 //this.iframe.className = '';
24471 //this.setSize(this.owner.wrap.getSize());
24472 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24479 * Protected method that will not generally be called directly. If you need/want
24480 * custom HTML cleanup, this is the method you should override.
24481 * @param {String} html The HTML to be cleaned
24482 * return {String} The cleaned HTML
24484 cleanHtml : function(html){
24485 html = String(html);
24486 if(html.length > 5){
24487 if(Roo.isSafari){ // strip safari nonsense
24488 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24491 if(html == ' '){
24498 * HTML Editor -> Textarea
24499 * Protected method that will not generally be called directly. Syncs the contents
24500 * of the editor iframe with the textarea.
24502 syncValue : function(){
24503 if(this.initialized){
24504 var bd = (this.doc.body || this.doc.documentElement);
24505 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24506 var html = bd.innerHTML;
24508 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24509 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24511 html = '<div style="'+m[0]+'">' + html + '</div>';
24514 html = this.cleanHtml(html);
24515 // fix up the special chars.. normaly like back quotes in word...
24516 // however we do not want to do this with chinese..
24517 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24519 var cc = match.charCodeAt();
24521 // Get the character value, handling surrogate pairs
24522 if (match.length == 2) {
24523 // It's a surrogate pair, calculate the Unicode code point
24524 var high = match.charCodeAt(0) - 0xD800;
24525 var low = match.charCodeAt(1) - 0xDC00;
24526 cc = (high * 0x400) + low + 0x10000;
24528 (cc >= 0x4E00 && cc < 0xA000 ) ||
24529 (cc >= 0x3400 && cc < 0x4E00 ) ||
24530 (cc >= 0xf900 && cc < 0xfb00 )
24535 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24536 return "&#" + cc + ";";
24543 if(this.owner.fireEvent('beforesync', this, html) !== false){
24544 this.el.dom.value = html;
24545 this.owner.fireEvent('sync', this, html);
24551 * Protected method that will not generally be called directly. Pushes the value of the textarea
24552 * into the iframe editor.
24554 pushValue : function(){
24555 if(this.initialized){
24556 var v = this.el.dom.value.trim();
24558 // if(v.length < 1){
24562 if(this.owner.fireEvent('beforepush', this, v) !== false){
24563 var d = (this.doc.body || this.doc.documentElement);
24565 this.cleanUpPaste();
24566 this.el.dom.value = d.innerHTML;
24567 this.owner.fireEvent('push', this, v);
24573 deferFocus : function(){
24574 this.focus.defer(10, this);
24578 focus : function(){
24579 if(this.win && !this.sourceEditMode){
24586 assignDocWin: function()
24588 var iframe = this.iframe;
24591 this.doc = iframe.contentWindow.document;
24592 this.win = iframe.contentWindow;
24594 // if (!Roo.get(this.frameId)) {
24597 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24598 // this.win = Roo.get(this.frameId).dom.contentWindow;
24600 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24604 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24605 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24610 initEditor : function(){
24611 //console.log("INIT EDITOR");
24612 this.assignDocWin();
24616 this.doc.designMode="on";
24618 this.doc.write(this.getDocMarkup());
24621 var dbody = (this.doc.body || this.doc.documentElement);
24622 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24623 // this copies styles from the containing element into thsi one..
24624 // not sure why we need all of this..
24625 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24627 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24628 //ss['background-attachment'] = 'fixed'; // w3c
24629 dbody.bgProperties = 'fixed'; // ie
24630 //Roo.DomHelper.applyStyles(dbody, ss);
24631 Roo.EventManager.on(this.doc, {
24632 //'mousedown': this.onEditorEvent,
24633 'mouseup': this.onEditorEvent,
24634 'dblclick': this.onEditorEvent,
24635 'click': this.onEditorEvent,
24636 'keyup': this.onEditorEvent,
24641 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24643 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24644 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24646 this.initialized = true;
24648 this.owner.fireEvent('initialize', this);
24653 onDestroy : function(){
24659 //for (var i =0; i < this.toolbars.length;i++) {
24660 // // fixme - ask toolbars for heights?
24661 // this.toolbars[i].onDestroy();
24664 //this.wrap.dom.innerHTML = '';
24665 //this.wrap.remove();
24670 onFirstFocus : function(){
24672 this.assignDocWin();
24675 this.activated = true;
24678 if(Roo.isGecko){ // prevent silly gecko errors
24680 var s = this.win.getSelection();
24681 if(!s.focusNode || s.focusNode.nodeType != 3){
24682 var r = s.getRangeAt(0);
24683 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24688 this.execCmd('useCSS', true);
24689 this.execCmd('styleWithCSS', false);
24692 this.owner.fireEvent('activate', this);
24696 adjustFont: function(btn){
24697 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24698 //if(Roo.isSafari){ // safari
24701 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24702 if(Roo.isSafari){ // safari
24703 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24704 v = (v < 10) ? 10 : v;
24705 v = (v > 48) ? 48 : v;
24706 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24711 v = Math.max(1, v+adjust);
24713 this.execCmd('FontSize', v );
24716 onEditorEvent : function(e)
24718 this.owner.fireEvent('editorevent', this, e);
24719 // this.updateToolbar();
24720 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24723 insertTag : function(tg)
24725 // could be a bit smarter... -> wrap the current selected tRoo..
24726 if (tg.toLowerCase() == 'span' ||
24727 tg.toLowerCase() == 'code' ||
24728 tg.toLowerCase() == 'sup' ||
24729 tg.toLowerCase() == 'sub'
24732 range = this.createRange(this.getSelection());
24733 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24734 wrappingNode.appendChild(range.extractContents());
24735 range.insertNode(wrappingNode);
24742 this.execCmd("formatblock", tg);
24746 insertText : function(txt)
24750 var range = this.createRange();
24751 range.deleteContents();
24752 //alert(Sender.getAttribute('label'));
24754 range.insertNode(this.doc.createTextNode(txt));
24760 * Executes a Midas editor command on the editor document and performs necessary focus and
24761 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24762 * @param {String} cmd The Midas command
24763 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24765 relayCmd : function(cmd, value){
24767 this.execCmd(cmd, value);
24768 this.owner.fireEvent('editorevent', this);
24769 //this.updateToolbar();
24770 this.owner.deferFocus();
24774 * Executes a Midas editor command directly on the editor document.
24775 * For visual commands, you should use {@link #relayCmd} instead.
24776 * <b>This should only be called after the editor is initialized.</b>
24777 * @param {String} cmd The Midas command
24778 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24780 execCmd : function(cmd, value){
24781 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24788 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24790 * @param {String} text | dom node..
24792 insertAtCursor : function(text)
24795 if(!this.activated){
24801 var r = this.doc.selection.createRange();
24812 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24816 // from jquery ui (MIT licenced)
24818 var win = this.win;
24820 if (win.getSelection && win.getSelection().getRangeAt) {
24821 range = win.getSelection().getRangeAt(0);
24822 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24823 range.insertNode(node);
24824 } else if (win.document.selection && win.document.selection.createRange) {
24825 // no firefox support
24826 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24827 win.document.selection.createRange().pasteHTML(txt);
24829 // no firefox support
24830 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24831 this.execCmd('InsertHTML', txt);
24840 mozKeyPress : function(e){
24842 var c = e.getCharCode(), cmd;
24845 c = String.fromCharCode(c).toLowerCase();
24859 this.cleanUpPaste.defer(100, this);
24867 e.preventDefault();
24875 fixKeys : function(){ // load time branching for fastest keydown performance
24877 return function(e){
24878 var k = e.getKey(), r;
24881 r = this.doc.selection.createRange();
24884 r.pasteHTML('    ');
24891 r = this.doc.selection.createRange();
24893 var target = r.parentElement();
24894 if(!target || target.tagName.toLowerCase() != 'li'){
24896 r.pasteHTML('<br />');
24902 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24903 this.cleanUpPaste.defer(100, this);
24909 }else if(Roo.isOpera){
24910 return function(e){
24911 var k = e.getKey();
24915 this.execCmd('InsertHTML','    ');
24918 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24919 this.cleanUpPaste.defer(100, this);
24924 }else if(Roo.isSafari){
24925 return function(e){
24926 var k = e.getKey();
24930 this.execCmd('InsertText','\t');
24934 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24935 this.cleanUpPaste.defer(100, this);
24943 getAllAncestors: function()
24945 var p = this.getSelectedNode();
24948 a.push(p); // push blank onto stack..
24949 p = this.getParentElement();
24953 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24957 a.push(this.doc.body);
24961 lastSelNode : false,
24964 getSelection : function()
24966 this.assignDocWin();
24967 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24970 getSelectedNode: function()
24972 // this may only work on Gecko!!!
24974 // should we cache this!!!!
24979 var range = this.createRange(this.getSelection()).cloneRange();
24982 var parent = range.parentElement();
24984 var testRange = range.duplicate();
24985 testRange.moveToElementText(parent);
24986 if (testRange.inRange(range)) {
24989 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24992 parent = parent.parentElement;
24997 // is ancestor a text element.
24998 var ac = range.commonAncestorContainer;
24999 if (ac.nodeType == 3) {
25000 ac = ac.parentNode;
25003 var ar = ac.childNodes;
25006 var other_nodes = [];
25007 var has_other_nodes = false;
25008 for (var i=0;i<ar.length;i++) {
25009 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25012 // fullly contained node.
25014 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25019 // probably selected..
25020 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25021 other_nodes.push(ar[i]);
25025 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25030 has_other_nodes = true;
25032 if (!nodes.length && other_nodes.length) {
25033 nodes= other_nodes;
25035 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25041 createRange: function(sel)
25043 // this has strange effects when using with
25044 // top toolbar - not sure if it's a great idea.
25045 //this.editor.contentWindow.focus();
25046 if (typeof sel != "undefined") {
25048 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25050 return this.doc.createRange();
25053 return this.doc.createRange();
25056 getParentElement: function()
25059 this.assignDocWin();
25060 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25062 var range = this.createRange(sel);
25065 var p = range.commonAncestorContainer;
25066 while (p.nodeType == 3) { // text node
25077 * Range intersection.. the hard stuff...
25081 * [ -- selected range --- ]
25085 * if end is before start or hits it. fail.
25086 * if start is after end or hits it fail.
25088 * if either hits (but other is outside. - then it's not
25094 // @see http://www.thismuchiknow.co.uk/?p=64.
25095 rangeIntersectsNode : function(range, node)
25097 var nodeRange = node.ownerDocument.createRange();
25099 nodeRange.selectNode(node);
25101 nodeRange.selectNodeContents(node);
25104 var rangeStartRange = range.cloneRange();
25105 rangeStartRange.collapse(true);
25107 var rangeEndRange = range.cloneRange();
25108 rangeEndRange.collapse(false);
25110 var nodeStartRange = nodeRange.cloneRange();
25111 nodeStartRange.collapse(true);
25113 var nodeEndRange = nodeRange.cloneRange();
25114 nodeEndRange.collapse(false);
25116 return rangeStartRange.compareBoundaryPoints(
25117 Range.START_TO_START, nodeEndRange) == -1 &&
25118 rangeEndRange.compareBoundaryPoints(
25119 Range.START_TO_START, nodeStartRange) == 1;
25123 rangeCompareNode : function(range, node)
25125 var nodeRange = node.ownerDocument.createRange();
25127 nodeRange.selectNode(node);
25129 nodeRange.selectNodeContents(node);
25133 range.collapse(true);
25135 nodeRange.collapse(true);
25137 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25138 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25140 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25142 var nodeIsBefore = ss == 1;
25143 var nodeIsAfter = ee == -1;
25145 if (nodeIsBefore && nodeIsAfter) {
25148 if (!nodeIsBefore && nodeIsAfter) {
25149 return 1; //right trailed.
25152 if (nodeIsBefore && !nodeIsAfter) {
25153 return 2; // left trailed.
25159 // private? - in a new class?
25160 cleanUpPaste : function()
25162 // cleans up the whole document..
25163 Roo.log('cleanuppaste');
25165 this.cleanUpChildren(this.doc.body);
25166 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25167 if (clean != this.doc.body.innerHTML) {
25168 this.doc.body.innerHTML = clean;
25173 cleanWordChars : function(input) {// change the chars to hex code
25174 var he = Roo.HtmlEditorCore;
25176 var output = input;
25177 Roo.each(he.swapCodes, function(sw) {
25178 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25180 output = output.replace(swapper, sw[1]);
25187 cleanUpChildren : function (n)
25189 if (!n.childNodes.length) {
25192 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25193 this.cleanUpChild(n.childNodes[i]);
25200 cleanUpChild : function (node)
25203 //console.log(node);
25204 if (node.nodeName == "#text") {
25205 // clean up silly Windows -- stuff?
25208 if (node.nodeName == "#comment") {
25209 node.parentNode.removeChild(node);
25210 // clean up silly Windows -- stuff?
25213 var lcname = node.tagName.toLowerCase();
25214 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25215 // whitelist of tags..
25217 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25219 node.parentNode.removeChild(node);
25224 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25226 // spans with no attributes - just remove them..
25227 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25228 remove_keep_children = true;
25231 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25232 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25234 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25235 // remove_keep_children = true;
25238 if (remove_keep_children) {
25239 this.cleanUpChildren(node);
25240 // inserts everything just before this node...
25241 while (node.childNodes.length) {
25242 var cn = node.childNodes[0];
25243 node.removeChild(cn);
25244 node.parentNode.insertBefore(cn, node);
25246 node.parentNode.removeChild(node);
25250 if (!node.attributes || !node.attributes.length) {
25255 this.cleanUpChildren(node);
25259 function cleanAttr(n,v)
25262 if (v.match(/^\./) || v.match(/^\//)) {
25265 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25268 if (v.match(/^#/)) {
25271 if (v.match(/^\{/)) { // allow template editing.
25274 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25275 node.removeAttribute(n);
25279 var cwhite = this.cwhite;
25280 var cblack = this.cblack;
25282 function cleanStyle(n,v)
25284 if (v.match(/expression/)) { //XSS?? should we even bother..
25285 node.removeAttribute(n);
25289 var parts = v.split(/;/);
25292 Roo.each(parts, function(p) {
25293 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25297 var l = p.split(':').shift().replace(/\s+/g,'');
25298 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25300 if ( cwhite.length && cblack.indexOf(l) > -1) {
25301 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25302 //node.removeAttribute(n);
25306 // only allow 'c whitelisted system attributes'
25307 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25308 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25309 //node.removeAttribute(n);
25319 if (clean.length) {
25320 node.setAttribute(n, clean.join(';'));
25322 node.removeAttribute(n);
25328 for (var i = node.attributes.length-1; i > -1 ; i--) {
25329 var a = node.attributes[i];
25332 if (a.name.toLowerCase().substr(0,2)=='on') {
25333 node.removeAttribute(a.name);
25336 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25337 node.removeAttribute(a.name);
25340 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25341 cleanAttr(a.name,a.value); // fixme..
25344 if (a.name == 'style') {
25345 cleanStyle(a.name,a.value);
25348 /// clean up MS crap..
25349 // tecnically this should be a list of valid class'es..
25352 if (a.name == 'class') {
25353 if (a.value.match(/^Mso/)) {
25354 node.removeAttribute('class');
25357 if (a.value.match(/^body$/)) {
25358 node.removeAttribute('class');
25369 this.cleanUpChildren(node);
25375 * Clean up MS wordisms...
25377 cleanWord : function(node)
25380 this.cleanWord(this.doc.body);
25385 node.nodeName == 'SPAN' &&
25386 !node.hasAttributes() &&
25387 node.childNodes.length == 1 &&
25388 node.firstChild.nodeName == "#text"
25390 var textNode = node.firstChild;
25391 node.removeChild(textNode);
25392 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25393 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25395 node.parentNode.insertBefore(textNode, node);
25396 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25397 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25399 node.parentNode.removeChild(node);
25402 if (node.nodeName == "#text") {
25403 // clean up silly Windows -- stuff?
25406 if (node.nodeName == "#comment") {
25407 node.parentNode.removeChild(node);
25408 // clean up silly Windows -- stuff?
25412 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25413 node.parentNode.removeChild(node);
25416 //Roo.log(node.tagName);
25417 // remove - but keep children..
25418 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25419 //Roo.log('-- removed');
25420 while (node.childNodes.length) {
25421 var cn = node.childNodes[0];
25422 node.removeChild(cn);
25423 node.parentNode.insertBefore(cn, node);
25424 // move node to parent - and clean it..
25425 this.cleanWord(cn);
25427 node.parentNode.removeChild(node);
25428 /// no need to iterate chidlren = it's got none..
25429 //this.iterateChildren(node, this.cleanWord);
25433 if (node.className.length) {
25435 var cn = node.className.split(/\W+/);
25437 Roo.each(cn, function(cls) {
25438 if (cls.match(/Mso[a-zA-Z]+/)) {
25443 node.className = cna.length ? cna.join(' ') : '';
25445 node.removeAttribute("class");
25449 if (node.hasAttribute("lang")) {
25450 node.removeAttribute("lang");
25453 if (node.hasAttribute("style")) {
25455 var styles = node.getAttribute("style").split(";");
25457 Roo.each(styles, function(s) {
25458 if (!s.match(/:/)) {
25461 var kv = s.split(":");
25462 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25465 // what ever is left... we allow.
25468 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25469 if (!nstyle.length) {
25470 node.removeAttribute('style');
25473 this.iterateChildren(node, this.cleanWord);
25479 * iterateChildren of a Node, calling fn each time, using this as the scole..
25480 * @param {DomNode} node node to iterate children of.
25481 * @param {Function} fn method of this class to call on each item.
25483 iterateChildren : function(node, fn)
25485 if (!node.childNodes.length) {
25488 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25489 fn.call(this, node.childNodes[i])
25495 * cleanTableWidths.
25497 * Quite often pasting from word etc.. results in tables with column and widths.
25498 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25501 cleanTableWidths : function(node)
25506 this.cleanTableWidths(this.doc.body);
25511 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25514 Roo.log(node.tagName);
25515 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25516 this.iterateChildren(node, this.cleanTableWidths);
25519 if (node.hasAttribute('width')) {
25520 node.removeAttribute('width');
25524 if (node.hasAttribute("style")) {
25527 var styles = node.getAttribute("style").split(";");
25529 Roo.each(styles, function(s) {
25530 if (!s.match(/:/)) {
25533 var kv = s.split(":");
25534 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25537 // what ever is left... we allow.
25540 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25541 if (!nstyle.length) {
25542 node.removeAttribute('style');
25546 this.iterateChildren(node, this.cleanTableWidths);
25554 domToHTML : function(currentElement, depth, nopadtext) {
25556 depth = depth || 0;
25557 nopadtext = nopadtext || false;
25559 if (!currentElement) {
25560 return this.domToHTML(this.doc.body);
25563 //Roo.log(currentElement);
25565 var allText = false;
25566 var nodeName = currentElement.nodeName;
25567 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25569 if (nodeName == '#text') {
25571 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25576 if (nodeName != 'BODY') {
25579 // Prints the node tagName, such as <A>, <IMG>, etc
25582 for(i = 0; i < currentElement.attributes.length;i++) {
25584 var aname = currentElement.attributes.item(i).name;
25585 if (!currentElement.attributes.item(i).value.length) {
25588 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25591 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25600 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25603 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25608 // Traverse the tree
25610 var currentElementChild = currentElement.childNodes.item(i);
25611 var allText = true;
25612 var innerHTML = '';
25614 while (currentElementChild) {
25615 // Formatting code (indent the tree so it looks nice on the screen)
25616 var nopad = nopadtext;
25617 if (lastnode == 'SPAN') {
25621 if (currentElementChild.nodeName == '#text') {
25622 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25623 toadd = nopadtext ? toadd : toadd.trim();
25624 if (!nopad && toadd.length > 80) {
25625 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25627 innerHTML += toadd;
25630 currentElementChild = currentElement.childNodes.item(i);
25636 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25638 // Recursively traverse the tree structure of the child node
25639 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25640 lastnode = currentElementChild.nodeName;
25642 currentElementChild=currentElement.childNodes.item(i);
25648 // The remaining code is mostly for formatting the tree
25649 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25654 ret+= "</"+tagName+">";
25660 applyBlacklists : function()
25662 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25663 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25667 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25668 if (b.indexOf(tag) > -1) {
25671 this.white.push(tag);
25675 Roo.each(w, function(tag) {
25676 if (b.indexOf(tag) > -1) {
25679 if (this.white.indexOf(tag) > -1) {
25682 this.white.push(tag);
25687 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25688 if (w.indexOf(tag) > -1) {
25691 this.black.push(tag);
25695 Roo.each(b, function(tag) {
25696 if (w.indexOf(tag) > -1) {
25699 if (this.black.indexOf(tag) > -1) {
25702 this.black.push(tag);
25707 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25708 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25712 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25713 if (b.indexOf(tag) > -1) {
25716 this.cwhite.push(tag);
25720 Roo.each(w, function(tag) {
25721 if (b.indexOf(tag) > -1) {
25724 if (this.cwhite.indexOf(tag) > -1) {
25727 this.cwhite.push(tag);
25732 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25733 if (w.indexOf(tag) > -1) {
25736 this.cblack.push(tag);
25740 Roo.each(b, function(tag) {
25741 if (w.indexOf(tag) > -1) {
25744 if (this.cblack.indexOf(tag) > -1) {
25747 this.cblack.push(tag);
25752 setStylesheets : function(stylesheets)
25754 if(typeof(stylesheets) == 'string'){
25755 Roo.get(this.iframe.contentDocument.head).createChild({
25757 rel : 'stylesheet',
25766 Roo.each(stylesheets, function(s) {
25771 Roo.get(_this.iframe.contentDocument.head).createChild({
25773 rel : 'stylesheet',
25782 removeStylesheets : function()
25786 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25791 setStyle : function(style)
25793 Roo.get(this.iframe.contentDocument.head).createChild({
25802 // hide stuff that is not compatible
25816 * @event specialkey
25820 * @cfg {String} fieldClass @hide
25823 * @cfg {String} focusClass @hide
25826 * @cfg {String} autoCreate @hide
25829 * @cfg {String} inputType @hide
25832 * @cfg {String} invalidClass @hide
25835 * @cfg {String} invalidText @hide
25838 * @cfg {String} msgFx @hide
25841 * @cfg {String} validateOnBlur @hide
25845 Roo.HtmlEditorCore.white = [
25846 'area', 'br', 'img', 'input', 'hr', 'wbr',
25848 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25849 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25850 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25851 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25852 'table', 'ul', 'xmp',
25854 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25857 'dir', 'menu', 'ol', 'ul', 'dl',
25863 Roo.HtmlEditorCore.black = [
25864 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25866 'base', 'basefont', 'bgsound', 'blink', 'body',
25867 'frame', 'frameset', 'head', 'html', 'ilayer',
25868 'iframe', 'layer', 'link', 'meta', 'object',
25869 'script', 'style' ,'title', 'xml' // clean later..
25871 Roo.HtmlEditorCore.clean = [
25872 'script', 'style', 'title', 'xml'
25874 Roo.HtmlEditorCore.remove = [
25879 Roo.HtmlEditorCore.ablack = [
25883 Roo.HtmlEditorCore.aclean = [
25884 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25888 Roo.HtmlEditorCore.pwhite= [
25889 'http', 'https', 'mailto'
25892 // white listed style attributes.
25893 Roo.HtmlEditorCore.cwhite= [
25894 // 'text-align', /// default is to allow most things..
25900 // black listed style attributes.
25901 Roo.HtmlEditorCore.cblack= [
25902 // 'font-size' -- this can be set by the project
25906 Roo.HtmlEditorCore.swapCodes =[
25907 [ 8211, "–" ],
25908 [ 8212, "—" ],
25925 * @class Roo.bootstrap.HtmlEditor
25926 * @extends Roo.bootstrap.TextArea
25927 * Bootstrap HtmlEditor class
25930 * Create a new HtmlEditor
25931 * @param {Object} config The config object
25934 Roo.bootstrap.HtmlEditor = function(config){
25935 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25936 if (!this.toolbars) {
25937 this.toolbars = [];
25940 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25943 * @event initialize
25944 * Fires when the editor is fully initialized (including the iframe)
25945 * @param {HtmlEditor} this
25950 * Fires when the editor is first receives the focus. Any insertion must wait
25951 * until after this event.
25952 * @param {HtmlEditor} this
25956 * @event beforesync
25957 * Fires before the textarea is updated with content from the editor iframe. Return false
25958 * to cancel the sync.
25959 * @param {HtmlEditor} this
25960 * @param {String} html
25964 * @event beforepush
25965 * Fires before the iframe editor is updated with content from the textarea. Return false
25966 * to cancel the push.
25967 * @param {HtmlEditor} this
25968 * @param {String} html
25973 * Fires when the textarea is updated with content from the editor iframe.
25974 * @param {HtmlEditor} this
25975 * @param {String} html
25980 * Fires when the iframe editor is updated with content from the textarea.
25981 * @param {HtmlEditor} this
25982 * @param {String} html
25986 * @event editmodechange
25987 * Fires when the editor switches edit modes
25988 * @param {HtmlEditor} this
25989 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25991 editmodechange: true,
25993 * @event editorevent
25994 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25995 * @param {HtmlEditor} this
25999 * @event firstfocus
26000 * Fires when on first focus - needed by toolbars..
26001 * @param {HtmlEditor} this
26006 * Auto save the htmlEditor value as a file into Events
26007 * @param {HtmlEditor} this
26011 * @event savedpreview
26012 * preview the saved version of htmlEditor
26013 * @param {HtmlEditor} this
26020 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26024 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26029 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26034 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26039 * @cfg {Number} height (in pixels)
26043 * @cfg {Number} width (in pixels)
26048 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26051 stylesheets: false,
26056 // private properties
26057 validationEvent : false,
26059 initialized : false,
26062 onFocus : Roo.emptyFn,
26064 hideMode:'offsets',
26066 tbContainer : false,
26070 toolbarContainer :function() {
26071 return this.wrap.select('.x-html-editor-tb',true).first();
26075 * Protected method that will not generally be called directly. It
26076 * is called when the editor creates its toolbar. Override this method if you need to
26077 * add custom toolbar buttons.
26078 * @param {HtmlEditor} editor
26080 createToolbar : function(){
26081 Roo.log('renewing');
26082 Roo.log("create toolbars");
26084 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26085 this.toolbars[0].render(this.toolbarContainer());
26089 // if (!editor.toolbars || !editor.toolbars.length) {
26090 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26093 // for (var i =0 ; i < editor.toolbars.length;i++) {
26094 // editor.toolbars[i] = Roo.factory(
26095 // typeof(editor.toolbars[i]) == 'string' ?
26096 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26097 // Roo.bootstrap.HtmlEditor);
26098 // editor.toolbars[i].init(editor);
26104 onRender : function(ct, position)
26106 // Roo.log("Call onRender: " + this.xtype);
26108 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26110 this.wrap = this.inputEl().wrap({
26111 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26114 this.editorcore.onRender(ct, position);
26116 if (this.resizable) {
26117 this.resizeEl = new Roo.Resizable(this.wrap, {
26121 minHeight : this.height,
26122 height: this.height,
26123 handles : this.resizable,
26126 resize : function(r, w, h) {
26127 _t.onResize(w,h); // -something
26133 this.createToolbar(this);
26136 if(!this.width && this.resizable){
26137 this.setSize(this.wrap.getSize());
26139 if (this.resizeEl) {
26140 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26141 // should trigger onReize..
26147 onResize : function(w, h)
26149 Roo.log('resize: ' +w + ',' + h );
26150 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26154 if(this.inputEl() ){
26155 if(typeof w == 'number'){
26156 var aw = w - this.wrap.getFrameWidth('lr');
26157 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26160 if(typeof h == 'number'){
26161 var tbh = -11; // fixme it needs to tool bar size!
26162 for (var i =0; i < this.toolbars.length;i++) {
26163 // fixme - ask toolbars for heights?
26164 tbh += this.toolbars[i].el.getHeight();
26165 //if (this.toolbars[i].footer) {
26166 // tbh += this.toolbars[i].footer.el.getHeight();
26174 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26175 ah -= 5; // knock a few pixes off for look..
26176 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26180 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26181 this.editorcore.onResize(ew,eh);
26186 * Toggles the editor between standard and source edit mode.
26187 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26189 toggleSourceEdit : function(sourceEditMode)
26191 this.editorcore.toggleSourceEdit(sourceEditMode);
26193 if(this.editorcore.sourceEditMode){
26194 Roo.log('editor - showing textarea');
26197 // Roo.log(this.syncValue());
26199 this.inputEl().removeClass(['hide', 'x-hidden']);
26200 this.inputEl().dom.removeAttribute('tabIndex');
26201 this.inputEl().focus();
26203 Roo.log('editor - hiding textarea');
26205 // Roo.log(this.pushValue());
26208 this.inputEl().addClass(['hide', 'x-hidden']);
26209 this.inputEl().dom.setAttribute('tabIndex', -1);
26210 //this.deferFocus();
26213 if(this.resizable){
26214 this.setSize(this.wrap.getSize());
26217 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26220 // private (for BoxComponent)
26221 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26223 // private (for BoxComponent)
26224 getResizeEl : function(){
26228 // private (for BoxComponent)
26229 getPositionEl : function(){
26234 initEvents : function(){
26235 this.originalValue = this.getValue();
26239 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26242 // markInvalid : Roo.emptyFn,
26244 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26247 // clearInvalid : Roo.emptyFn,
26249 setValue : function(v){
26250 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26251 this.editorcore.pushValue();
26256 deferFocus : function(){
26257 this.focus.defer(10, this);
26261 focus : function(){
26262 this.editorcore.focus();
26268 onDestroy : function(){
26274 for (var i =0; i < this.toolbars.length;i++) {
26275 // fixme - ask toolbars for heights?
26276 this.toolbars[i].onDestroy();
26279 this.wrap.dom.innerHTML = '';
26280 this.wrap.remove();
26285 onFirstFocus : function(){
26286 //Roo.log("onFirstFocus");
26287 this.editorcore.onFirstFocus();
26288 for (var i =0; i < this.toolbars.length;i++) {
26289 this.toolbars[i].onFirstFocus();
26295 syncValue : function()
26297 this.editorcore.syncValue();
26300 pushValue : function()
26302 this.editorcore.pushValue();
26306 // hide stuff that is not compatible
26320 * @event specialkey
26324 * @cfg {String} fieldClass @hide
26327 * @cfg {String} focusClass @hide
26330 * @cfg {String} autoCreate @hide
26333 * @cfg {String} inputType @hide
26337 * @cfg {String} invalidText @hide
26340 * @cfg {String} msgFx @hide
26343 * @cfg {String} validateOnBlur @hide
26352 Roo.namespace('Roo.bootstrap.htmleditor');
26354 * @class Roo.bootstrap.HtmlEditorToolbar1
26360 new Roo.bootstrap.HtmlEditor({
26363 new Roo.bootstrap.HtmlEditorToolbar1({
26364 disable : { fonts: 1 , format: 1, ..., ... , ...],
26370 * @cfg {Object} disable List of elements to disable..
26371 * @cfg {Array} btns List of additional buttons.
26375 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26378 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26381 Roo.apply(this, config);
26383 // default disabled, based on 'good practice'..
26384 this.disable = this.disable || {};
26385 Roo.applyIf(this.disable, {
26388 specialElements : true
26390 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26392 this.editor = config.editor;
26393 this.editorcore = config.editor.editorcore;
26395 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26397 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26398 // dont call parent... till later.
26400 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26405 editorcore : false,
26410 "h1","h2","h3","h4","h5","h6",
26412 "abbr", "acronym", "address", "cite", "samp", "var",
26416 onRender : function(ct, position)
26418 // Roo.log("Call onRender: " + this.xtype);
26420 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26422 this.el.dom.style.marginBottom = '0';
26424 var editorcore = this.editorcore;
26425 var editor= this.editor;
26428 var btn = function(id,cmd , toggle, handler, html){
26430 var event = toggle ? 'toggle' : 'click';
26435 xns: Roo.bootstrap,
26439 enableToggle:toggle !== false,
26441 pressed : toggle ? false : null,
26444 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26445 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26451 // var cb_box = function...
26456 xns: Roo.bootstrap,
26461 xns: Roo.bootstrap,
26465 Roo.each(this.formats, function(f) {
26466 style.menu.items.push({
26468 xns: Roo.bootstrap,
26469 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26474 editorcore.insertTag(this.tagname);
26481 children.push(style);
26483 btn('bold',false,true);
26484 btn('italic',false,true);
26485 btn('align-left', 'justifyleft',true);
26486 btn('align-center', 'justifycenter',true);
26487 btn('align-right' , 'justifyright',true);
26488 btn('link', false, false, function(btn) {
26489 //Roo.log("create link?");
26490 var url = prompt(this.createLinkText, this.defaultLinkValue);
26491 if(url && url != 'http:/'+'/'){
26492 this.editorcore.relayCmd('createlink', url);
26495 btn('list','insertunorderedlist',true);
26496 btn('pencil', false,true, function(btn){
26498 this.toggleSourceEdit(btn.pressed);
26501 if (this.editor.btns.length > 0) {
26502 for (var i = 0; i<this.editor.btns.length; i++) {
26503 children.push(this.editor.btns[i]);
26511 xns: Roo.bootstrap,
26516 xns: Roo.bootstrap,
26521 cog.menu.items.push({
26523 xns: Roo.bootstrap,
26524 html : Clean styles,
26529 editorcore.insertTag(this.tagname);
26538 this.xtype = 'NavSimplebar';
26540 for(var i=0;i< children.length;i++) {
26542 this.buttons.add(this.addxtypeChild(children[i]));
26546 editor.on('editorevent', this.updateToolbar, this);
26548 onBtnClick : function(id)
26550 this.editorcore.relayCmd(id);
26551 this.editorcore.focus();
26555 * Protected method that will not generally be called directly. It triggers
26556 * a toolbar update by reading the markup state of the current selection in the editor.
26558 updateToolbar: function(){
26560 if(!this.editorcore.activated){
26561 this.editor.onFirstFocus(); // is this neeed?
26565 var btns = this.buttons;
26566 var doc = this.editorcore.doc;
26567 btns.get('bold').setActive(doc.queryCommandState('bold'));
26568 btns.get('italic').setActive(doc.queryCommandState('italic'));
26569 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26571 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26572 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26573 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26575 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26576 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26579 var ans = this.editorcore.getAllAncestors();
26580 if (this.formatCombo) {
26583 var store = this.formatCombo.store;
26584 this.formatCombo.setValue("");
26585 for (var i =0; i < ans.length;i++) {
26586 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26588 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26596 // hides menus... - so this cant be on a menu...
26597 Roo.bootstrap.MenuMgr.hideAll();
26599 Roo.bootstrap.MenuMgr.hideAll();
26600 //this.editorsyncValue();
26602 onFirstFocus: function() {
26603 this.buttons.each(function(item){
26607 toggleSourceEdit : function(sourceEditMode){
26610 if(sourceEditMode){
26611 Roo.log("disabling buttons");
26612 this.buttons.each( function(item){
26613 if(item.cmd != 'pencil'){
26619 Roo.log("enabling buttons");
26620 if(this.editorcore.initialized){
26621 this.buttons.each( function(item){
26627 Roo.log("calling toggole on editor");
26628 // tell the editor that it's been pressed..
26629 this.editor.toggleSourceEdit(sourceEditMode);
26643 * @class Roo.bootstrap.Markdown
26644 * @extends Roo.bootstrap.TextArea
26645 * Bootstrap Showdown editable area
26646 * @cfg {string} content
26649 * Create a new Showdown
26652 Roo.bootstrap.Markdown = function(config){
26653 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26657 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26661 initEvents : function()
26664 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26665 this.markdownEl = this.el.createChild({
26666 cls : 'roo-markdown-area'
26668 this.inputEl().addClass('d-none');
26669 if (this.getValue() == '') {
26670 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26673 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26675 this.markdownEl.on('click', this.toggleTextEdit, this);
26676 this.on('blur', this.toggleTextEdit, this);
26677 this.on('specialkey', this.resizeTextArea, this);
26680 toggleTextEdit : function()
26682 var sh = this.markdownEl.getHeight();
26683 this.inputEl().addClass('d-none');
26684 this.markdownEl.addClass('d-none');
26685 if (!this.editing) {
26687 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26688 this.inputEl().removeClass('d-none');
26689 this.inputEl().focus();
26690 this.editing = true;
26693 // show showdown...
26694 this.updateMarkdown();
26695 this.markdownEl.removeClass('d-none');
26696 this.editing = false;
26699 updateMarkdown : function()
26701 if (this.getValue() == '') {
26702 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26706 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26709 resizeTextArea: function () {
26712 Roo.log([sh, this.getValue().split("\n").length * 30]);
26713 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26715 setValue : function(val)
26717 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26718 if (!this.editing) {
26719 this.updateMarkdown();
26725 if (!this.editing) {
26726 this.toggleTextEdit();
26734 * @class Roo.bootstrap.Table.AbstractSelectionModel
26735 * @extends Roo.util.Observable
26736 * Abstract base class for grid SelectionModels. It provides the interface that should be
26737 * implemented by descendant classes. This class should not be directly instantiated.
26740 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26741 this.locked = false;
26742 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26746 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26747 /** @ignore Called by the grid automatically. Do not call directly. */
26748 init : function(grid){
26754 * Locks the selections.
26757 this.locked = true;
26761 * Unlocks the selections.
26763 unlock : function(){
26764 this.locked = false;
26768 * Returns true if the selections are locked.
26769 * @return {Boolean}
26771 isLocked : function(){
26772 return this.locked;
26776 initEvents : function ()
26782 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26783 * @class Roo.bootstrap.Table.RowSelectionModel
26784 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26785 * It supports multiple selections and keyboard selection/navigation.
26787 * @param {Object} config
26790 Roo.bootstrap.Table.RowSelectionModel = function(config){
26791 Roo.apply(this, config);
26792 this.selections = new Roo.util.MixedCollection(false, function(o){
26797 this.lastActive = false;
26801 * @event selectionchange
26802 * Fires when the selection changes
26803 * @param {SelectionModel} this
26805 "selectionchange" : true,
26807 * @event afterselectionchange
26808 * Fires after the selection changes (eg. by key press or clicking)
26809 * @param {SelectionModel} this
26811 "afterselectionchange" : true,
26813 * @event beforerowselect
26814 * Fires when a row is selected being selected, return false to cancel.
26815 * @param {SelectionModel} this
26816 * @param {Number} rowIndex The selected index
26817 * @param {Boolean} keepExisting False if other selections will be cleared
26819 "beforerowselect" : true,
26822 * Fires when a row is selected.
26823 * @param {SelectionModel} this
26824 * @param {Number} rowIndex The selected index
26825 * @param {Roo.data.Record} r The record
26827 "rowselect" : true,
26829 * @event rowdeselect
26830 * Fires when a row is deselected.
26831 * @param {SelectionModel} this
26832 * @param {Number} rowIndex The selected index
26834 "rowdeselect" : true
26836 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26837 this.locked = false;
26840 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26842 * @cfg {Boolean} singleSelect
26843 * True to allow selection of only one row at a time (defaults to false)
26845 singleSelect : false,
26848 initEvents : function()
26851 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26852 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26853 //}else{ // allow click to work like normal
26854 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26856 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26857 this.grid.on("rowclick", this.handleMouseDown, this);
26859 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26860 "up" : function(e){
26862 this.selectPrevious(e.shiftKey);
26863 }else if(this.last !== false && this.lastActive !== false){
26864 var last = this.last;
26865 this.selectRange(this.last, this.lastActive-1);
26866 this.grid.getView().focusRow(this.lastActive);
26867 if(last !== false){
26871 this.selectFirstRow();
26873 this.fireEvent("afterselectionchange", this);
26875 "down" : function(e){
26877 this.selectNext(e.shiftKey);
26878 }else if(this.last !== false && this.lastActive !== false){
26879 var last = this.last;
26880 this.selectRange(this.last, this.lastActive+1);
26881 this.grid.getView().focusRow(this.lastActive);
26882 if(last !== false){
26886 this.selectFirstRow();
26888 this.fireEvent("afterselectionchange", this);
26892 this.grid.store.on('load', function(){
26893 this.selections.clear();
26896 var view = this.grid.view;
26897 view.on("refresh", this.onRefresh, this);
26898 view.on("rowupdated", this.onRowUpdated, this);
26899 view.on("rowremoved", this.onRemove, this);
26904 onRefresh : function()
26906 var ds = this.grid.store, i, v = this.grid.view;
26907 var s = this.selections;
26908 s.each(function(r){
26909 if((i = ds.indexOfId(r.id)) != -1){
26918 onRemove : function(v, index, r){
26919 this.selections.remove(r);
26923 onRowUpdated : function(v, index, r){
26924 if(this.isSelected(r)){
26925 v.onRowSelect(index);
26931 * @param {Array} records The records to select
26932 * @param {Boolean} keepExisting (optional) True to keep existing selections
26934 selectRecords : function(records, keepExisting)
26937 this.clearSelections();
26939 var ds = this.grid.store;
26940 for(var i = 0, len = records.length; i < len; i++){
26941 this.selectRow(ds.indexOf(records[i]), true);
26946 * Gets the number of selected rows.
26949 getCount : function(){
26950 return this.selections.length;
26954 * Selects the first row in the grid.
26956 selectFirstRow : function(){
26961 * Select the last row.
26962 * @param {Boolean} keepExisting (optional) True to keep existing selections
26964 selectLastRow : function(keepExisting){
26965 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26966 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26970 * Selects the row immediately following the last selected row.
26971 * @param {Boolean} keepExisting (optional) True to keep existing selections
26973 selectNext : function(keepExisting)
26975 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26976 this.selectRow(this.last+1, keepExisting);
26977 this.grid.getView().focusRow(this.last);
26982 * Selects the row that precedes the last selected row.
26983 * @param {Boolean} keepExisting (optional) True to keep existing selections
26985 selectPrevious : function(keepExisting){
26987 this.selectRow(this.last-1, keepExisting);
26988 this.grid.getView().focusRow(this.last);
26993 * Returns the selected records
26994 * @return {Array} Array of selected records
26996 getSelections : function(){
26997 return [].concat(this.selections.items);
27001 * Returns the first selected record.
27004 getSelected : function(){
27005 return this.selections.itemAt(0);
27010 * Clears all selections.
27012 clearSelections : function(fast)
27018 var ds = this.grid.store;
27019 var s = this.selections;
27020 s.each(function(r){
27021 this.deselectRow(ds.indexOfId(r.id));
27025 this.selections.clear();
27032 * Selects all rows.
27034 selectAll : function(){
27038 this.selections.clear();
27039 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27040 this.selectRow(i, true);
27045 * Returns True if there is a selection.
27046 * @return {Boolean}
27048 hasSelection : function(){
27049 return this.selections.length > 0;
27053 * Returns True if the specified row is selected.
27054 * @param {Number/Record} record The record or index of the record to check
27055 * @return {Boolean}
27057 isSelected : function(index){
27058 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27059 return (r && this.selections.key(r.id) ? true : false);
27063 * Returns True if the specified record id is selected.
27064 * @param {String} id The id of record to check
27065 * @return {Boolean}
27067 isIdSelected : function(id){
27068 return (this.selections.key(id) ? true : false);
27073 handleMouseDBClick : function(e, t){
27077 handleMouseDown : function(e, t)
27079 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27080 if(this.isLocked() || rowIndex < 0 ){
27083 if(e.shiftKey && this.last !== false){
27084 var last = this.last;
27085 this.selectRange(last, rowIndex, e.ctrlKey);
27086 this.last = last; // reset the last
27090 var isSelected = this.isSelected(rowIndex);
27091 //Roo.log("select row:" + rowIndex);
27093 this.deselectRow(rowIndex);
27095 this.selectRow(rowIndex, true);
27099 if(e.button !== 0 && isSelected){
27100 alert('rowIndex 2: ' + rowIndex);
27101 view.focusRow(rowIndex);
27102 }else if(e.ctrlKey && isSelected){
27103 this.deselectRow(rowIndex);
27104 }else if(!isSelected){
27105 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27106 view.focusRow(rowIndex);
27110 this.fireEvent("afterselectionchange", this);
27113 handleDragableRowClick : function(grid, rowIndex, e)
27115 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27116 this.selectRow(rowIndex, false);
27117 grid.view.focusRow(rowIndex);
27118 this.fireEvent("afterselectionchange", this);
27123 * Selects multiple rows.
27124 * @param {Array} rows Array of the indexes of the row to select
27125 * @param {Boolean} keepExisting (optional) True to keep existing selections
27127 selectRows : function(rows, keepExisting){
27129 this.clearSelections();
27131 for(var i = 0, len = rows.length; i < len; i++){
27132 this.selectRow(rows[i], true);
27137 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27138 * @param {Number} startRow The index of the first row in the range
27139 * @param {Number} endRow The index of the last row in the range
27140 * @param {Boolean} keepExisting (optional) True to retain existing selections
27142 selectRange : function(startRow, endRow, keepExisting){
27147 this.clearSelections();
27149 if(startRow <= endRow){
27150 for(var i = startRow; i <= endRow; i++){
27151 this.selectRow(i, true);
27154 for(var i = startRow; i >= endRow; i--){
27155 this.selectRow(i, true);
27161 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27162 * @param {Number} startRow The index of the first row in the range
27163 * @param {Number} endRow The index of the last row in the range
27165 deselectRange : function(startRow, endRow, preventViewNotify){
27169 for(var i = startRow; i <= endRow; i++){
27170 this.deselectRow(i, preventViewNotify);
27176 * @param {Number} row The index of the row to select
27177 * @param {Boolean} keepExisting (optional) True to keep existing selections
27179 selectRow : function(index, keepExisting, preventViewNotify)
27181 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27184 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27185 if(!keepExisting || this.singleSelect){
27186 this.clearSelections();
27189 var r = this.grid.store.getAt(index);
27190 //console.log('selectRow - record id :' + r.id);
27192 this.selections.add(r);
27193 this.last = this.lastActive = index;
27194 if(!preventViewNotify){
27195 var proxy = new Roo.Element(
27196 this.grid.getRowDom(index)
27198 proxy.addClass('bg-info info');
27200 this.fireEvent("rowselect", this, index, r);
27201 this.fireEvent("selectionchange", this);
27207 * @param {Number} row The index of the row to deselect
27209 deselectRow : function(index, preventViewNotify)
27214 if(this.last == index){
27217 if(this.lastActive == index){
27218 this.lastActive = false;
27221 var r = this.grid.store.getAt(index);
27226 this.selections.remove(r);
27227 //.console.log('deselectRow - record id :' + r.id);
27228 if(!preventViewNotify){
27230 var proxy = new Roo.Element(
27231 this.grid.getRowDom(index)
27233 proxy.removeClass('bg-info info');
27235 this.fireEvent("rowdeselect", this, index);
27236 this.fireEvent("selectionchange", this);
27240 restoreLast : function(){
27242 this.last = this._last;
27247 acceptsNav : function(row, col, cm){
27248 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27252 onEditorKey : function(field, e){
27253 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27258 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27260 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27262 }else if(k == e.ENTER && !e.ctrlKey){
27266 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27268 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27270 }else if(k == e.ESC){
27274 g.startEditing(newCell[0], newCell[1]);
27280 * Ext JS Library 1.1.1
27281 * Copyright(c) 2006-2007, Ext JS, LLC.
27283 * Originally Released Under LGPL - original licence link has changed is not relivant.
27286 * <script type="text/javascript">
27290 * @class Roo.bootstrap.PagingToolbar
27291 * @extends Roo.bootstrap.NavSimplebar
27292 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27294 * Create a new PagingToolbar
27295 * @param {Object} config The config object
27296 * @param {Roo.data.Store} store
27298 Roo.bootstrap.PagingToolbar = function(config)
27300 // old args format still supported... - xtype is prefered..
27301 // created from xtype...
27303 this.ds = config.dataSource;
27305 if (config.store && !this.ds) {
27306 this.store= Roo.factory(config.store, Roo.data);
27307 this.ds = this.store;
27308 this.ds.xmodule = this.xmodule || false;
27311 this.toolbarItems = [];
27312 if (config.items) {
27313 this.toolbarItems = config.items;
27316 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27321 this.bind(this.ds);
27324 if (Roo.bootstrap.version == 4) {
27325 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27327 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27332 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27334 * @cfg {Roo.data.Store} dataSource
27335 * The underlying data store providing the paged data
27338 * @cfg {String/HTMLElement/Element} container
27339 * container The id or element that will contain the toolbar
27342 * @cfg {Boolean} displayInfo
27343 * True to display the displayMsg (defaults to false)
27346 * @cfg {Number} pageSize
27347 * The number of records to display per page (defaults to 20)
27351 * @cfg {String} displayMsg
27352 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27354 displayMsg : 'Displaying {0} - {1} of {2}',
27356 * @cfg {String} emptyMsg
27357 * The message to display when no records are found (defaults to "No data to display")
27359 emptyMsg : 'No data to display',
27361 * Customizable piece of the default paging text (defaults to "Page")
27364 beforePageText : "Page",
27366 * Customizable piece of the default paging text (defaults to "of %0")
27369 afterPageText : "of {0}",
27371 * Customizable piece of the default paging text (defaults to "First Page")
27374 firstText : "First Page",
27376 * Customizable piece of the default paging text (defaults to "Previous Page")
27379 prevText : "Previous Page",
27381 * Customizable piece of the default paging text (defaults to "Next Page")
27384 nextText : "Next Page",
27386 * Customizable piece of the default paging text (defaults to "Last Page")
27389 lastText : "Last Page",
27391 * Customizable piece of the default paging text (defaults to "Refresh")
27394 refreshText : "Refresh",
27398 onRender : function(ct, position)
27400 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27401 this.navgroup.parentId = this.id;
27402 this.navgroup.onRender(this.el, null);
27403 // add the buttons to the navgroup
27405 if(this.displayInfo){
27406 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27407 this.displayEl = this.el.select('.x-paging-info', true).first();
27408 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27409 // this.displayEl = navel.el.select('span',true).first();
27415 Roo.each(_this.buttons, function(e){ // this might need to use render????
27416 Roo.factory(e).render(_this.el);
27420 Roo.each(_this.toolbarItems, function(e) {
27421 _this.navgroup.addItem(e);
27425 this.first = this.navgroup.addItem({
27426 tooltip: this.firstText,
27427 cls: "prev btn-outline-secondary",
27428 html : ' <i class="fa fa-step-backward"></i>',
27430 preventDefault: true,
27431 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27434 this.prev = this.navgroup.addItem({
27435 tooltip: this.prevText,
27436 cls: "prev btn-outline-secondary",
27437 html : ' <i class="fa fa-backward"></i>',
27439 preventDefault: true,
27440 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27442 //this.addSeparator();
27445 var field = this.navgroup.addItem( {
27447 cls : 'x-paging-position btn-outline-secondary',
27449 html : this.beforePageText +
27450 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27451 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27454 this.field = field.el.select('input', true).first();
27455 this.field.on("keydown", this.onPagingKeydown, this);
27456 this.field.on("focus", function(){this.dom.select();});
27459 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27460 //this.field.setHeight(18);
27461 //this.addSeparator();
27462 this.next = this.navgroup.addItem({
27463 tooltip: this.nextText,
27464 cls: "next btn-outline-secondary",
27465 html : ' <i class="fa fa-forward"></i>',
27467 preventDefault: true,
27468 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27470 this.last = this.navgroup.addItem({
27471 tooltip: this.lastText,
27472 html : ' <i class="fa fa-step-forward"></i>',
27473 cls: "next btn-outline-secondary",
27475 preventDefault: true,
27476 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27478 //this.addSeparator();
27479 this.loading = this.navgroup.addItem({
27480 tooltip: this.refreshText,
27481 cls: "btn-outline-secondary",
27482 html : ' <i class="fa fa-refresh"></i>',
27483 preventDefault: true,
27484 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27490 updateInfo : function(){
27491 if(this.displayEl){
27492 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27493 var msg = count == 0 ?
27497 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27499 this.displayEl.update(msg);
27504 onLoad : function(ds, r, o)
27506 this.cursor = o.params && o.params.start ? o.params.start : 0;
27508 var d = this.getPageData(),
27513 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27514 this.field.dom.value = ap;
27515 this.first.setDisabled(ap == 1);
27516 this.prev.setDisabled(ap == 1);
27517 this.next.setDisabled(ap == ps);
27518 this.last.setDisabled(ap == ps);
27519 this.loading.enable();
27524 getPageData : function(){
27525 var total = this.ds.getTotalCount();
27528 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27529 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27534 onLoadError : function(){
27535 this.loading.enable();
27539 onPagingKeydown : function(e){
27540 var k = e.getKey();
27541 var d = this.getPageData();
27543 var v = this.field.dom.value, pageNum;
27544 if(!v || isNaN(pageNum = parseInt(v, 10))){
27545 this.field.dom.value = d.activePage;
27548 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27549 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27552 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))
27554 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27555 this.field.dom.value = pageNum;
27556 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27559 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27561 var v = this.field.dom.value, pageNum;
27562 var increment = (e.shiftKey) ? 10 : 1;
27563 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27566 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27567 this.field.dom.value = d.activePage;
27570 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27572 this.field.dom.value = parseInt(v, 10) + increment;
27573 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27574 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27581 beforeLoad : function(){
27583 this.loading.disable();
27588 onClick : function(which){
27597 ds.load({params:{start: 0, limit: this.pageSize}});
27600 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27603 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27606 var total = ds.getTotalCount();
27607 var extra = total % this.pageSize;
27608 var lastStart = extra ? (total - extra) : total-this.pageSize;
27609 ds.load({params:{start: lastStart, limit: this.pageSize}});
27612 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27618 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27619 * @param {Roo.data.Store} store The data store to unbind
27621 unbind : function(ds){
27622 ds.un("beforeload", this.beforeLoad, this);
27623 ds.un("load", this.onLoad, this);
27624 ds.un("loadexception", this.onLoadError, this);
27625 ds.un("remove", this.updateInfo, this);
27626 ds.un("add", this.updateInfo, this);
27627 this.ds = undefined;
27631 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27632 * @param {Roo.data.Store} store The data store to bind
27634 bind : function(ds){
27635 ds.on("beforeload", this.beforeLoad, this);
27636 ds.on("load", this.onLoad, this);
27637 ds.on("loadexception", this.onLoadError, this);
27638 ds.on("remove", this.updateInfo, this);
27639 ds.on("add", this.updateInfo, this);
27650 * @class Roo.bootstrap.MessageBar
27651 * @extends Roo.bootstrap.Component
27652 * Bootstrap MessageBar class
27653 * @cfg {String} html contents of the MessageBar
27654 * @cfg {String} weight (info | success | warning | danger) default info
27655 * @cfg {String} beforeClass insert the bar before the given class
27656 * @cfg {Boolean} closable (true | false) default false
27657 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27660 * Create a new Element
27661 * @param {Object} config The config object
27664 Roo.bootstrap.MessageBar = function(config){
27665 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27668 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27674 beforeClass: 'bootstrap-sticky-wrap',
27676 getAutoCreate : function(){
27680 cls: 'alert alert-dismissable alert-' + this.weight,
27685 html: this.html || ''
27691 cfg.cls += ' alert-messages-fixed';
27705 onRender : function(ct, position)
27707 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27710 var cfg = Roo.apply({}, this.getAutoCreate());
27714 cfg.cls += ' ' + this.cls;
27717 cfg.style = this.style;
27719 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27721 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27724 this.el.select('>button.close').on('click', this.hide, this);
27730 if (!this.rendered) {
27736 this.fireEvent('show', this);
27742 if (!this.rendered) {
27748 this.fireEvent('hide', this);
27751 update : function()
27753 // var e = this.el.dom.firstChild;
27755 // if(this.closable){
27756 // e = e.nextSibling;
27759 // e.data = this.html || '';
27761 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27777 * @class Roo.bootstrap.Graph
27778 * @extends Roo.bootstrap.Component
27779 * Bootstrap Graph class
27783 @cfg {String} graphtype bar | vbar | pie
27784 @cfg {number} g_x coodinator | centre x (pie)
27785 @cfg {number} g_y coodinator | centre y (pie)
27786 @cfg {number} g_r radius (pie)
27787 @cfg {number} g_height height of the chart (respected by all elements in the set)
27788 @cfg {number} g_width width of the chart (respected by all elements in the set)
27789 @cfg {Object} title The title of the chart
27792 -opts (object) options for the chart
27794 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27795 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27797 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.
27798 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27800 o stretch (boolean)
27802 -opts (object) options for the pie
27805 o startAngle (number)
27806 o endAngle (number)
27810 * Create a new Input
27811 * @param {Object} config The config object
27814 Roo.bootstrap.Graph = function(config){
27815 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27821 * The img click event for the img.
27822 * @param {Roo.EventObject} e
27828 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27839 //g_colors: this.colors,
27846 getAutoCreate : function(){
27857 onRender : function(ct,position){
27860 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27862 if (typeof(Raphael) == 'undefined') {
27863 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27867 this.raphael = Raphael(this.el.dom);
27869 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27870 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27871 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27872 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27874 r.text(160, 10, "Single Series Chart").attr(txtattr);
27875 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27876 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27877 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27879 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27880 r.barchart(330, 10, 300, 220, data1);
27881 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27882 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27885 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27886 // r.barchart(30, 30, 560, 250, xdata, {
27887 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27888 // axis : "0 0 1 1",
27889 // axisxlabels : xdata
27890 // //yvalues : cols,
27893 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27895 // this.load(null,xdata,{
27896 // axis : "0 0 1 1",
27897 // axisxlabels : xdata
27902 load : function(graphtype,xdata,opts)
27904 this.raphael.clear();
27906 graphtype = this.graphtype;
27911 var r = this.raphael,
27912 fin = function () {
27913 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27915 fout = function () {
27916 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27918 pfin = function() {
27919 this.sector.stop();
27920 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27923 this.label[0].stop();
27924 this.label[0].attr({ r: 7.5 });
27925 this.label[1].attr({ "font-weight": 800 });
27928 pfout = function() {
27929 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27932 this.label[0].animate({ r: 5 }, 500, "bounce");
27933 this.label[1].attr({ "font-weight": 400 });
27939 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27942 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27945 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27946 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27948 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27955 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27960 setTitle: function(o)
27965 initEvents: function() {
27968 this.el.on('click', this.onClick, this);
27972 onClick : function(e)
27974 Roo.log('img onclick');
27975 this.fireEvent('click', this, e);
27987 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27990 * @class Roo.bootstrap.dash.NumberBox
27991 * @extends Roo.bootstrap.Component
27992 * Bootstrap NumberBox class
27993 * @cfg {String} headline Box headline
27994 * @cfg {String} content Box content
27995 * @cfg {String} icon Box icon
27996 * @cfg {String} footer Footer text
27997 * @cfg {String} fhref Footer href
28000 * Create a new NumberBox
28001 * @param {Object} config The config object
28005 Roo.bootstrap.dash.NumberBox = function(config){
28006 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28010 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28019 getAutoCreate : function(){
28023 cls : 'small-box ',
28031 cls : 'roo-headline',
28032 html : this.headline
28036 cls : 'roo-content',
28037 html : this.content
28051 cls : 'ion ' + this.icon
28060 cls : 'small-box-footer',
28061 href : this.fhref || '#',
28065 cfg.cn.push(footer);
28072 onRender : function(ct,position){
28073 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28080 setHeadline: function (value)
28082 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28085 setFooter: function (value, href)
28087 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28090 this.el.select('a.small-box-footer',true).first().attr('href', href);
28095 setContent: function (value)
28097 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28100 initEvents: function()
28114 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28117 * @class Roo.bootstrap.dash.TabBox
28118 * @extends Roo.bootstrap.Component
28119 * Bootstrap TabBox class
28120 * @cfg {String} title Title of the TabBox
28121 * @cfg {String} icon Icon of the TabBox
28122 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28123 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28126 * Create a new TabBox
28127 * @param {Object} config The config object
28131 Roo.bootstrap.dash.TabBox = function(config){
28132 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28137 * When a pane is added
28138 * @param {Roo.bootstrap.dash.TabPane} pane
28142 * @event activatepane
28143 * When a pane is activated
28144 * @param {Roo.bootstrap.dash.TabPane} pane
28146 "activatepane" : true
28154 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28159 tabScrollable : false,
28161 getChildContainer : function()
28163 return this.el.select('.tab-content', true).first();
28166 getAutoCreate : function(){
28170 cls: 'pull-left header',
28178 cls: 'fa ' + this.icon
28184 cls: 'nav nav-tabs pull-right',
28190 if(this.tabScrollable){
28197 cls: 'nav nav-tabs pull-right',
28208 cls: 'nav-tabs-custom',
28213 cls: 'tab-content no-padding',
28221 initEvents : function()
28223 //Roo.log('add add pane handler');
28224 this.on('addpane', this.onAddPane, this);
28227 * Updates the box title
28228 * @param {String} html to set the title to.
28230 setTitle : function(value)
28232 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28234 onAddPane : function(pane)
28236 this.panes.push(pane);
28237 //Roo.log('addpane');
28239 // tabs are rendere left to right..
28240 if(!this.showtabs){
28244 var ctr = this.el.select('.nav-tabs', true).first();
28247 var existing = ctr.select('.nav-tab',true);
28248 var qty = existing.getCount();;
28251 var tab = ctr.createChild({
28253 cls : 'nav-tab' + (qty ? '' : ' active'),
28261 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28264 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28266 pane.el.addClass('active');
28271 onTabClick : function(ev,un,ob,pane)
28273 //Roo.log('tab - prev default');
28274 ev.preventDefault();
28277 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28278 pane.tab.addClass('active');
28279 //Roo.log(pane.title);
28280 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28281 // technically we should have a deactivate event.. but maybe add later.
28282 // and it should not de-activate the selected tab...
28283 this.fireEvent('activatepane', pane);
28284 pane.el.addClass('active');
28285 pane.fireEvent('activate');
28290 getActivePane : function()
28293 Roo.each(this.panes, function(p) {
28294 if(p.el.hasClass('active')){
28315 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28317 * @class Roo.bootstrap.TabPane
28318 * @extends Roo.bootstrap.Component
28319 * Bootstrap TabPane class
28320 * @cfg {Boolean} active (false | true) Default false
28321 * @cfg {String} title title of panel
28325 * Create a new TabPane
28326 * @param {Object} config The config object
28329 Roo.bootstrap.dash.TabPane = function(config){
28330 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28336 * When a pane is activated
28337 * @param {Roo.bootstrap.dash.TabPane} pane
28344 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28349 // the tabBox that this is attached to.
28352 getAutoCreate : function()
28360 cfg.cls += ' active';
28365 initEvents : function()
28367 //Roo.log('trigger add pane handler');
28368 this.parent().fireEvent('addpane', this)
28372 * Updates the tab title
28373 * @param {String} html to set the title to.
28375 setTitle: function(str)
28381 this.tab.select('a', true).first().dom.innerHTML = str;
28398 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28401 * @class Roo.bootstrap.menu.Menu
28402 * @extends Roo.bootstrap.Component
28403 * Bootstrap Menu class - container for Menu
28404 * @cfg {String} html Text of the menu
28405 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28406 * @cfg {String} icon Font awesome icon
28407 * @cfg {String} pos Menu align to (top | bottom) default bottom
28411 * Create a new Menu
28412 * @param {Object} config The config object
28416 Roo.bootstrap.menu.Menu = function(config){
28417 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28421 * @event beforeshow
28422 * Fires before this menu is displayed
28423 * @param {Roo.bootstrap.menu.Menu} this
28427 * @event beforehide
28428 * Fires before this menu is hidden
28429 * @param {Roo.bootstrap.menu.Menu} this
28434 * Fires after this menu is displayed
28435 * @param {Roo.bootstrap.menu.Menu} this
28440 * Fires after this menu is hidden
28441 * @param {Roo.bootstrap.menu.Menu} this
28446 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28447 * @param {Roo.bootstrap.menu.Menu} this
28448 * @param {Roo.EventObject} e
28455 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28459 weight : 'default',
28464 getChildContainer : function() {
28465 if(this.isSubMenu){
28469 return this.el.select('ul.dropdown-menu', true).first();
28472 getAutoCreate : function()
28477 cls : 'roo-menu-text',
28485 cls : 'fa ' + this.icon
28496 cls : 'dropdown-button btn btn-' + this.weight,
28501 cls : 'dropdown-toggle btn btn-' + this.weight,
28511 cls : 'dropdown-menu'
28517 if(this.pos == 'top'){
28518 cfg.cls += ' dropup';
28521 if(this.isSubMenu){
28524 cls : 'dropdown-menu'
28531 onRender : function(ct, position)
28533 this.isSubMenu = ct.hasClass('dropdown-submenu');
28535 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28538 initEvents : function()
28540 if(this.isSubMenu){
28544 this.hidden = true;
28546 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28547 this.triggerEl.on('click', this.onTriggerPress, this);
28549 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28550 this.buttonEl.on('click', this.onClick, this);
28556 if(this.isSubMenu){
28560 return this.el.select('ul.dropdown-menu', true).first();
28563 onClick : function(e)
28565 this.fireEvent("click", this, e);
28568 onTriggerPress : function(e)
28570 if (this.isVisible()) {
28577 isVisible : function(){
28578 return !this.hidden;
28583 this.fireEvent("beforeshow", this);
28585 this.hidden = false;
28586 this.el.addClass('open');
28588 Roo.get(document).on("mouseup", this.onMouseUp, this);
28590 this.fireEvent("show", this);
28597 this.fireEvent("beforehide", this);
28599 this.hidden = true;
28600 this.el.removeClass('open');
28602 Roo.get(document).un("mouseup", this.onMouseUp);
28604 this.fireEvent("hide", this);
28607 onMouseUp : function()
28621 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28624 * @class Roo.bootstrap.menu.Item
28625 * @extends Roo.bootstrap.Component
28626 * Bootstrap MenuItem class
28627 * @cfg {Boolean} submenu (true | false) default false
28628 * @cfg {String} html text of the item
28629 * @cfg {String} href the link
28630 * @cfg {Boolean} disable (true | false) default false
28631 * @cfg {Boolean} preventDefault (true | false) default true
28632 * @cfg {String} icon Font awesome icon
28633 * @cfg {String} pos Submenu align to (left | right) default right
28637 * Create a new Item
28638 * @param {Object} config The config object
28642 Roo.bootstrap.menu.Item = function(config){
28643 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28647 * Fires when the mouse is hovering over this menu
28648 * @param {Roo.bootstrap.menu.Item} this
28649 * @param {Roo.EventObject} e
28654 * Fires when the mouse exits this menu
28655 * @param {Roo.bootstrap.menu.Item} this
28656 * @param {Roo.EventObject} e
28662 * The raw click event for the entire grid.
28663 * @param {Roo.EventObject} e
28669 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28674 preventDefault: true,
28679 getAutoCreate : function()
28684 cls : 'roo-menu-item-text',
28692 cls : 'fa ' + this.icon
28701 href : this.href || '#',
28708 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28712 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28714 if(this.pos == 'left'){
28715 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28722 initEvents : function()
28724 this.el.on('mouseover', this.onMouseOver, this);
28725 this.el.on('mouseout', this.onMouseOut, this);
28727 this.el.select('a', true).first().on('click', this.onClick, this);
28731 onClick : function(e)
28733 if(this.preventDefault){
28734 e.preventDefault();
28737 this.fireEvent("click", this, e);
28740 onMouseOver : function(e)
28742 if(this.submenu && this.pos == 'left'){
28743 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28746 this.fireEvent("mouseover", this, e);
28749 onMouseOut : function(e)
28751 this.fireEvent("mouseout", this, e);
28763 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28766 * @class Roo.bootstrap.menu.Separator
28767 * @extends Roo.bootstrap.Component
28768 * Bootstrap Separator class
28771 * Create a new Separator
28772 * @param {Object} config The config object
28776 Roo.bootstrap.menu.Separator = function(config){
28777 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28780 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28782 getAutoCreate : function(){
28803 * @class Roo.bootstrap.Tooltip
28804 * Bootstrap Tooltip class
28805 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28806 * to determine which dom element triggers the tooltip.
28808 * It needs to add support for additional attributes like tooltip-position
28811 * Create a new Toolti
28812 * @param {Object} config The config object
28815 Roo.bootstrap.Tooltip = function(config){
28816 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28818 this.alignment = Roo.bootstrap.Tooltip.alignment;
28820 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28821 this.alignment = config.alignment;
28826 Roo.apply(Roo.bootstrap.Tooltip, {
28828 * @function init initialize tooltip monitoring.
28832 currentTip : false,
28833 currentRegion : false,
28839 Roo.get(document).on('mouseover', this.enter ,this);
28840 Roo.get(document).on('mouseout', this.leave, this);
28843 this.currentTip = new Roo.bootstrap.Tooltip();
28846 enter : function(ev)
28848 var dom = ev.getTarget();
28850 //Roo.log(['enter',dom]);
28851 var el = Roo.fly(dom);
28852 if (this.currentEl) {
28854 //Roo.log(this.currentEl);
28855 //Roo.log(this.currentEl.contains(dom));
28856 if (this.currentEl == el) {
28859 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28865 if (this.currentTip.el) {
28866 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28870 if(!el || el.dom == document){
28876 // you can not look for children, as if el is the body.. then everythign is the child..
28877 if (!el.attr('tooltip')) { //
28878 if (!el.select("[tooltip]").elements.length) {
28881 // is the mouse over this child...?
28882 bindEl = el.select("[tooltip]").first();
28883 var xy = ev.getXY();
28884 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28885 //Roo.log("not in region.");
28888 //Roo.log("child element over..");
28891 this.currentEl = bindEl;
28892 this.currentTip.bind(bindEl);
28893 this.currentRegion = Roo.lib.Region.getRegion(dom);
28894 this.currentTip.enter();
28897 leave : function(ev)
28899 var dom = ev.getTarget();
28900 //Roo.log(['leave',dom]);
28901 if (!this.currentEl) {
28906 if (dom != this.currentEl.dom) {
28909 var xy = ev.getXY();
28910 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28913 // only activate leave if mouse cursor is outside... bounding box..
28918 if (this.currentTip) {
28919 this.currentTip.leave();
28921 //Roo.log('clear currentEl');
28922 this.currentEl = false;
28927 'left' : ['r-l', [-2,0], 'right'],
28928 'right' : ['l-r', [2,0], 'left'],
28929 'bottom' : ['t-b', [0,2], 'top'],
28930 'top' : [ 'b-t', [0,-2], 'bottom']
28936 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28941 delay : null, // can be { show : 300 , hide: 500}
28945 hoverState : null, //???
28947 placement : 'bottom',
28951 getAutoCreate : function(){
28958 cls : 'tooltip-arrow arrow'
28961 cls : 'tooltip-inner'
28968 bind : function(el)
28973 initEvents : function()
28975 this.arrowEl = this.el.select('.arrow', true).first();
28976 this.innerEl = this.el.select('.tooltip-inner', true).first();
28979 enter : function () {
28981 if (this.timeout != null) {
28982 clearTimeout(this.timeout);
28985 this.hoverState = 'in';
28986 //Roo.log("enter - show");
28987 if (!this.delay || !this.delay.show) {
28992 this.timeout = setTimeout(function () {
28993 if (_t.hoverState == 'in') {
28996 }, this.delay.show);
29000 clearTimeout(this.timeout);
29002 this.hoverState = 'out';
29003 if (!this.delay || !this.delay.hide) {
29009 this.timeout = setTimeout(function () {
29010 //Roo.log("leave - timeout");
29012 if (_t.hoverState == 'out') {
29014 Roo.bootstrap.Tooltip.currentEl = false;
29019 show : function (msg)
29022 this.render(document.body);
29025 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29027 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29029 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29031 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29032 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29034 var placement = typeof this.placement == 'function' ?
29035 this.placement.call(this, this.el, on_el) :
29038 var autoToken = /\s?auto?\s?/i;
29039 var autoPlace = autoToken.test(placement);
29041 placement = placement.replace(autoToken, '') || 'top';
29045 //this.el.setXY([0,0]);
29047 //this.el.dom.style.display='block';
29049 //this.el.appendTo(on_el);
29051 var p = this.getPosition();
29052 var box = this.el.getBox();
29058 var align = this.alignment[placement];
29060 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29062 if(placement == 'top' || placement == 'bottom'){
29064 placement = 'right';
29067 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29068 placement = 'left';
29071 var scroll = Roo.select('body', true).first().getScroll();
29073 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29077 align = this.alignment[placement];
29079 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29083 this.el.alignTo(this.bindEl, align[0],align[1]);
29084 //var arrow = this.el.select('.arrow',true).first();
29085 //arrow.set(align[2],
29087 this.el.addClass(placement);
29088 this.el.addClass("bs-tooltip-"+ placement);
29090 this.el.addClass('in fade show');
29092 this.hoverState = null;
29094 if (this.el.hasClass('fade')) {
29109 //this.el.setXY([0,0]);
29110 this.el.removeClass(['show', 'in']);
29126 * @class Roo.bootstrap.LocationPicker
29127 * @extends Roo.bootstrap.Component
29128 * Bootstrap LocationPicker class
29129 * @cfg {Number} latitude Position when init default 0
29130 * @cfg {Number} longitude Position when init default 0
29131 * @cfg {Number} zoom default 15
29132 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29133 * @cfg {Boolean} mapTypeControl default false
29134 * @cfg {Boolean} disableDoubleClickZoom default false
29135 * @cfg {Boolean} scrollwheel default true
29136 * @cfg {Boolean} streetViewControl default false
29137 * @cfg {Number} radius default 0
29138 * @cfg {String} locationName
29139 * @cfg {Boolean} draggable default true
29140 * @cfg {Boolean} enableAutocomplete default false
29141 * @cfg {Boolean} enableReverseGeocode default true
29142 * @cfg {String} markerTitle
29145 * Create a new LocationPicker
29146 * @param {Object} config The config object
29150 Roo.bootstrap.LocationPicker = function(config){
29152 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29157 * Fires when the picker initialized.
29158 * @param {Roo.bootstrap.LocationPicker} this
29159 * @param {Google Location} location
29163 * @event positionchanged
29164 * Fires when the picker position changed.
29165 * @param {Roo.bootstrap.LocationPicker} this
29166 * @param {Google Location} location
29168 positionchanged : true,
29171 * Fires when the map resize.
29172 * @param {Roo.bootstrap.LocationPicker} this
29177 * Fires when the map show.
29178 * @param {Roo.bootstrap.LocationPicker} this
29183 * Fires when the map hide.
29184 * @param {Roo.bootstrap.LocationPicker} this
29189 * Fires when click the map.
29190 * @param {Roo.bootstrap.LocationPicker} this
29191 * @param {Map event} e
29195 * @event mapRightClick
29196 * Fires when right click the map.
29197 * @param {Roo.bootstrap.LocationPicker} this
29198 * @param {Map event} e
29200 mapRightClick : true,
29202 * @event markerClick
29203 * Fires when click the marker.
29204 * @param {Roo.bootstrap.LocationPicker} this
29205 * @param {Map event} e
29207 markerClick : true,
29209 * @event markerRightClick
29210 * Fires when right click the marker.
29211 * @param {Roo.bootstrap.LocationPicker} this
29212 * @param {Map event} e
29214 markerRightClick : true,
29216 * @event OverlayViewDraw
29217 * Fires when OverlayView Draw
29218 * @param {Roo.bootstrap.LocationPicker} this
29220 OverlayViewDraw : true,
29222 * @event OverlayViewOnAdd
29223 * Fires when OverlayView Draw
29224 * @param {Roo.bootstrap.LocationPicker} this
29226 OverlayViewOnAdd : true,
29228 * @event OverlayViewOnRemove
29229 * Fires when OverlayView Draw
29230 * @param {Roo.bootstrap.LocationPicker} this
29232 OverlayViewOnRemove : true,
29234 * @event OverlayViewShow
29235 * Fires when OverlayView Draw
29236 * @param {Roo.bootstrap.LocationPicker} this
29237 * @param {Pixel} cpx
29239 OverlayViewShow : true,
29241 * @event OverlayViewHide
29242 * Fires when OverlayView Draw
29243 * @param {Roo.bootstrap.LocationPicker} this
29245 OverlayViewHide : true,
29247 * @event loadexception
29248 * Fires when load google lib failed.
29249 * @param {Roo.bootstrap.LocationPicker} this
29251 loadexception : true
29256 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29258 gMapContext: false,
29264 mapTypeControl: false,
29265 disableDoubleClickZoom: false,
29267 streetViewControl: false,
29271 enableAutocomplete: false,
29272 enableReverseGeocode: true,
29275 getAutoCreate: function()
29280 cls: 'roo-location-picker'
29286 initEvents: function(ct, position)
29288 if(!this.el.getWidth() || this.isApplied()){
29292 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29297 initial: function()
29299 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29300 this.fireEvent('loadexception', this);
29304 if(!this.mapTypeId){
29305 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29308 this.gMapContext = this.GMapContext();
29310 this.initOverlayView();
29312 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29316 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29317 _this.setPosition(_this.gMapContext.marker.position);
29320 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29321 _this.fireEvent('mapClick', this, event);
29325 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29326 _this.fireEvent('mapRightClick', this, event);
29330 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29331 _this.fireEvent('markerClick', this, event);
29335 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29336 _this.fireEvent('markerRightClick', this, event);
29340 this.setPosition(this.gMapContext.location);
29342 this.fireEvent('initial', this, this.gMapContext.location);
29345 initOverlayView: function()
29349 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29353 _this.fireEvent('OverlayViewDraw', _this);
29358 _this.fireEvent('OverlayViewOnAdd', _this);
29361 onRemove: function()
29363 _this.fireEvent('OverlayViewOnRemove', _this);
29366 show: function(cpx)
29368 _this.fireEvent('OverlayViewShow', _this, cpx);
29373 _this.fireEvent('OverlayViewHide', _this);
29379 fromLatLngToContainerPixel: function(event)
29381 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29384 isApplied: function()
29386 return this.getGmapContext() == false ? false : true;
29389 getGmapContext: function()
29391 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29394 GMapContext: function()
29396 var position = new google.maps.LatLng(this.latitude, this.longitude);
29398 var _map = new google.maps.Map(this.el.dom, {
29401 mapTypeId: this.mapTypeId,
29402 mapTypeControl: this.mapTypeControl,
29403 disableDoubleClickZoom: this.disableDoubleClickZoom,
29404 scrollwheel: this.scrollwheel,
29405 streetViewControl: this.streetViewControl,
29406 locationName: this.locationName,
29407 draggable: this.draggable,
29408 enableAutocomplete: this.enableAutocomplete,
29409 enableReverseGeocode: this.enableReverseGeocode
29412 var _marker = new google.maps.Marker({
29413 position: position,
29415 title: this.markerTitle,
29416 draggable: this.draggable
29423 location: position,
29424 radius: this.radius,
29425 locationName: this.locationName,
29426 addressComponents: {
29427 formatted_address: null,
29428 addressLine1: null,
29429 addressLine2: null,
29431 streetNumber: null,
29435 stateOrProvince: null
29438 domContainer: this.el.dom,
29439 geodecoder: new google.maps.Geocoder()
29443 drawCircle: function(center, radius, options)
29445 if (this.gMapContext.circle != null) {
29446 this.gMapContext.circle.setMap(null);
29450 options = Roo.apply({}, options, {
29451 strokeColor: "#0000FF",
29452 strokeOpacity: .35,
29454 fillColor: "#0000FF",
29458 options.map = this.gMapContext.map;
29459 options.radius = radius;
29460 options.center = center;
29461 this.gMapContext.circle = new google.maps.Circle(options);
29462 return this.gMapContext.circle;
29468 setPosition: function(location)
29470 this.gMapContext.location = location;
29471 this.gMapContext.marker.setPosition(location);
29472 this.gMapContext.map.panTo(location);
29473 this.drawCircle(location, this.gMapContext.radius, {});
29477 if (this.gMapContext.settings.enableReverseGeocode) {
29478 this.gMapContext.geodecoder.geocode({
29479 latLng: this.gMapContext.location
29480 }, function(results, status) {
29482 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29483 _this.gMapContext.locationName = results[0].formatted_address;
29484 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29486 _this.fireEvent('positionchanged', this, location);
29493 this.fireEvent('positionchanged', this, location);
29498 google.maps.event.trigger(this.gMapContext.map, "resize");
29500 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29502 this.fireEvent('resize', this);
29505 setPositionByLatLng: function(latitude, longitude)
29507 this.setPosition(new google.maps.LatLng(latitude, longitude));
29510 getCurrentPosition: function()
29513 latitude: this.gMapContext.location.lat(),
29514 longitude: this.gMapContext.location.lng()
29518 getAddressName: function()
29520 return this.gMapContext.locationName;
29523 getAddressComponents: function()
29525 return this.gMapContext.addressComponents;
29528 address_component_from_google_geocode: function(address_components)
29532 for (var i = 0; i < address_components.length; i++) {
29533 var component = address_components[i];
29534 if (component.types.indexOf("postal_code") >= 0) {
29535 result.postalCode = component.short_name;
29536 } else if (component.types.indexOf("street_number") >= 0) {
29537 result.streetNumber = component.short_name;
29538 } else if (component.types.indexOf("route") >= 0) {
29539 result.streetName = component.short_name;
29540 } else if (component.types.indexOf("neighborhood") >= 0) {
29541 result.city = component.short_name;
29542 } else if (component.types.indexOf("locality") >= 0) {
29543 result.city = component.short_name;
29544 } else if (component.types.indexOf("sublocality") >= 0) {
29545 result.district = component.short_name;
29546 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29547 result.stateOrProvince = component.short_name;
29548 } else if (component.types.indexOf("country") >= 0) {
29549 result.country = component.short_name;
29553 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29554 result.addressLine2 = "";
29558 setZoomLevel: function(zoom)
29560 this.gMapContext.map.setZoom(zoom);
29573 this.fireEvent('show', this);
29584 this.fireEvent('hide', this);
29589 Roo.apply(Roo.bootstrap.LocationPicker, {
29591 OverlayView : function(map, options)
29593 options = options || {};
29600 * @class Roo.bootstrap.Alert
29601 * @extends Roo.bootstrap.Component
29602 * Bootstrap Alert class - shows an alert area box
29604 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29605 Enter a valid email address
29608 * @cfg {String} title The title of alert
29609 * @cfg {String} html The content of alert
29610 * @cfg {String} weight ( success | info | warning | danger )
29611 * @cfg {String} faicon font-awesomeicon
29614 * Create a new alert
29615 * @param {Object} config The config object
29619 Roo.bootstrap.Alert = function(config){
29620 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29624 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29631 getAutoCreate : function()
29640 cls : 'roo-alert-icon'
29645 cls : 'roo-alert-title',
29650 cls : 'roo-alert-text',
29657 cfg.cn[0].cls += ' fa ' + this.faicon;
29661 cfg.cls += ' alert-' + this.weight;
29667 initEvents: function()
29669 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29672 setTitle : function(str)
29674 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29677 setText : function(str)
29679 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29682 setWeight : function(weight)
29685 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29688 this.weight = weight;
29690 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29693 setIcon : function(icon)
29696 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29699 this.faicon = icon;
29701 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29722 * @class Roo.bootstrap.UploadCropbox
29723 * @extends Roo.bootstrap.Component
29724 * Bootstrap UploadCropbox class
29725 * @cfg {String} emptyText show when image has been loaded
29726 * @cfg {String} rotateNotify show when image too small to rotate
29727 * @cfg {Number} errorTimeout default 3000
29728 * @cfg {Number} minWidth default 300
29729 * @cfg {Number} minHeight default 300
29730 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29731 * @cfg {Boolean} isDocument (true|false) default false
29732 * @cfg {String} url action url
29733 * @cfg {String} paramName default 'imageUpload'
29734 * @cfg {String} method default POST
29735 * @cfg {Boolean} loadMask (true|false) default true
29736 * @cfg {Boolean} loadingText default 'Loading...'
29739 * Create a new UploadCropbox
29740 * @param {Object} config The config object
29743 Roo.bootstrap.UploadCropbox = function(config){
29744 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29748 * @event beforeselectfile
29749 * Fire before select file
29750 * @param {Roo.bootstrap.UploadCropbox} this
29752 "beforeselectfile" : true,
29755 * Fire after initEvent
29756 * @param {Roo.bootstrap.UploadCropbox} this
29761 * Fire after initEvent
29762 * @param {Roo.bootstrap.UploadCropbox} this
29763 * @param {String} data
29768 * Fire when preparing the file data
29769 * @param {Roo.bootstrap.UploadCropbox} this
29770 * @param {Object} file
29775 * Fire when get exception
29776 * @param {Roo.bootstrap.UploadCropbox} this
29777 * @param {XMLHttpRequest} xhr
29779 "exception" : true,
29781 * @event beforeloadcanvas
29782 * Fire before load the canvas
29783 * @param {Roo.bootstrap.UploadCropbox} this
29784 * @param {String} src
29786 "beforeloadcanvas" : true,
29789 * Fire when trash image
29790 * @param {Roo.bootstrap.UploadCropbox} this
29795 * Fire when download the image
29796 * @param {Roo.bootstrap.UploadCropbox} this
29800 * @event footerbuttonclick
29801 * Fire when footerbuttonclick
29802 * @param {Roo.bootstrap.UploadCropbox} this
29803 * @param {String} type
29805 "footerbuttonclick" : true,
29809 * @param {Roo.bootstrap.UploadCropbox} this
29814 * Fire when rotate the image
29815 * @param {Roo.bootstrap.UploadCropbox} this
29816 * @param {String} pos
29821 * Fire when inspect the file
29822 * @param {Roo.bootstrap.UploadCropbox} this
29823 * @param {Object} file
29828 * Fire when xhr upload the file
29829 * @param {Roo.bootstrap.UploadCropbox} this
29830 * @param {Object} data
29835 * Fire when arrange the file data
29836 * @param {Roo.bootstrap.UploadCropbox} this
29837 * @param {Object} formData
29842 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29845 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29847 emptyText : 'Click to upload image',
29848 rotateNotify : 'Image is too small to rotate',
29849 errorTimeout : 3000,
29863 cropType : 'image/jpeg',
29865 canvasLoaded : false,
29866 isDocument : false,
29868 paramName : 'imageUpload',
29870 loadingText : 'Loading...',
29873 getAutoCreate : function()
29877 cls : 'roo-upload-cropbox',
29881 cls : 'roo-upload-cropbox-selector',
29886 cls : 'roo-upload-cropbox-body',
29887 style : 'cursor:pointer',
29891 cls : 'roo-upload-cropbox-preview'
29895 cls : 'roo-upload-cropbox-thumb'
29899 cls : 'roo-upload-cropbox-empty-notify',
29900 html : this.emptyText
29904 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29905 html : this.rotateNotify
29911 cls : 'roo-upload-cropbox-footer',
29914 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29924 onRender : function(ct, position)
29926 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29928 if (this.buttons.length) {
29930 Roo.each(this.buttons, function(bb) {
29932 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29934 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29940 this.maskEl = this.el;
29944 initEvents : function()
29946 this.urlAPI = (window.createObjectURL && window) ||
29947 (window.URL && URL.revokeObjectURL && URL) ||
29948 (window.webkitURL && webkitURL);
29950 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29951 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29953 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29954 this.selectorEl.hide();
29956 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29957 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29959 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29960 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29961 this.thumbEl.hide();
29963 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29964 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29966 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29967 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29968 this.errorEl.hide();
29970 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29971 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29972 this.footerEl.hide();
29974 this.setThumbBoxSize();
29980 this.fireEvent('initial', this);
29987 window.addEventListener("resize", function() { _this.resize(); } );
29989 this.bodyEl.on('click', this.beforeSelectFile, this);
29992 this.bodyEl.on('touchstart', this.onTouchStart, this);
29993 this.bodyEl.on('touchmove', this.onTouchMove, this);
29994 this.bodyEl.on('touchend', this.onTouchEnd, this);
29998 this.bodyEl.on('mousedown', this.onMouseDown, this);
29999 this.bodyEl.on('mousemove', this.onMouseMove, this);
30000 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30001 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30002 Roo.get(document).on('mouseup', this.onMouseUp, this);
30005 this.selectorEl.on('change', this.onFileSelected, this);
30011 this.baseScale = 1;
30013 this.baseRotate = 1;
30014 this.dragable = false;
30015 this.pinching = false;
30018 this.cropData = false;
30019 this.notifyEl.dom.innerHTML = this.emptyText;
30021 this.selectorEl.dom.value = '';
30025 resize : function()
30027 if(this.fireEvent('resize', this) != false){
30028 this.setThumbBoxPosition();
30029 this.setCanvasPosition();
30033 onFooterButtonClick : function(e, el, o, type)
30036 case 'rotate-left' :
30037 this.onRotateLeft(e);
30039 case 'rotate-right' :
30040 this.onRotateRight(e);
30043 this.beforeSelectFile(e);
30058 this.fireEvent('footerbuttonclick', this, type);
30061 beforeSelectFile : function(e)
30063 e.preventDefault();
30065 if(this.fireEvent('beforeselectfile', this) != false){
30066 this.selectorEl.dom.click();
30070 onFileSelected : function(e)
30072 e.preventDefault();
30074 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30078 var file = this.selectorEl.dom.files[0];
30080 if(this.fireEvent('inspect', this, file) != false){
30081 this.prepare(file);
30086 trash : function(e)
30088 this.fireEvent('trash', this);
30091 download : function(e)
30093 this.fireEvent('download', this);
30096 loadCanvas : function(src)
30098 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30102 this.imageEl = document.createElement('img');
30106 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30108 this.imageEl.src = src;
30112 onLoadCanvas : function()
30114 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30115 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30117 this.bodyEl.un('click', this.beforeSelectFile, this);
30119 this.notifyEl.hide();
30120 this.thumbEl.show();
30121 this.footerEl.show();
30123 this.baseRotateLevel();
30125 if(this.isDocument){
30126 this.setThumbBoxSize();
30129 this.setThumbBoxPosition();
30131 this.baseScaleLevel();
30137 this.canvasLoaded = true;
30140 this.maskEl.unmask();
30145 setCanvasPosition : function()
30147 if(!this.canvasEl){
30151 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30152 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30154 this.previewEl.setLeft(pw);
30155 this.previewEl.setTop(ph);
30159 onMouseDown : function(e)
30163 this.dragable = true;
30164 this.pinching = false;
30166 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30167 this.dragable = false;
30171 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30172 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30176 onMouseMove : function(e)
30180 if(!this.canvasLoaded){
30184 if (!this.dragable){
30188 var minX = Math.ceil(this.thumbEl.getLeft(true));
30189 var minY = Math.ceil(this.thumbEl.getTop(true));
30191 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30192 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30194 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30195 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30197 x = x - this.mouseX;
30198 y = y - this.mouseY;
30200 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30201 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30203 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30204 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30206 this.previewEl.setLeft(bgX);
30207 this.previewEl.setTop(bgY);
30209 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30210 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30213 onMouseUp : function(e)
30217 this.dragable = false;
30220 onMouseWheel : function(e)
30224 this.startScale = this.scale;
30226 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30228 if(!this.zoomable()){
30229 this.scale = this.startScale;
30238 zoomable : function()
30240 var minScale = this.thumbEl.getWidth() / this.minWidth;
30242 if(this.minWidth < this.minHeight){
30243 minScale = this.thumbEl.getHeight() / this.minHeight;
30246 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30247 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30251 (this.rotate == 0 || this.rotate == 180) &&
30253 width > this.imageEl.OriginWidth ||
30254 height > this.imageEl.OriginHeight ||
30255 (width < this.minWidth && height < this.minHeight)
30263 (this.rotate == 90 || this.rotate == 270) &&
30265 width > this.imageEl.OriginWidth ||
30266 height > this.imageEl.OriginHeight ||
30267 (width < this.minHeight && height < this.minWidth)
30274 !this.isDocument &&
30275 (this.rotate == 0 || this.rotate == 180) &&
30277 width < this.minWidth ||
30278 width > this.imageEl.OriginWidth ||
30279 height < this.minHeight ||
30280 height > this.imageEl.OriginHeight
30287 !this.isDocument &&
30288 (this.rotate == 90 || this.rotate == 270) &&
30290 width < this.minHeight ||
30291 width > this.imageEl.OriginWidth ||
30292 height < this.minWidth ||
30293 height > this.imageEl.OriginHeight
30303 onRotateLeft : function(e)
30305 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30307 var minScale = this.thumbEl.getWidth() / this.minWidth;
30309 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30310 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30312 this.startScale = this.scale;
30314 while (this.getScaleLevel() < minScale){
30316 this.scale = this.scale + 1;
30318 if(!this.zoomable()){
30323 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30324 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30329 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30336 this.scale = this.startScale;
30338 this.onRotateFail();
30343 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30345 if(this.isDocument){
30346 this.setThumbBoxSize();
30347 this.setThumbBoxPosition();
30348 this.setCanvasPosition();
30353 this.fireEvent('rotate', this, 'left');
30357 onRotateRight : function(e)
30359 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30361 var minScale = this.thumbEl.getWidth() / this.minWidth;
30363 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30364 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30366 this.startScale = this.scale;
30368 while (this.getScaleLevel() < minScale){
30370 this.scale = this.scale + 1;
30372 if(!this.zoomable()){
30377 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30378 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30383 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30390 this.scale = this.startScale;
30392 this.onRotateFail();
30397 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30399 if(this.isDocument){
30400 this.setThumbBoxSize();
30401 this.setThumbBoxPosition();
30402 this.setCanvasPosition();
30407 this.fireEvent('rotate', this, 'right');
30410 onRotateFail : function()
30412 this.errorEl.show(true);
30416 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30421 this.previewEl.dom.innerHTML = '';
30423 var canvasEl = document.createElement("canvas");
30425 var contextEl = canvasEl.getContext("2d");
30427 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30428 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30429 var center = this.imageEl.OriginWidth / 2;
30431 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30432 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30433 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30434 center = this.imageEl.OriginHeight / 2;
30437 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30439 contextEl.translate(center, center);
30440 contextEl.rotate(this.rotate * Math.PI / 180);
30442 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30444 this.canvasEl = document.createElement("canvas");
30446 this.contextEl = this.canvasEl.getContext("2d");
30448 switch (this.rotate) {
30451 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30452 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30454 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30459 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30460 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30462 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30463 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);
30467 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30472 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30473 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30475 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30476 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);
30480 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);
30485 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30486 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30488 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30489 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30493 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);
30500 this.previewEl.appendChild(this.canvasEl);
30502 this.setCanvasPosition();
30507 if(!this.canvasLoaded){
30511 var imageCanvas = document.createElement("canvas");
30513 var imageContext = imageCanvas.getContext("2d");
30515 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30516 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30518 var center = imageCanvas.width / 2;
30520 imageContext.translate(center, center);
30522 imageContext.rotate(this.rotate * Math.PI / 180);
30524 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30526 var canvas = document.createElement("canvas");
30528 var context = canvas.getContext("2d");
30530 canvas.width = this.minWidth;
30531 canvas.height = this.minHeight;
30533 switch (this.rotate) {
30536 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30537 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30539 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30540 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30542 var targetWidth = this.minWidth - 2 * x;
30543 var targetHeight = this.minHeight - 2 * y;
30547 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30548 scale = targetWidth / width;
30551 if(x > 0 && y == 0){
30552 scale = targetHeight / height;
30555 if(x > 0 && y > 0){
30556 scale = targetWidth / width;
30558 if(width < height){
30559 scale = targetHeight / height;
30563 context.scale(scale, scale);
30565 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30566 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30568 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30569 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30571 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30576 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30577 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30579 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30580 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30582 var targetWidth = this.minWidth - 2 * x;
30583 var targetHeight = this.minHeight - 2 * y;
30587 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30588 scale = targetWidth / width;
30591 if(x > 0 && y == 0){
30592 scale = targetHeight / height;
30595 if(x > 0 && y > 0){
30596 scale = targetWidth / width;
30598 if(width < height){
30599 scale = targetHeight / height;
30603 context.scale(scale, scale);
30605 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30606 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30608 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30609 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30611 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30613 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30618 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30619 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30621 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30622 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30624 var targetWidth = this.minWidth - 2 * x;
30625 var targetHeight = this.minHeight - 2 * y;
30629 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30630 scale = targetWidth / width;
30633 if(x > 0 && y == 0){
30634 scale = targetHeight / height;
30637 if(x > 0 && y > 0){
30638 scale = targetWidth / width;
30640 if(width < height){
30641 scale = targetHeight / height;
30645 context.scale(scale, scale);
30647 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30648 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30650 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30651 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30653 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30654 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30656 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30661 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30662 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30664 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30665 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30667 var targetWidth = this.minWidth - 2 * x;
30668 var targetHeight = this.minHeight - 2 * y;
30672 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30673 scale = targetWidth / width;
30676 if(x > 0 && y == 0){
30677 scale = targetHeight / height;
30680 if(x > 0 && y > 0){
30681 scale = targetWidth / width;
30683 if(width < height){
30684 scale = targetHeight / height;
30688 context.scale(scale, scale);
30690 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30691 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30693 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30694 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30696 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30698 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30705 this.cropData = canvas.toDataURL(this.cropType);
30707 if(this.fireEvent('crop', this, this.cropData) !== false){
30708 this.process(this.file, this.cropData);
30715 setThumbBoxSize : function()
30719 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30720 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30721 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30723 this.minWidth = width;
30724 this.minHeight = height;
30726 if(this.rotate == 90 || this.rotate == 270){
30727 this.minWidth = height;
30728 this.minHeight = width;
30733 width = Math.ceil(this.minWidth * height / this.minHeight);
30735 if(this.minWidth > this.minHeight){
30737 height = Math.ceil(this.minHeight * width / this.minWidth);
30740 this.thumbEl.setStyle({
30741 width : width + 'px',
30742 height : height + 'px'
30749 setThumbBoxPosition : function()
30751 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30752 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30754 this.thumbEl.setLeft(x);
30755 this.thumbEl.setTop(y);
30759 baseRotateLevel : function()
30761 this.baseRotate = 1;
30764 typeof(this.exif) != 'undefined' &&
30765 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30766 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30768 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30771 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30775 baseScaleLevel : function()
30779 if(this.isDocument){
30781 if(this.baseRotate == 6 || this.baseRotate == 8){
30783 height = this.thumbEl.getHeight();
30784 this.baseScale = height / this.imageEl.OriginWidth;
30786 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30787 width = this.thumbEl.getWidth();
30788 this.baseScale = width / this.imageEl.OriginHeight;
30794 height = this.thumbEl.getHeight();
30795 this.baseScale = height / this.imageEl.OriginHeight;
30797 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30798 width = this.thumbEl.getWidth();
30799 this.baseScale = width / this.imageEl.OriginWidth;
30805 if(this.baseRotate == 6 || this.baseRotate == 8){
30807 width = this.thumbEl.getHeight();
30808 this.baseScale = width / this.imageEl.OriginHeight;
30810 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30811 height = this.thumbEl.getWidth();
30812 this.baseScale = height / this.imageEl.OriginHeight;
30815 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30816 height = this.thumbEl.getWidth();
30817 this.baseScale = height / this.imageEl.OriginHeight;
30819 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30820 width = this.thumbEl.getHeight();
30821 this.baseScale = width / this.imageEl.OriginWidth;
30828 width = this.thumbEl.getWidth();
30829 this.baseScale = width / this.imageEl.OriginWidth;
30831 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30832 height = this.thumbEl.getHeight();
30833 this.baseScale = height / this.imageEl.OriginHeight;
30836 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30838 height = this.thumbEl.getHeight();
30839 this.baseScale = height / this.imageEl.OriginHeight;
30841 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30842 width = this.thumbEl.getWidth();
30843 this.baseScale = width / this.imageEl.OriginWidth;
30851 getScaleLevel : function()
30853 return this.baseScale * Math.pow(1.1, this.scale);
30856 onTouchStart : function(e)
30858 if(!this.canvasLoaded){
30859 this.beforeSelectFile(e);
30863 var touches = e.browserEvent.touches;
30869 if(touches.length == 1){
30870 this.onMouseDown(e);
30874 if(touches.length != 2){
30880 for(var i = 0, finger; finger = touches[i]; i++){
30881 coords.push(finger.pageX, finger.pageY);
30884 var x = Math.pow(coords[0] - coords[2], 2);
30885 var y = Math.pow(coords[1] - coords[3], 2);
30887 this.startDistance = Math.sqrt(x + y);
30889 this.startScale = this.scale;
30891 this.pinching = true;
30892 this.dragable = false;
30896 onTouchMove : function(e)
30898 if(!this.pinching && !this.dragable){
30902 var touches = e.browserEvent.touches;
30909 this.onMouseMove(e);
30915 for(var i = 0, finger; finger = touches[i]; i++){
30916 coords.push(finger.pageX, finger.pageY);
30919 var x = Math.pow(coords[0] - coords[2], 2);
30920 var y = Math.pow(coords[1] - coords[3], 2);
30922 this.endDistance = Math.sqrt(x + y);
30924 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30926 if(!this.zoomable()){
30927 this.scale = this.startScale;
30935 onTouchEnd : function(e)
30937 this.pinching = false;
30938 this.dragable = false;
30942 process : function(file, crop)
30945 this.maskEl.mask(this.loadingText);
30948 this.xhr = new XMLHttpRequest();
30950 file.xhr = this.xhr;
30952 this.xhr.open(this.method, this.url, true);
30955 "Accept": "application/json",
30956 "Cache-Control": "no-cache",
30957 "X-Requested-With": "XMLHttpRequest"
30960 for (var headerName in headers) {
30961 var headerValue = headers[headerName];
30963 this.xhr.setRequestHeader(headerName, headerValue);
30969 this.xhr.onload = function()
30971 _this.xhrOnLoad(_this.xhr);
30974 this.xhr.onerror = function()
30976 _this.xhrOnError(_this.xhr);
30979 var formData = new FormData();
30981 formData.append('returnHTML', 'NO');
30984 formData.append('crop', crop);
30987 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30988 formData.append(this.paramName, file, file.name);
30991 if(typeof(file.filename) != 'undefined'){
30992 formData.append('filename', file.filename);
30995 if(typeof(file.mimetype) != 'undefined'){
30996 formData.append('mimetype', file.mimetype);
30999 if(this.fireEvent('arrange', this, formData) != false){
31000 this.xhr.send(formData);
31004 xhrOnLoad : function(xhr)
31007 this.maskEl.unmask();
31010 if (xhr.readyState !== 4) {
31011 this.fireEvent('exception', this, xhr);
31015 var response = Roo.decode(xhr.responseText);
31017 if(!response.success){
31018 this.fireEvent('exception', this, xhr);
31022 var response = Roo.decode(xhr.responseText);
31024 this.fireEvent('upload', this, response);
31028 xhrOnError : function()
31031 this.maskEl.unmask();
31034 Roo.log('xhr on error');
31036 var response = Roo.decode(xhr.responseText);
31042 prepare : function(file)
31045 this.maskEl.mask(this.loadingText);
31051 if(typeof(file) === 'string'){
31052 this.loadCanvas(file);
31056 if(!file || !this.urlAPI){
31061 this.cropType = file.type;
31065 if(this.fireEvent('prepare', this, this.file) != false){
31067 var reader = new FileReader();
31069 reader.onload = function (e) {
31070 if (e.target.error) {
31071 Roo.log(e.target.error);
31075 var buffer = e.target.result,
31076 dataView = new DataView(buffer),
31078 maxOffset = dataView.byteLength - 4,
31082 if (dataView.getUint16(0) === 0xffd8) {
31083 while (offset < maxOffset) {
31084 markerBytes = dataView.getUint16(offset);
31086 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31087 markerLength = dataView.getUint16(offset + 2) + 2;
31088 if (offset + markerLength > dataView.byteLength) {
31089 Roo.log('Invalid meta data: Invalid segment size.');
31093 if(markerBytes == 0xffe1){
31094 _this.parseExifData(
31101 offset += markerLength;
31111 var url = _this.urlAPI.createObjectURL(_this.file);
31113 _this.loadCanvas(url);
31118 reader.readAsArrayBuffer(this.file);
31124 parseExifData : function(dataView, offset, length)
31126 var tiffOffset = offset + 10,
31130 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31131 // No Exif data, might be XMP data instead
31135 // Check for the ASCII code for "Exif" (0x45786966):
31136 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31137 // No Exif data, might be XMP data instead
31140 if (tiffOffset + 8 > dataView.byteLength) {
31141 Roo.log('Invalid Exif data: Invalid segment size.');
31144 // Check for the two null bytes:
31145 if (dataView.getUint16(offset + 8) !== 0x0000) {
31146 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31149 // Check the byte alignment:
31150 switch (dataView.getUint16(tiffOffset)) {
31152 littleEndian = true;
31155 littleEndian = false;
31158 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31161 // Check for the TIFF tag marker (0x002A):
31162 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31163 Roo.log('Invalid Exif data: Missing TIFF marker.');
31166 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31167 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31169 this.parseExifTags(
31172 tiffOffset + dirOffset,
31177 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31182 if (dirOffset + 6 > dataView.byteLength) {
31183 Roo.log('Invalid Exif data: Invalid directory offset.');
31186 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31187 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31188 if (dirEndOffset + 4 > dataView.byteLength) {
31189 Roo.log('Invalid Exif data: Invalid directory size.');
31192 for (i = 0; i < tagsNumber; i += 1) {
31196 dirOffset + 2 + 12 * i, // tag offset
31200 // Return the offset to the next directory:
31201 return dataView.getUint32(dirEndOffset, littleEndian);
31204 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31206 var tag = dataView.getUint16(offset, littleEndian);
31208 this.exif[tag] = this.getExifValue(
31212 dataView.getUint16(offset + 2, littleEndian), // tag type
31213 dataView.getUint32(offset + 4, littleEndian), // tag length
31218 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31220 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31229 Roo.log('Invalid Exif data: Invalid tag type.');
31233 tagSize = tagType.size * length;
31234 // Determine if the value is contained in the dataOffset bytes,
31235 // or if the value at the dataOffset is a pointer to the actual data:
31236 dataOffset = tagSize > 4 ?
31237 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31238 if (dataOffset + tagSize > dataView.byteLength) {
31239 Roo.log('Invalid Exif data: Invalid data offset.');
31242 if (length === 1) {
31243 return tagType.getValue(dataView, dataOffset, littleEndian);
31246 for (i = 0; i < length; i += 1) {
31247 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31250 if (tagType.ascii) {
31252 // Concatenate the chars:
31253 for (i = 0; i < values.length; i += 1) {
31255 // Ignore the terminating NULL byte(s):
31256 if (c === '\u0000') {
31268 Roo.apply(Roo.bootstrap.UploadCropbox, {
31270 'Orientation': 0x0112
31274 1: 0, //'top-left',
31276 3: 180, //'bottom-right',
31277 // 4: 'bottom-left',
31279 6: 90, //'right-top',
31280 // 7: 'right-bottom',
31281 8: 270 //'left-bottom'
31285 // byte, 8-bit unsigned int:
31287 getValue: function (dataView, dataOffset) {
31288 return dataView.getUint8(dataOffset);
31292 // ascii, 8-bit byte:
31294 getValue: function (dataView, dataOffset) {
31295 return String.fromCharCode(dataView.getUint8(dataOffset));
31300 // short, 16 bit int:
31302 getValue: function (dataView, dataOffset, littleEndian) {
31303 return dataView.getUint16(dataOffset, littleEndian);
31307 // long, 32 bit int:
31309 getValue: function (dataView, dataOffset, littleEndian) {
31310 return dataView.getUint32(dataOffset, littleEndian);
31314 // rational = two long values, first is numerator, second is denominator:
31316 getValue: function (dataView, dataOffset, littleEndian) {
31317 return dataView.getUint32(dataOffset, littleEndian) /
31318 dataView.getUint32(dataOffset + 4, littleEndian);
31322 // slong, 32 bit signed int:
31324 getValue: function (dataView, dataOffset, littleEndian) {
31325 return dataView.getInt32(dataOffset, littleEndian);
31329 // srational, two slongs, first is numerator, second is denominator:
31331 getValue: function (dataView, dataOffset, littleEndian) {
31332 return dataView.getInt32(dataOffset, littleEndian) /
31333 dataView.getInt32(dataOffset + 4, littleEndian);
31343 cls : 'btn-group roo-upload-cropbox-rotate-left',
31344 action : 'rotate-left',
31348 cls : 'btn btn-default',
31349 html : '<i class="fa fa-undo"></i>'
31355 cls : 'btn-group roo-upload-cropbox-picture',
31356 action : 'picture',
31360 cls : 'btn btn-default',
31361 html : '<i class="fa fa-picture-o"></i>'
31367 cls : 'btn-group roo-upload-cropbox-rotate-right',
31368 action : 'rotate-right',
31372 cls : 'btn btn-default',
31373 html : '<i class="fa fa-repeat"></i>'
31381 cls : 'btn-group roo-upload-cropbox-rotate-left',
31382 action : 'rotate-left',
31386 cls : 'btn btn-default',
31387 html : '<i class="fa fa-undo"></i>'
31393 cls : 'btn-group roo-upload-cropbox-download',
31394 action : 'download',
31398 cls : 'btn btn-default',
31399 html : '<i class="fa fa-download"></i>'
31405 cls : 'btn-group roo-upload-cropbox-crop',
31410 cls : 'btn btn-default',
31411 html : '<i class="fa fa-crop"></i>'
31417 cls : 'btn-group roo-upload-cropbox-trash',
31422 cls : 'btn btn-default',
31423 html : '<i class="fa fa-trash"></i>'
31429 cls : 'btn-group roo-upload-cropbox-rotate-right',
31430 action : 'rotate-right',
31434 cls : 'btn btn-default',
31435 html : '<i class="fa fa-repeat"></i>'
31443 cls : 'btn-group roo-upload-cropbox-rotate-left',
31444 action : 'rotate-left',
31448 cls : 'btn btn-default',
31449 html : '<i class="fa fa-undo"></i>'
31455 cls : 'btn-group roo-upload-cropbox-rotate-right',
31456 action : 'rotate-right',
31460 cls : 'btn btn-default',
31461 html : '<i class="fa fa-repeat"></i>'
31474 * @class Roo.bootstrap.DocumentManager
31475 * @extends Roo.bootstrap.Component
31476 * Bootstrap DocumentManager class
31477 * @cfg {String} paramName default 'imageUpload'
31478 * @cfg {String} toolTipName default 'filename'
31479 * @cfg {String} method default POST
31480 * @cfg {String} url action url
31481 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31482 * @cfg {Boolean} multiple multiple upload default true
31483 * @cfg {Number} thumbSize default 300
31484 * @cfg {String} fieldLabel
31485 * @cfg {Number} labelWidth default 4
31486 * @cfg {String} labelAlign (left|top) default left
31487 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31488 * @cfg {Number} labellg set the width of label (1-12)
31489 * @cfg {Number} labelmd set the width of label (1-12)
31490 * @cfg {Number} labelsm set the width of label (1-12)
31491 * @cfg {Number} labelxs set the width of label (1-12)
31494 * Create a new DocumentManager
31495 * @param {Object} config The config object
31498 Roo.bootstrap.DocumentManager = function(config){
31499 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31502 this.delegates = [];
31507 * Fire when initial the DocumentManager
31508 * @param {Roo.bootstrap.DocumentManager} this
31513 * inspect selected file
31514 * @param {Roo.bootstrap.DocumentManager} this
31515 * @param {File} file
31520 * Fire when xhr load exception
31521 * @param {Roo.bootstrap.DocumentManager} this
31522 * @param {XMLHttpRequest} xhr
31524 "exception" : true,
31526 * @event afterupload
31527 * Fire when xhr load exception
31528 * @param {Roo.bootstrap.DocumentManager} this
31529 * @param {XMLHttpRequest} xhr
31531 "afterupload" : true,
31534 * prepare the form data
31535 * @param {Roo.bootstrap.DocumentManager} this
31536 * @param {Object} formData
31541 * Fire when remove the file
31542 * @param {Roo.bootstrap.DocumentManager} this
31543 * @param {Object} file
31548 * Fire after refresh the file
31549 * @param {Roo.bootstrap.DocumentManager} this
31554 * Fire after click the image
31555 * @param {Roo.bootstrap.DocumentManager} this
31556 * @param {Object} file
31561 * Fire when upload a image and editable set to true
31562 * @param {Roo.bootstrap.DocumentManager} this
31563 * @param {Object} file
31567 * @event beforeselectfile
31568 * Fire before select file
31569 * @param {Roo.bootstrap.DocumentManager} this
31571 "beforeselectfile" : true,
31574 * Fire before process file
31575 * @param {Roo.bootstrap.DocumentManager} this
31576 * @param {Object} file
31580 * @event previewrendered
31581 * Fire when preview rendered
31582 * @param {Roo.bootstrap.DocumentManager} this
31583 * @param {Object} file
31585 "previewrendered" : true,
31588 "previewResize" : true
31593 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31602 paramName : 'imageUpload',
31603 toolTipName : 'filename',
31606 labelAlign : 'left',
31616 getAutoCreate : function()
31618 var managerWidget = {
31620 cls : 'roo-document-manager',
31624 cls : 'roo-document-manager-selector',
31629 cls : 'roo-document-manager-uploader',
31633 cls : 'roo-document-manager-upload-btn',
31634 html : '<i class="fa fa-plus"></i>'
31645 cls : 'column col-md-12',
31650 if(this.fieldLabel.length){
31655 cls : 'column col-md-12',
31656 html : this.fieldLabel
31660 cls : 'column col-md-12',
31665 if(this.labelAlign == 'left'){
31670 html : this.fieldLabel
31679 if(this.labelWidth > 12){
31680 content[0].style = "width: " + this.labelWidth + 'px';
31683 if(this.labelWidth < 13 && this.labelmd == 0){
31684 this.labelmd = this.labelWidth;
31687 if(this.labellg > 0){
31688 content[0].cls += ' col-lg-' + this.labellg;
31689 content[1].cls += ' col-lg-' + (12 - this.labellg);
31692 if(this.labelmd > 0){
31693 content[0].cls += ' col-md-' + this.labelmd;
31694 content[1].cls += ' col-md-' + (12 - this.labelmd);
31697 if(this.labelsm > 0){
31698 content[0].cls += ' col-sm-' + this.labelsm;
31699 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31702 if(this.labelxs > 0){
31703 content[0].cls += ' col-xs-' + this.labelxs;
31704 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31712 cls : 'row clearfix',
31720 initEvents : function()
31722 this.managerEl = this.el.select('.roo-document-manager', true).first();
31723 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31725 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31726 this.selectorEl.hide();
31729 this.selectorEl.attr('multiple', 'multiple');
31732 this.selectorEl.on('change', this.onFileSelected, this);
31734 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31735 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31737 this.uploader.on('click', this.onUploaderClick, this);
31739 this.renderProgressDialog();
31743 window.addEventListener("resize", function() { _this.refresh(); } );
31745 this.fireEvent('initial', this);
31748 renderProgressDialog : function()
31752 this.progressDialog = new Roo.bootstrap.Modal({
31753 cls : 'roo-document-manager-progress-dialog',
31754 allow_close : false,
31765 btnclick : function() {
31766 _this.uploadCancel();
31772 this.progressDialog.render(Roo.get(document.body));
31774 this.progress = new Roo.bootstrap.Progress({
31775 cls : 'roo-document-manager-progress',
31780 this.progress.render(this.progressDialog.getChildContainer());
31782 this.progressBar = new Roo.bootstrap.ProgressBar({
31783 cls : 'roo-document-manager-progress-bar',
31786 aria_valuemax : 12,
31790 this.progressBar.render(this.progress.getChildContainer());
31793 onUploaderClick : function(e)
31795 e.preventDefault();
31797 if(this.fireEvent('beforeselectfile', this) != false){
31798 this.selectorEl.dom.click();
31803 onFileSelected : function(e)
31805 e.preventDefault();
31807 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31811 Roo.each(this.selectorEl.dom.files, function(file){
31812 if(this.fireEvent('inspect', this, file) != false){
31813 this.files.push(file);
31823 this.selectorEl.dom.value = '';
31825 if(!this.files || !this.files.length){
31829 if(this.boxes > 0 && this.files.length > this.boxes){
31830 this.files = this.files.slice(0, this.boxes);
31833 this.uploader.show();
31835 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31836 this.uploader.hide();
31845 Roo.each(this.files, function(file){
31847 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31848 var f = this.renderPreview(file);
31853 if(file.type.indexOf('image') != -1){
31854 this.delegates.push(
31856 _this.process(file);
31857 }).createDelegate(this)
31865 _this.process(file);
31866 }).createDelegate(this)
31871 this.files = files;
31873 this.delegates = this.delegates.concat(docs);
31875 if(!this.delegates.length){
31880 this.progressBar.aria_valuemax = this.delegates.length;
31887 arrange : function()
31889 if(!this.delegates.length){
31890 this.progressDialog.hide();
31895 var delegate = this.delegates.shift();
31897 this.progressDialog.show();
31899 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31901 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31906 refresh : function()
31908 this.uploader.show();
31910 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31911 this.uploader.hide();
31914 Roo.isTouch ? this.closable(false) : this.closable(true);
31916 this.fireEvent('refresh', this);
31919 onRemove : function(e, el, o)
31921 e.preventDefault();
31923 this.fireEvent('remove', this, o);
31927 remove : function(o)
31931 Roo.each(this.files, function(file){
31932 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31941 this.files = files;
31948 Roo.each(this.files, function(file){
31953 file.target.remove();
31962 onClick : function(e, el, o)
31964 e.preventDefault();
31966 this.fireEvent('click', this, o);
31970 closable : function(closable)
31972 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31974 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31986 xhrOnLoad : function(xhr)
31988 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31992 if (xhr.readyState !== 4) {
31994 this.fireEvent('exception', this, xhr);
31998 var response = Roo.decode(xhr.responseText);
32000 if(!response.success){
32002 this.fireEvent('exception', this, xhr);
32006 var file = this.renderPreview(response.data);
32008 this.files.push(file);
32012 this.fireEvent('afterupload', this, xhr);
32016 xhrOnError : function(xhr)
32018 Roo.log('xhr on error');
32020 var response = Roo.decode(xhr.responseText);
32027 process : function(file)
32029 if(this.fireEvent('process', this, file) !== false){
32030 if(this.editable && file.type.indexOf('image') != -1){
32031 this.fireEvent('edit', this, file);
32035 this.uploadStart(file, false);
32042 uploadStart : function(file, crop)
32044 this.xhr = new XMLHttpRequest();
32046 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32051 file.xhr = this.xhr;
32053 this.managerEl.createChild({
32055 cls : 'roo-document-manager-loading',
32059 tooltip : file.name,
32060 cls : 'roo-document-manager-thumb',
32061 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32067 this.xhr.open(this.method, this.url, true);
32070 "Accept": "application/json",
32071 "Cache-Control": "no-cache",
32072 "X-Requested-With": "XMLHttpRequest"
32075 for (var headerName in headers) {
32076 var headerValue = headers[headerName];
32078 this.xhr.setRequestHeader(headerName, headerValue);
32084 this.xhr.onload = function()
32086 _this.xhrOnLoad(_this.xhr);
32089 this.xhr.onerror = function()
32091 _this.xhrOnError(_this.xhr);
32094 var formData = new FormData();
32096 formData.append('returnHTML', 'NO');
32099 formData.append('crop', crop);
32102 formData.append(this.paramName, file, file.name);
32109 if(this.fireEvent('prepare', this, formData, options) != false){
32111 if(options.manually){
32115 this.xhr.send(formData);
32119 this.uploadCancel();
32122 uploadCancel : function()
32128 this.delegates = [];
32130 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32137 renderPreview : function(file)
32139 if(typeof(file.target) != 'undefined' && file.target){
32143 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32145 var previewEl = this.managerEl.createChild({
32147 cls : 'roo-document-manager-preview',
32151 tooltip : file[this.toolTipName],
32152 cls : 'roo-document-manager-thumb',
32153 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32158 html : '<i class="fa fa-times-circle"></i>'
32163 var close = previewEl.select('button.close', true).first();
32165 close.on('click', this.onRemove, this, file);
32167 file.target = previewEl;
32169 var image = previewEl.select('img', true).first();
32173 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32175 image.on('click', this.onClick, this, file);
32177 this.fireEvent('previewrendered', this, file);
32183 onPreviewLoad : function(file, image)
32185 if(typeof(file.target) == 'undefined' || !file.target){
32189 var width = image.dom.naturalWidth || image.dom.width;
32190 var height = image.dom.naturalHeight || image.dom.height;
32192 if(!this.previewResize) {
32196 if(width > height){
32197 file.target.addClass('wide');
32201 file.target.addClass('tall');
32206 uploadFromSource : function(file, crop)
32208 this.xhr = new XMLHttpRequest();
32210 this.managerEl.createChild({
32212 cls : 'roo-document-manager-loading',
32216 tooltip : file.name,
32217 cls : 'roo-document-manager-thumb',
32218 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32224 this.xhr.open(this.method, this.url, true);
32227 "Accept": "application/json",
32228 "Cache-Control": "no-cache",
32229 "X-Requested-With": "XMLHttpRequest"
32232 for (var headerName in headers) {
32233 var headerValue = headers[headerName];
32235 this.xhr.setRequestHeader(headerName, headerValue);
32241 this.xhr.onload = function()
32243 _this.xhrOnLoad(_this.xhr);
32246 this.xhr.onerror = function()
32248 _this.xhrOnError(_this.xhr);
32251 var formData = new FormData();
32253 formData.append('returnHTML', 'NO');
32255 formData.append('crop', crop);
32257 if(typeof(file.filename) != 'undefined'){
32258 formData.append('filename', file.filename);
32261 if(typeof(file.mimetype) != 'undefined'){
32262 formData.append('mimetype', file.mimetype);
32267 if(this.fireEvent('prepare', this, formData) != false){
32268 this.xhr.send(formData);
32278 * @class Roo.bootstrap.DocumentViewer
32279 * @extends Roo.bootstrap.Component
32280 * Bootstrap DocumentViewer class
32281 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32282 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32285 * Create a new DocumentViewer
32286 * @param {Object} config The config object
32289 Roo.bootstrap.DocumentViewer = function(config){
32290 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32295 * Fire after initEvent
32296 * @param {Roo.bootstrap.DocumentViewer} this
32302 * @param {Roo.bootstrap.DocumentViewer} this
32307 * Fire after download button
32308 * @param {Roo.bootstrap.DocumentViewer} this
32313 * Fire after trash button
32314 * @param {Roo.bootstrap.DocumentViewer} this
32321 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32323 showDownload : true,
32327 getAutoCreate : function()
32331 cls : 'roo-document-viewer',
32335 cls : 'roo-document-viewer-body',
32339 cls : 'roo-document-viewer-thumb',
32343 cls : 'roo-document-viewer-image'
32351 cls : 'roo-document-viewer-footer',
32354 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32358 cls : 'btn-group roo-document-viewer-download',
32362 cls : 'btn btn-default',
32363 html : '<i class="fa fa-download"></i>'
32369 cls : 'btn-group roo-document-viewer-trash',
32373 cls : 'btn btn-default',
32374 html : '<i class="fa fa-trash"></i>'
32387 initEvents : function()
32389 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32390 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32392 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32393 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32395 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32396 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32398 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32399 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32401 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32402 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32404 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32405 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32407 this.bodyEl.on('click', this.onClick, this);
32408 this.downloadBtn.on('click', this.onDownload, this);
32409 this.trashBtn.on('click', this.onTrash, this);
32411 this.downloadBtn.hide();
32412 this.trashBtn.hide();
32414 if(this.showDownload){
32415 this.downloadBtn.show();
32418 if(this.showTrash){
32419 this.trashBtn.show();
32422 if(!this.showDownload && !this.showTrash) {
32423 this.footerEl.hide();
32428 initial : function()
32430 this.fireEvent('initial', this);
32434 onClick : function(e)
32436 e.preventDefault();
32438 this.fireEvent('click', this);
32441 onDownload : function(e)
32443 e.preventDefault();
32445 this.fireEvent('download', this);
32448 onTrash : function(e)
32450 e.preventDefault();
32452 this.fireEvent('trash', this);
32464 * @class Roo.bootstrap.NavProgressBar
32465 * @extends Roo.bootstrap.Component
32466 * Bootstrap NavProgressBar class
32469 * Create a new nav progress bar
32470 * @param {Object} config The config object
32473 Roo.bootstrap.NavProgressBar = function(config){
32474 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32476 this.bullets = this.bullets || [];
32478 // Roo.bootstrap.NavProgressBar.register(this);
32482 * Fires when the active item changes
32483 * @param {Roo.bootstrap.NavProgressBar} this
32484 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32485 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32492 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32497 getAutoCreate : function()
32499 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32503 cls : 'roo-navigation-bar-group',
32507 cls : 'roo-navigation-top-bar'
32511 cls : 'roo-navigation-bullets-bar',
32515 cls : 'roo-navigation-bar'
32522 cls : 'roo-navigation-bottom-bar'
32532 initEvents: function()
32537 onRender : function(ct, position)
32539 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32541 if(this.bullets.length){
32542 Roo.each(this.bullets, function(b){
32551 addItem : function(cfg)
32553 var item = new Roo.bootstrap.NavProgressItem(cfg);
32555 item.parentId = this.id;
32556 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32559 var top = new Roo.bootstrap.Element({
32561 cls : 'roo-navigation-bar-text'
32564 var bottom = new Roo.bootstrap.Element({
32566 cls : 'roo-navigation-bar-text'
32569 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32570 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32572 var topText = new Roo.bootstrap.Element({
32574 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32577 var bottomText = new Roo.bootstrap.Element({
32579 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32582 topText.onRender(top.el, null);
32583 bottomText.onRender(bottom.el, null);
32586 item.bottomEl = bottom;
32589 this.barItems.push(item);
32594 getActive : function()
32596 var active = false;
32598 Roo.each(this.barItems, function(v){
32600 if (!v.isActive()) {
32612 setActiveItem : function(item)
32616 Roo.each(this.barItems, function(v){
32617 if (v.rid == item.rid) {
32621 if (v.isActive()) {
32622 v.setActive(false);
32627 item.setActive(true);
32629 this.fireEvent('changed', this, item, prev);
32632 getBarItem: function(rid)
32636 Roo.each(this.barItems, function(e) {
32637 if (e.rid != rid) {
32648 indexOfItem : function(item)
32652 Roo.each(this.barItems, function(v, i){
32654 if (v.rid != item.rid) {
32665 setActiveNext : function()
32667 var i = this.indexOfItem(this.getActive());
32669 if (i > this.barItems.length) {
32673 this.setActiveItem(this.barItems[i+1]);
32676 setActivePrev : function()
32678 var i = this.indexOfItem(this.getActive());
32684 this.setActiveItem(this.barItems[i-1]);
32687 format : function()
32689 if(!this.barItems.length){
32693 var width = 100 / this.barItems.length;
32695 Roo.each(this.barItems, function(i){
32696 i.el.setStyle('width', width + '%');
32697 i.topEl.el.setStyle('width', width + '%');
32698 i.bottomEl.el.setStyle('width', width + '%');
32707 * Nav Progress Item
32712 * @class Roo.bootstrap.NavProgressItem
32713 * @extends Roo.bootstrap.Component
32714 * Bootstrap NavProgressItem class
32715 * @cfg {String} rid the reference id
32716 * @cfg {Boolean} active (true|false) Is item active default false
32717 * @cfg {Boolean} disabled (true|false) Is item active default false
32718 * @cfg {String} html
32719 * @cfg {String} position (top|bottom) text position default bottom
32720 * @cfg {String} icon show icon instead of number
32723 * Create a new NavProgressItem
32724 * @param {Object} config The config object
32726 Roo.bootstrap.NavProgressItem = function(config){
32727 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32732 * The raw click event for the entire grid.
32733 * @param {Roo.bootstrap.NavProgressItem} this
32734 * @param {Roo.EventObject} e
32741 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32747 position : 'bottom',
32750 getAutoCreate : function()
32752 var iconCls = 'roo-navigation-bar-item-icon';
32754 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32758 cls: 'roo-navigation-bar-item',
32768 cfg.cls += ' active';
32771 cfg.cls += ' disabled';
32777 disable : function()
32779 this.setDisabled(true);
32782 enable : function()
32784 this.setDisabled(false);
32787 initEvents: function()
32789 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32791 this.iconEl.on('click', this.onClick, this);
32794 onClick : function(e)
32796 e.preventDefault();
32802 if(this.fireEvent('click', this, e) === false){
32806 this.parent().setActiveItem(this);
32809 isActive: function ()
32811 return this.active;
32814 setActive : function(state)
32816 if(this.active == state){
32820 this.active = state;
32823 this.el.addClass('active');
32827 this.el.removeClass('active');
32832 setDisabled : function(state)
32834 if(this.disabled == state){
32838 this.disabled = state;
32841 this.el.addClass('disabled');
32845 this.el.removeClass('disabled');
32848 tooltipEl : function()
32850 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32863 * @class Roo.bootstrap.FieldLabel
32864 * @extends Roo.bootstrap.Component
32865 * Bootstrap FieldLabel class
32866 * @cfg {String} html contents of the element
32867 * @cfg {String} tag tag of the element default label
32868 * @cfg {String} cls class of the element
32869 * @cfg {String} target label target
32870 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32871 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32872 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32873 * @cfg {String} iconTooltip default "This field is required"
32874 * @cfg {String} indicatorpos (left|right) default left
32877 * Create a new FieldLabel
32878 * @param {Object} config The config object
32881 Roo.bootstrap.FieldLabel = function(config){
32882 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32887 * Fires after the field has been marked as invalid.
32888 * @param {Roo.form.FieldLabel} this
32889 * @param {String} msg The validation message
32894 * Fires after the field has been validated with no errors.
32895 * @param {Roo.form.FieldLabel} this
32901 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32908 invalidClass : 'has-warning',
32909 validClass : 'has-success',
32910 iconTooltip : 'This field is required',
32911 indicatorpos : 'left',
32913 getAutoCreate : function(){
32916 if (!this.allowBlank) {
32922 cls : 'roo-bootstrap-field-label ' + this.cls,
32927 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32928 tooltip : this.iconTooltip
32937 if(this.indicatorpos == 'right'){
32940 cls : 'roo-bootstrap-field-label ' + this.cls,
32949 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32950 tooltip : this.iconTooltip
32959 initEvents: function()
32961 Roo.bootstrap.Element.superclass.initEvents.call(this);
32963 this.indicator = this.indicatorEl();
32965 if(this.indicator){
32966 this.indicator.removeClass('visible');
32967 this.indicator.addClass('invisible');
32970 Roo.bootstrap.FieldLabel.register(this);
32973 indicatorEl : function()
32975 var indicator = this.el.select('i.roo-required-indicator',true).first();
32986 * Mark this field as valid
32988 markValid : function()
32990 if(this.indicator){
32991 this.indicator.removeClass('visible');
32992 this.indicator.addClass('invisible');
32994 if (Roo.bootstrap.version == 3) {
32995 this.el.removeClass(this.invalidClass);
32996 this.el.addClass(this.validClass);
32998 this.el.removeClass('is-invalid');
32999 this.el.addClass('is-valid');
33003 this.fireEvent('valid', this);
33007 * Mark this field as invalid
33008 * @param {String} msg The validation message
33010 markInvalid : function(msg)
33012 if(this.indicator){
33013 this.indicator.removeClass('invisible');
33014 this.indicator.addClass('visible');
33016 if (Roo.bootstrap.version == 3) {
33017 this.el.removeClass(this.validClass);
33018 this.el.addClass(this.invalidClass);
33020 this.el.removeClass('is-valid');
33021 this.el.addClass('is-invalid');
33025 this.fireEvent('invalid', this, msg);
33031 Roo.apply(Roo.bootstrap.FieldLabel, {
33036 * register a FieldLabel Group
33037 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33039 register : function(label)
33041 if(this.groups.hasOwnProperty(label.target)){
33045 this.groups[label.target] = label;
33049 * fetch a FieldLabel Group based on the target
33050 * @param {string} target
33051 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33053 get: function(target) {
33054 if (typeof(this.groups[target]) == 'undefined') {
33058 return this.groups[target] ;
33067 * page DateSplitField.
33073 * @class Roo.bootstrap.DateSplitField
33074 * @extends Roo.bootstrap.Component
33075 * Bootstrap DateSplitField class
33076 * @cfg {string} fieldLabel - the label associated
33077 * @cfg {Number} labelWidth set the width of label (0-12)
33078 * @cfg {String} labelAlign (top|left)
33079 * @cfg {Boolean} dayAllowBlank (true|false) default false
33080 * @cfg {Boolean} monthAllowBlank (true|false) default false
33081 * @cfg {Boolean} yearAllowBlank (true|false) default false
33082 * @cfg {string} dayPlaceholder
33083 * @cfg {string} monthPlaceholder
33084 * @cfg {string} yearPlaceholder
33085 * @cfg {string} dayFormat default 'd'
33086 * @cfg {string} monthFormat default 'm'
33087 * @cfg {string} yearFormat default 'Y'
33088 * @cfg {Number} labellg set the width of label (1-12)
33089 * @cfg {Number} labelmd set the width of label (1-12)
33090 * @cfg {Number} labelsm set the width of label (1-12)
33091 * @cfg {Number} labelxs set the width of label (1-12)
33095 * Create a new DateSplitField
33096 * @param {Object} config The config object
33099 Roo.bootstrap.DateSplitField = function(config){
33100 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33106 * getting the data of years
33107 * @param {Roo.bootstrap.DateSplitField} this
33108 * @param {Object} years
33113 * getting the data of days
33114 * @param {Roo.bootstrap.DateSplitField} this
33115 * @param {Object} days
33120 * Fires after the field has been marked as invalid.
33121 * @param {Roo.form.Field} this
33122 * @param {String} msg The validation message
33127 * Fires after the field has been validated with no errors.
33128 * @param {Roo.form.Field} this
33134 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33137 labelAlign : 'top',
33139 dayAllowBlank : false,
33140 monthAllowBlank : false,
33141 yearAllowBlank : false,
33142 dayPlaceholder : '',
33143 monthPlaceholder : '',
33144 yearPlaceholder : '',
33148 isFormField : true,
33154 getAutoCreate : function()
33158 cls : 'row roo-date-split-field-group',
33163 cls : 'form-hidden-field roo-date-split-field-group-value',
33169 var labelCls = 'col-md-12';
33170 var contentCls = 'col-md-4';
33172 if(this.fieldLabel){
33176 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33180 html : this.fieldLabel
33185 if(this.labelAlign == 'left'){
33187 if(this.labelWidth > 12){
33188 label.style = "width: " + this.labelWidth + 'px';
33191 if(this.labelWidth < 13 && this.labelmd == 0){
33192 this.labelmd = this.labelWidth;
33195 if(this.labellg > 0){
33196 labelCls = ' col-lg-' + this.labellg;
33197 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33200 if(this.labelmd > 0){
33201 labelCls = ' col-md-' + this.labelmd;
33202 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33205 if(this.labelsm > 0){
33206 labelCls = ' col-sm-' + this.labelsm;
33207 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33210 if(this.labelxs > 0){
33211 labelCls = ' col-xs-' + this.labelxs;
33212 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33216 label.cls += ' ' + labelCls;
33218 cfg.cn.push(label);
33221 Roo.each(['day', 'month', 'year'], function(t){
33224 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33231 inputEl: function ()
33233 return this.el.select('.roo-date-split-field-group-value', true).first();
33236 onRender : function(ct, position)
33240 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33242 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33244 this.dayField = new Roo.bootstrap.ComboBox({
33245 allowBlank : this.dayAllowBlank,
33246 alwaysQuery : true,
33247 displayField : 'value',
33250 forceSelection : true,
33252 placeholder : this.dayPlaceholder,
33253 selectOnFocus : true,
33254 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33255 triggerAction : 'all',
33257 valueField : 'value',
33258 store : new Roo.data.SimpleStore({
33259 data : (function() {
33261 _this.fireEvent('days', _this, days);
33264 fields : [ 'value' ]
33267 select : function (_self, record, index)
33269 _this.setValue(_this.getValue());
33274 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33276 this.monthField = new Roo.bootstrap.MonthField({
33277 after : '<i class=\"fa fa-calendar\"></i>',
33278 allowBlank : this.monthAllowBlank,
33279 placeholder : this.monthPlaceholder,
33282 render : function (_self)
33284 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33285 e.preventDefault();
33289 select : function (_self, oldvalue, newvalue)
33291 _this.setValue(_this.getValue());
33296 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33298 this.yearField = new Roo.bootstrap.ComboBox({
33299 allowBlank : this.yearAllowBlank,
33300 alwaysQuery : true,
33301 displayField : 'value',
33304 forceSelection : true,
33306 placeholder : this.yearPlaceholder,
33307 selectOnFocus : true,
33308 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33309 triggerAction : 'all',
33311 valueField : 'value',
33312 store : new Roo.data.SimpleStore({
33313 data : (function() {
33315 _this.fireEvent('years', _this, years);
33318 fields : [ 'value' ]
33321 select : function (_self, record, index)
33323 _this.setValue(_this.getValue());
33328 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33331 setValue : function(v, format)
33333 this.inputEl.dom.value = v;
33335 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33337 var d = Date.parseDate(v, f);
33344 this.setDay(d.format(this.dayFormat));
33345 this.setMonth(d.format(this.monthFormat));
33346 this.setYear(d.format(this.yearFormat));
33353 setDay : function(v)
33355 this.dayField.setValue(v);
33356 this.inputEl.dom.value = this.getValue();
33361 setMonth : function(v)
33363 this.monthField.setValue(v, true);
33364 this.inputEl.dom.value = this.getValue();
33369 setYear : function(v)
33371 this.yearField.setValue(v);
33372 this.inputEl.dom.value = this.getValue();
33377 getDay : function()
33379 return this.dayField.getValue();
33382 getMonth : function()
33384 return this.monthField.getValue();
33387 getYear : function()
33389 return this.yearField.getValue();
33392 getValue : function()
33394 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33396 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33406 this.inputEl.dom.value = '';
33411 validate : function()
33413 var d = this.dayField.validate();
33414 var m = this.monthField.validate();
33415 var y = this.yearField.validate();
33420 (!this.dayAllowBlank && !d) ||
33421 (!this.monthAllowBlank && !m) ||
33422 (!this.yearAllowBlank && !y)
33427 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33436 this.markInvalid();
33441 markValid : function()
33444 var label = this.el.select('label', true).first();
33445 var icon = this.el.select('i.fa-star', true).first();
33451 this.fireEvent('valid', this);
33455 * Mark this field as invalid
33456 * @param {String} msg The validation message
33458 markInvalid : function(msg)
33461 var label = this.el.select('label', true).first();
33462 var icon = this.el.select('i.fa-star', true).first();
33464 if(label && !icon){
33465 this.el.select('.roo-date-split-field-label', true).createChild({
33467 cls : 'text-danger fa fa-lg fa-star',
33468 tooltip : 'This field is required',
33469 style : 'margin-right:5px;'
33473 this.fireEvent('invalid', this, msg);
33476 clearInvalid : function()
33478 var label = this.el.select('label', true).first();
33479 var icon = this.el.select('i.fa-star', true).first();
33485 this.fireEvent('valid', this);
33488 getName: function()
33498 * http://masonry.desandro.com
33500 * The idea is to render all the bricks based on vertical width...
33502 * The original code extends 'outlayer' - we might need to use that....
33508 * @class Roo.bootstrap.LayoutMasonry
33509 * @extends Roo.bootstrap.Component
33510 * Bootstrap Layout Masonry class
33513 * Create a new Element
33514 * @param {Object} config The config object
33517 Roo.bootstrap.LayoutMasonry = function(config){
33519 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33523 Roo.bootstrap.LayoutMasonry.register(this);
33529 * Fire after layout the items
33530 * @param {Roo.bootstrap.LayoutMasonry} this
33531 * @param {Roo.EventObject} e
33538 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33541 * @cfg {Boolean} isLayoutInstant = no animation?
33543 isLayoutInstant : false, // needed?
33546 * @cfg {Number} boxWidth width of the columns
33551 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33556 * @cfg {Number} padWidth padding below box..
33561 * @cfg {Number} gutter gutter width..
33566 * @cfg {Number} maxCols maximum number of columns
33572 * @cfg {Boolean} isAutoInitial defalut true
33574 isAutoInitial : true,
33579 * @cfg {Boolean} isHorizontal defalut false
33581 isHorizontal : false,
33583 currentSize : null,
33589 bricks: null, //CompositeElement
33593 _isLayoutInited : false,
33595 // isAlternative : false, // only use for vertical layout...
33598 * @cfg {Number} alternativePadWidth padding below box..
33600 alternativePadWidth : 50,
33602 selectedBrick : [],
33604 getAutoCreate : function(){
33606 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33610 cls: 'blog-masonary-wrapper ' + this.cls,
33612 cls : 'mas-boxes masonary'
33619 getChildContainer: function( )
33621 if (this.boxesEl) {
33622 return this.boxesEl;
33625 this.boxesEl = this.el.select('.mas-boxes').first();
33627 return this.boxesEl;
33631 initEvents : function()
33635 if(this.isAutoInitial){
33636 Roo.log('hook children rendered');
33637 this.on('childrenrendered', function() {
33638 Roo.log('children rendered');
33644 initial : function()
33646 this.selectedBrick = [];
33648 this.currentSize = this.el.getBox(true);
33650 Roo.EventManager.onWindowResize(this.resize, this);
33652 if(!this.isAutoInitial){
33660 //this.layout.defer(500,this);
33664 resize : function()
33666 var cs = this.el.getBox(true);
33669 this.currentSize.width == cs.width &&
33670 this.currentSize.x == cs.x &&
33671 this.currentSize.height == cs.height &&
33672 this.currentSize.y == cs.y
33674 Roo.log("no change in with or X or Y");
33678 this.currentSize = cs;
33684 layout : function()
33686 this._resetLayout();
33688 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33690 this.layoutItems( isInstant );
33692 this._isLayoutInited = true;
33694 this.fireEvent('layout', this);
33698 _resetLayout : function()
33700 if(this.isHorizontal){
33701 this.horizontalMeasureColumns();
33705 this.verticalMeasureColumns();
33709 verticalMeasureColumns : function()
33711 this.getContainerWidth();
33713 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33714 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33718 var boxWidth = this.boxWidth + this.padWidth;
33720 if(this.containerWidth < this.boxWidth){
33721 boxWidth = this.containerWidth
33724 var containerWidth = this.containerWidth;
33726 var cols = Math.floor(containerWidth / boxWidth);
33728 this.cols = Math.max( cols, 1 );
33730 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33732 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33734 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33736 this.colWidth = boxWidth + avail - this.padWidth;
33738 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33739 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33742 horizontalMeasureColumns : function()
33744 this.getContainerWidth();
33746 var boxWidth = this.boxWidth;
33748 if(this.containerWidth < boxWidth){
33749 boxWidth = this.containerWidth;
33752 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33754 this.el.setHeight(boxWidth);
33758 getContainerWidth : function()
33760 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33763 layoutItems : function( isInstant )
33765 Roo.log(this.bricks);
33767 var items = Roo.apply([], this.bricks);
33769 if(this.isHorizontal){
33770 this._horizontalLayoutItems( items , isInstant );
33774 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33775 // this._verticalAlternativeLayoutItems( items , isInstant );
33779 this._verticalLayoutItems( items , isInstant );
33783 _verticalLayoutItems : function ( items , isInstant)
33785 if ( !items || !items.length ) {
33790 ['xs', 'xs', 'xs', 'tall'],
33791 ['xs', 'xs', 'tall'],
33792 ['xs', 'xs', 'sm'],
33793 ['xs', 'xs', 'xs'],
33799 ['sm', 'xs', 'xs'],
33803 ['tall', 'xs', 'xs', 'xs'],
33804 ['tall', 'xs', 'xs'],
33816 Roo.each(items, function(item, k){
33818 switch (item.size) {
33819 // these layouts take up a full box,
33830 boxes.push([item]);
33853 var filterPattern = function(box, length)
33861 var pattern = box.slice(0, length);
33865 Roo.each(pattern, function(i){
33866 format.push(i.size);
33869 Roo.each(standard, function(s){
33871 if(String(s) != String(format)){
33880 if(!match && length == 1){
33885 filterPattern(box, length - 1);
33889 queue.push(pattern);
33891 box = box.slice(length, box.length);
33893 filterPattern(box, 4);
33899 Roo.each(boxes, function(box, k){
33905 if(box.length == 1){
33910 filterPattern(box, 4);
33914 this._processVerticalLayoutQueue( queue, isInstant );
33918 // _verticalAlternativeLayoutItems : function( items , isInstant )
33920 // if ( !items || !items.length ) {
33924 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33928 _horizontalLayoutItems : function ( items , isInstant)
33930 if ( !items || !items.length || items.length < 3) {
33936 var eItems = items.slice(0, 3);
33938 items = items.slice(3, items.length);
33941 ['xs', 'xs', 'xs', 'wide'],
33942 ['xs', 'xs', 'wide'],
33943 ['xs', 'xs', 'sm'],
33944 ['xs', 'xs', 'xs'],
33950 ['sm', 'xs', 'xs'],
33954 ['wide', 'xs', 'xs', 'xs'],
33955 ['wide', 'xs', 'xs'],
33968 Roo.each(items, function(item, k){
33970 switch (item.size) {
33981 boxes.push([item]);
34005 var filterPattern = function(box, length)
34013 var pattern = box.slice(0, length);
34017 Roo.each(pattern, function(i){
34018 format.push(i.size);
34021 Roo.each(standard, function(s){
34023 if(String(s) != String(format)){
34032 if(!match && length == 1){
34037 filterPattern(box, length - 1);
34041 queue.push(pattern);
34043 box = box.slice(length, box.length);
34045 filterPattern(box, 4);
34051 Roo.each(boxes, function(box, k){
34057 if(box.length == 1){
34062 filterPattern(box, 4);
34069 var pos = this.el.getBox(true);
34073 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34075 var hit_end = false;
34077 Roo.each(queue, function(box){
34081 Roo.each(box, function(b){
34083 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34093 Roo.each(box, function(b){
34095 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34098 mx = Math.max(mx, b.x);
34102 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34106 Roo.each(box, function(b){
34108 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34122 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34125 /** Sets position of item in DOM
34126 * @param {Element} item
34127 * @param {Number} x - horizontal position
34128 * @param {Number} y - vertical position
34129 * @param {Boolean} isInstant - disables transitions
34131 _processVerticalLayoutQueue : function( queue, isInstant )
34133 var pos = this.el.getBox(true);
34138 for (var i = 0; i < this.cols; i++){
34142 Roo.each(queue, function(box, k){
34144 var col = k % this.cols;
34146 Roo.each(box, function(b,kk){
34148 b.el.position('absolute');
34150 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34151 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34153 if(b.size == 'md-left' || b.size == 'md-right'){
34154 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34155 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34158 b.el.setWidth(width);
34159 b.el.setHeight(height);
34161 b.el.select('iframe',true).setSize(width,height);
34165 for (var i = 0; i < this.cols; i++){
34167 if(maxY[i] < maxY[col]){
34172 col = Math.min(col, i);
34176 x = pos.x + col * (this.colWidth + this.padWidth);
34180 var positions = [];
34182 switch (box.length){
34184 positions = this.getVerticalOneBoxColPositions(x, y, box);
34187 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34190 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34193 positions = this.getVerticalFourBoxColPositions(x, y, box);
34199 Roo.each(box, function(b,kk){
34201 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34203 var sz = b.el.getSize();
34205 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34213 for (var i = 0; i < this.cols; i++){
34214 mY = Math.max(mY, maxY[i]);
34217 this.el.setHeight(mY - pos.y);
34221 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34223 // var pos = this.el.getBox(true);
34226 // var maxX = pos.right;
34228 // var maxHeight = 0;
34230 // Roo.each(items, function(item, k){
34234 // item.el.position('absolute');
34236 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34238 // item.el.setWidth(width);
34240 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34242 // item.el.setHeight(height);
34245 // item.el.setXY([x, y], isInstant ? false : true);
34247 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34250 // y = y + height + this.alternativePadWidth;
34252 // maxHeight = maxHeight + height + this.alternativePadWidth;
34256 // this.el.setHeight(maxHeight);
34260 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34262 var pos = this.el.getBox(true);
34267 var maxX = pos.right;
34269 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34271 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34273 Roo.each(queue, function(box, k){
34275 Roo.each(box, function(b, kk){
34277 b.el.position('absolute');
34279 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34280 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34282 if(b.size == 'md-left' || b.size == 'md-right'){
34283 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34284 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34287 b.el.setWidth(width);
34288 b.el.setHeight(height);
34296 var positions = [];
34298 switch (box.length){
34300 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34303 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34306 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34309 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34315 Roo.each(box, function(b,kk){
34317 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34319 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34327 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34329 Roo.each(eItems, function(b,k){
34331 b.size = (k == 0) ? 'sm' : 'xs';
34332 b.x = (k == 0) ? 2 : 1;
34333 b.y = (k == 0) ? 2 : 1;
34335 b.el.position('absolute');
34337 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34339 b.el.setWidth(width);
34341 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34343 b.el.setHeight(height);
34347 var positions = [];
34350 x : maxX - this.unitWidth * 2 - this.gutter,
34355 x : maxX - this.unitWidth,
34356 y : minY + (this.unitWidth + this.gutter) * 2
34360 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34364 Roo.each(eItems, function(b,k){
34366 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34372 getVerticalOneBoxColPositions : function(x, y, box)
34376 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34378 if(box[0].size == 'md-left'){
34382 if(box[0].size == 'md-right'){
34387 x : x + (this.unitWidth + this.gutter) * rand,
34394 getVerticalTwoBoxColPositions : function(x, y, box)
34398 if(box[0].size == 'xs'){
34402 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34406 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34420 x : x + (this.unitWidth + this.gutter) * 2,
34421 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34428 getVerticalThreeBoxColPositions : function(x, y, box)
34432 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34440 x : x + (this.unitWidth + this.gutter) * 1,
34445 x : x + (this.unitWidth + this.gutter) * 2,
34453 if(box[0].size == 'xs' && box[1].size == 'xs'){
34462 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34466 x : x + (this.unitWidth + this.gutter) * 1,
34480 x : x + (this.unitWidth + this.gutter) * 2,
34485 x : x + (this.unitWidth + this.gutter) * 2,
34486 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34493 getVerticalFourBoxColPositions : function(x, y, box)
34497 if(box[0].size == 'xs'){
34506 y : y + (this.unitHeight + this.gutter) * 1
34511 y : y + (this.unitHeight + this.gutter) * 2
34515 x : x + (this.unitWidth + this.gutter) * 1,
34529 x : x + (this.unitWidth + this.gutter) * 2,
34534 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34535 y : y + (this.unitHeight + this.gutter) * 1
34539 x : x + (this.unitWidth + this.gutter) * 2,
34540 y : y + (this.unitWidth + this.gutter) * 2
34547 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34551 if(box[0].size == 'md-left'){
34553 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34560 if(box[0].size == 'md-right'){
34562 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34563 y : minY + (this.unitWidth + this.gutter) * 1
34569 var rand = Math.floor(Math.random() * (4 - box[0].y));
34572 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34573 y : minY + (this.unitWidth + this.gutter) * rand
34580 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34584 if(box[0].size == 'xs'){
34587 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34592 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34593 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34601 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34606 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34607 y : minY + (this.unitWidth + this.gutter) * 2
34614 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34618 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34621 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34626 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34627 y : minY + (this.unitWidth + this.gutter) * 1
34631 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34632 y : minY + (this.unitWidth + this.gutter) * 2
34639 if(box[0].size == 'xs' && box[1].size == 'xs'){
34642 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34647 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34652 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34653 y : minY + (this.unitWidth + this.gutter) * 1
34661 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34666 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34667 y : minY + (this.unitWidth + this.gutter) * 2
34671 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34672 y : minY + (this.unitWidth + this.gutter) * 2
34679 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34683 if(box[0].size == 'xs'){
34686 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34691 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34696 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),
34701 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34702 y : minY + (this.unitWidth + this.gutter) * 1
34710 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34715 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34716 y : minY + (this.unitWidth + this.gutter) * 2
34720 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34721 y : minY + (this.unitWidth + this.gutter) * 2
34725 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),
34726 y : minY + (this.unitWidth + this.gutter) * 2
34734 * remove a Masonry Brick
34735 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34737 removeBrick : function(brick_id)
34743 for (var i = 0; i<this.bricks.length; i++) {
34744 if (this.bricks[i].id == brick_id) {
34745 this.bricks.splice(i,1);
34746 this.el.dom.removeChild(Roo.get(brick_id).dom);
34753 * adds a Masonry Brick
34754 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34756 addBrick : function(cfg)
34758 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34759 //this.register(cn);
34760 cn.parentId = this.id;
34761 cn.render(this.el);
34766 * register a Masonry Brick
34767 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34770 register : function(brick)
34772 this.bricks.push(brick);
34773 brick.masonryId = this.id;
34777 * clear all the Masonry Brick
34779 clearAll : function()
34782 //this.getChildContainer().dom.innerHTML = "";
34783 this.el.dom.innerHTML = '';
34786 getSelected : function()
34788 if (!this.selectedBrick) {
34792 return this.selectedBrick;
34796 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34800 * register a Masonry Layout
34801 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34804 register : function(layout)
34806 this.groups[layout.id] = layout;
34809 * fetch a Masonry Layout based on the masonry layout ID
34810 * @param {string} the masonry layout to add
34811 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34814 get: function(layout_id) {
34815 if (typeof(this.groups[layout_id]) == 'undefined') {
34818 return this.groups[layout_id] ;
34830 * http://masonry.desandro.com
34832 * The idea is to render all the bricks based on vertical width...
34834 * The original code extends 'outlayer' - we might need to use that....
34840 * @class Roo.bootstrap.LayoutMasonryAuto
34841 * @extends Roo.bootstrap.Component
34842 * Bootstrap Layout Masonry class
34845 * Create a new Element
34846 * @param {Object} config The config object
34849 Roo.bootstrap.LayoutMasonryAuto = function(config){
34850 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34853 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34856 * @cfg {Boolean} isFitWidth - resize the width..
34858 isFitWidth : false, // options..
34860 * @cfg {Boolean} isOriginLeft = left align?
34862 isOriginLeft : true,
34864 * @cfg {Boolean} isOriginTop = top align?
34866 isOriginTop : false,
34868 * @cfg {Boolean} isLayoutInstant = no animation?
34870 isLayoutInstant : false, // needed?
34872 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34874 isResizingContainer : true,
34876 * @cfg {Number} columnWidth width of the columns
34882 * @cfg {Number} maxCols maximum number of columns
34887 * @cfg {Number} padHeight padding below box..
34893 * @cfg {Boolean} isAutoInitial defalut true
34896 isAutoInitial : true,
34902 initialColumnWidth : 0,
34903 currentSize : null,
34905 colYs : null, // array.
34912 bricks: null, //CompositeElement
34913 cols : 0, // array?
34914 // element : null, // wrapped now this.el
34915 _isLayoutInited : null,
34918 getAutoCreate : function(){
34922 cls: 'blog-masonary-wrapper ' + this.cls,
34924 cls : 'mas-boxes masonary'
34931 getChildContainer: function( )
34933 if (this.boxesEl) {
34934 return this.boxesEl;
34937 this.boxesEl = this.el.select('.mas-boxes').first();
34939 return this.boxesEl;
34943 initEvents : function()
34947 if(this.isAutoInitial){
34948 Roo.log('hook children rendered');
34949 this.on('childrenrendered', function() {
34950 Roo.log('children rendered');
34957 initial : function()
34959 this.reloadItems();
34961 this.currentSize = this.el.getBox(true);
34963 /// was window resize... - let's see if this works..
34964 Roo.EventManager.onWindowResize(this.resize, this);
34966 if(!this.isAutoInitial){
34971 this.layout.defer(500,this);
34974 reloadItems: function()
34976 this.bricks = this.el.select('.masonry-brick', true);
34978 this.bricks.each(function(b) {
34979 //Roo.log(b.getSize());
34980 if (!b.attr('originalwidth')) {
34981 b.attr('originalwidth', b.getSize().width);
34986 Roo.log(this.bricks.elements.length);
34989 resize : function()
34992 var cs = this.el.getBox(true);
34994 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34995 Roo.log("no change in with or X");
34998 this.currentSize = cs;
35002 layout : function()
35005 this._resetLayout();
35006 //this._manageStamps();
35008 // don't animate first layout
35009 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35010 this.layoutItems( isInstant );
35012 // flag for initalized
35013 this._isLayoutInited = true;
35016 layoutItems : function( isInstant )
35018 //var items = this._getItemsForLayout( this.items );
35019 // original code supports filtering layout items.. we just ignore it..
35021 this._layoutItems( this.bricks , isInstant );
35023 this._postLayout();
35025 _layoutItems : function ( items , isInstant)
35027 //this.fireEvent( 'layout', this, items );
35030 if ( !items || !items.elements.length ) {
35031 // no items, emit event with empty array
35036 items.each(function(item) {
35037 Roo.log("layout item");
35039 // get x/y object from method
35040 var position = this._getItemLayoutPosition( item );
35042 position.item = item;
35043 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35044 queue.push( position );
35047 this._processLayoutQueue( queue );
35049 /** Sets position of item in DOM
35050 * @param {Element} item
35051 * @param {Number} x - horizontal position
35052 * @param {Number} y - vertical position
35053 * @param {Boolean} isInstant - disables transitions
35055 _processLayoutQueue : function( queue )
35057 for ( var i=0, len = queue.length; i < len; i++ ) {
35058 var obj = queue[i];
35059 obj.item.position('absolute');
35060 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35066 * Any logic you want to do after each layout,
35067 * i.e. size the container
35069 _postLayout : function()
35071 this.resizeContainer();
35074 resizeContainer : function()
35076 if ( !this.isResizingContainer ) {
35079 var size = this._getContainerSize();
35081 this.el.setSize(size.width,size.height);
35082 this.boxesEl.setSize(size.width,size.height);
35088 _resetLayout : function()
35090 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35091 this.colWidth = this.el.getWidth();
35092 //this.gutter = this.el.getWidth();
35094 this.measureColumns();
35100 this.colYs.push( 0 );
35106 measureColumns : function()
35108 this.getContainerWidth();
35109 // if columnWidth is 0, default to outerWidth of first item
35110 if ( !this.columnWidth ) {
35111 var firstItem = this.bricks.first();
35112 Roo.log(firstItem);
35113 this.columnWidth = this.containerWidth;
35114 if (firstItem && firstItem.attr('originalwidth') ) {
35115 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35117 // columnWidth fall back to item of first element
35118 Roo.log("set column width?");
35119 this.initialColumnWidth = this.columnWidth ;
35121 // if first elem has no width, default to size of container
35126 if (this.initialColumnWidth) {
35127 this.columnWidth = this.initialColumnWidth;
35132 // column width is fixed at the top - however if container width get's smaller we should
35135 // this bit calcs how man columns..
35137 var columnWidth = this.columnWidth += this.gutter;
35139 // calculate columns
35140 var containerWidth = this.containerWidth + this.gutter;
35142 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35143 // fix rounding errors, typically with gutters
35144 var excess = columnWidth - containerWidth % columnWidth;
35147 // if overshoot is less than a pixel, round up, otherwise floor it
35148 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35149 cols = Math[ mathMethod ]( cols );
35150 this.cols = Math.max( cols, 1 );
35151 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35153 // padding positioning..
35154 var totalColWidth = this.cols * this.columnWidth;
35155 var padavail = this.containerWidth - totalColWidth;
35156 // so for 2 columns - we need 3 'pads'
35158 var padNeeded = (1+this.cols) * this.padWidth;
35160 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35162 this.columnWidth += padExtra
35163 //this.padWidth = Math.floor(padavail / ( this.cols));
35165 // adjust colum width so that padding is fixed??
35167 // we have 3 columns ... total = width * 3
35168 // we have X left over... that should be used by
35170 //if (this.expandC) {
35178 getContainerWidth : function()
35180 /* // container is parent if fit width
35181 var container = this.isFitWidth ? this.element.parentNode : this.element;
35182 // check that this.size and size are there
35183 // IE8 triggers resize on body size change, so they might not be
35185 var size = getSize( container ); //FIXME
35186 this.containerWidth = size && size.innerWidth; //FIXME
35189 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35193 _getItemLayoutPosition : function( item ) // what is item?
35195 // we resize the item to our columnWidth..
35197 item.setWidth(this.columnWidth);
35198 item.autoBoxAdjust = false;
35200 var sz = item.getSize();
35202 // how many columns does this brick span
35203 var remainder = this.containerWidth % this.columnWidth;
35205 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35206 // round if off by 1 pixel, otherwise use ceil
35207 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35208 colSpan = Math.min( colSpan, this.cols );
35210 // normally this should be '1' as we dont' currently allow multi width columns..
35212 var colGroup = this._getColGroup( colSpan );
35213 // get the minimum Y value from the columns
35214 var minimumY = Math.min.apply( Math, colGroup );
35215 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35217 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35219 // position the brick
35221 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35222 y: this.currentSize.y + minimumY + this.padHeight
35226 // apply setHeight to necessary columns
35227 var setHeight = minimumY + sz.height + this.padHeight;
35228 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35230 var setSpan = this.cols + 1 - colGroup.length;
35231 for ( var i = 0; i < setSpan; i++ ) {
35232 this.colYs[ shortColIndex + i ] = setHeight ;
35239 * @param {Number} colSpan - number of columns the element spans
35240 * @returns {Array} colGroup
35242 _getColGroup : function( colSpan )
35244 if ( colSpan < 2 ) {
35245 // if brick spans only one column, use all the column Ys
35250 // how many different places could this brick fit horizontally
35251 var groupCount = this.cols + 1 - colSpan;
35252 // for each group potential horizontal position
35253 for ( var i = 0; i < groupCount; i++ ) {
35254 // make an array of colY values for that one group
35255 var groupColYs = this.colYs.slice( i, i + colSpan );
35256 // and get the max value of the array
35257 colGroup[i] = Math.max.apply( Math, groupColYs );
35262 _manageStamp : function( stamp )
35264 var stampSize = stamp.getSize();
35265 var offset = stamp.getBox();
35266 // get the columns that this stamp affects
35267 var firstX = this.isOriginLeft ? offset.x : offset.right;
35268 var lastX = firstX + stampSize.width;
35269 var firstCol = Math.floor( firstX / this.columnWidth );
35270 firstCol = Math.max( 0, firstCol );
35272 var lastCol = Math.floor( lastX / this.columnWidth );
35273 // lastCol should not go over if multiple of columnWidth #425
35274 lastCol -= lastX % this.columnWidth ? 0 : 1;
35275 lastCol = Math.min( this.cols - 1, lastCol );
35277 // set colYs to bottom of the stamp
35278 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35281 for ( var i = firstCol; i <= lastCol; i++ ) {
35282 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35287 _getContainerSize : function()
35289 this.maxY = Math.max.apply( Math, this.colYs );
35294 if ( this.isFitWidth ) {
35295 size.width = this._getContainerFitWidth();
35301 _getContainerFitWidth : function()
35303 var unusedCols = 0;
35304 // count unused columns
35307 if ( this.colYs[i] !== 0 ) {
35312 // fit container to columns that have been used
35313 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35316 needsResizeLayout : function()
35318 var previousWidth = this.containerWidth;
35319 this.getContainerWidth();
35320 return previousWidth !== this.containerWidth;
35335 * @class Roo.bootstrap.MasonryBrick
35336 * @extends Roo.bootstrap.Component
35337 * Bootstrap MasonryBrick class
35340 * Create a new MasonryBrick
35341 * @param {Object} config The config object
35344 Roo.bootstrap.MasonryBrick = function(config){
35346 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35348 Roo.bootstrap.MasonryBrick.register(this);
35354 * When a MasonryBrick is clcik
35355 * @param {Roo.bootstrap.MasonryBrick} this
35356 * @param {Roo.EventObject} e
35362 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35365 * @cfg {String} title
35369 * @cfg {String} html
35373 * @cfg {String} bgimage
35377 * @cfg {String} videourl
35381 * @cfg {String} cls
35385 * @cfg {String} href
35389 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35394 * @cfg {String} placetitle (center|bottom)
35399 * @cfg {Boolean} isFitContainer defalut true
35401 isFitContainer : true,
35404 * @cfg {Boolean} preventDefault defalut false
35406 preventDefault : false,
35409 * @cfg {Boolean} inverse defalut false
35411 maskInverse : false,
35413 getAutoCreate : function()
35415 if(!this.isFitContainer){
35416 return this.getSplitAutoCreate();
35419 var cls = 'masonry-brick masonry-brick-full';
35421 if(this.href.length){
35422 cls += ' masonry-brick-link';
35425 if(this.bgimage.length){
35426 cls += ' masonry-brick-image';
35429 if(this.maskInverse){
35430 cls += ' mask-inverse';
35433 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35434 cls += ' enable-mask';
35438 cls += ' masonry-' + this.size + '-brick';
35441 if(this.placetitle.length){
35443 switch (this.placetitle) {
35445 cls += ' masonry-center-title';
35448 cls += ' masonry-bottom-title';
35455 if(!this.html.length && !this.bgimage.length){
35456 cls += ' masonry-center-title';
35459 if(!this.html.length && this.bgimage.length){
35460 cls += ' masonry-bottom-title';
35465 cls += ' ' + this.cls;
35469 tag: (this.href.length) ? 'a' : 'div',
35474 cls: 'masonry-brick-mask'
35478 cls: 'masonry-brick-paragraph',
35484 if(this.href.length){
35485 cfg.href = this.href;
35488 var cn = cfg.cn[1].cn;
35490 if(this.title.length){
35493 cls: 'masonry-brick-title',
35498 if(this.html.length){
35501 cls: 'masonry-brick-text',
35506 if (!this.title.length && !this.html.length) {
35507 cfg.cn[1].cls += ' hide';
35510 if(this.bgimage.length){
35513 cls: 'masonry-brick-image-view',
35518 if(this.videourl.length){
35519 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35520 // youtube support only?
35523 cls: 'masonry-brick-image-view',
35526 allowfullscreen : true
35534 getSplitAutoCreate : function()
35536 var cls = 'masonry-brick masonry-brick-split';
35538 if(this.href.length){
35539 cls += ' masonry-brick-link';
35542 if(this.bgimage.length){
35543 cls += ' masonry-brick-image';
35547 cls += ' masonry-' + this.size + '-brick';
35550 switch (this.placetitle) {
35552 cls += ' masonry-center-title';
35555 cls += ' masonry-bottom-title';
35558 if(!this.bgimage.length){
35559 cls += ' masonry-center-title';
35562 if(this.bgimage.length){
35563 cls += ' masonry-bottom-title';
35569 cls += ' ' + this.cls;
35573 tag: (this.href.length) ? 'a' : 'div',
35578 cls: 'masonry-brick-split-head',
35582 cls: 'masonry-brick-paragraph',
35589 cls: 'masonry-brick-split-body',
35595 if(this.href.length){
35596 cfg.href = this.href;
35599 if(this.title.length){
35600 cfg.cn[0].cn[0].cn.push({
35602 cls: 'masonry-brick-title',
35607 if(this.html.length){
35608 cfg.cn[1].cn.push({
35610 cls: 'masonry-brick-text',
35615 if(this.bgimage.length){
35616 cfg.cn[0].cn.push({
35618 cls: 'masonry-brick-image-view',
35623 if(this.videourl.length){
35624 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35625 // youtube support only?
35626 cfg.cn[0].cn.cn.push({
35628 cls: 'masonry-brick-image-view',
35631 allowfullscreen : true
35638 initEvents: function()
35640 switch (this.size) {
35673 this.el.on('touchstart', this.onTouchStart, this);
35674 this.el.on('touchmove', this.onTouchMove, this);
35675 this.el.on('touchend', this.onTouchEnd, this);
35676 this.el.on('contextmenu', this.onContextMenu, this);
35678 this.el.on('mouseenter' ,this.enter, this);
35679 this.el.on('mouseleave', this.leave, this);
35680 this.el.on('click', this.onClick, this);
35683 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35684 this.parent().bricks.push(this);
35689 onClick: function(e, el)
35691 var time = this.endTimer - this.startTimer;
35692 // Roo.log(e.preventDefault());
35695 e.preventDefault();
35700 if(!this.preventDefault){
35704 e.preventDefault();
35706 if (this.activeClass != '') {
35707 this.selectBrick();
35710 this.fireEvent('click', this, e);
35713 enter: function(e, el)
35715 e.preventDefault();
35717 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35721 if(this.bgimage.length && this.html.length){
35722 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35726 leave: function(e, el)
35728 e.preventDefault();
35730 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35734 if(this.bgimage.length && this.html.length){
35735 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35739 onTouchStart: function(e, el)
35741 // e.preventDefault();
35743 this.touchmoved = false;
35745 if(!this.isFitContainer){
35749 if(!this.bgimage.length || !this.html.length){
35753 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35755 this.timer = new Date().getTime();
35759 onTouchMove: function(e, el)
35761 this.touchmoved = true;
35764 onContextMenu : function(e,el)
35766 e.preventDefault();
35767 e.stopPropagation();
35771 onTouchEnd: function(e, el)
35773 // e.preventDefault();
35775 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35782 if(!this.bgimage.length || !this.html.length){
35784 if(this.href.length){
35785 window.location.href = this.href;
35791 if(!this.isFitContainer){
35795 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35797 window.location.href = this.href;
35800 //selection on single brick only
35801 selectBrick : function() {
35803 if (!this.parentId) {
35807 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35808 var index = m.selectedBrick.indexOf(this.id);
35811 m.selectedBrick.splice(index,1);
35812 this.el.removeClass(this.activeClass);
35816 for(var i = 0; i < m.selectedBrick.length; i++) {
35817 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35818 b.el.removeClass(b.activeClass);
35821 m.selectedBrick = [];
35823 m.selectedBrick.push(this.id);
35824 this.el.addClass(this.activeClass);
35828 isSelected : function(){
35829 return this.el.hasClass(this.activeClass);
35834 Roo.apply(Roo.bootstrap.MasonryBrick, {
35837 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35839 * register a Masonry Brick
35840 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35843 register : function(brick)
35845 //this.groups[brick.id] = brick;
35846 this.groups.add(brick.id, brick);
35849 * fetch a masonry brick based on the masonry brick ID
35850 * @param {string} the masonry brick to add
35851 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35854 get: function(brick_id)
35856 // if (typeof(this.groups[brick_id]) == 'undefined') {
35859 // return this.groups[brick_id] ;
35861 if(this.groups.key(brick_id)) {
35862 return this.groups.key(brick_id);
35880 * @class Roo.bootstrap.Brick
35881 * @extends Roo.bootstrap.Component
35882 * Bootstrap Brick class
35885 * Create a new Brick
35886 * @param {Object} config The config object
35889 Roo.bootstrap.Brick = function(config){
35890 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35896 * When a Brick is click
35897 * @param {Roo.bootstrap.Brick} this
35898 * @param {Roo.EventObject} e
35904 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35907 * @cfg {String} title
35911 * @cfg {String} html
35915 * @cfg {String} bgimage
35919 * @cfg {String} cls
35923 * @cfg {String} href
35927 * @cfg {String} video
35931 * @cfg {Boolean} square
35935 getAutoCreate : function()
35937 var cls = 'roo-brick';
35939 if(this.href.length){
35940 cls += ' roo-brick-link';
35943 if(this.bgimage.length){
35944 cls += ' roo-brick-image';
35947 if(!this.html.length && !this.bgimage.length){
35948 cls += ' roo-brick-center-title';
35951 if(!this.html.length && this.bgimage.length){
35952 cls += ' roo-brick-bottom-title';
35956 cls += ' ' + this.cls;
35960 tag: (this.href.length) ? 'a' : 'div',
35965 cls: 'roo-brick-paragraph',
35971 if(this.href.length){
35972 cfg.href = this.href;
35975 var cn = cfg.cn[0].cn;
35977 if(this.title.length){
35980 cls: 'roo-brick-title',
35985 if(this.html.length){
35988 cls: 'roo-brick-text',
35995 if(this.bgimage.length){
35998 cls: 'roo-brick-image-view',
36006 initEvents: function()
36008 if(this.title.length || this.html.length){
36009 this.el.on('mouseenter' ,this.enter, this);
36010 this.el.on('mouseleave', this.leave, this);
36013 Roo.EventManager.onWindowResize(this.resize, this);
36015 if(this.bgimage.length){
36016 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36017 this.imageEl.on('load', this.onImageLoad, this);
36024 onImageLoad : function()
36029 resize : function()
36031 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36033 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36035 if(this.bgimage.length){
36036 var image = this.el.select('.roo-brick-image-view', true).first();
36038 image.setWidth(paragraph.getWidth());
36041 image.setHeight(paragraph.getWidth());
36044 this.el.setHeight(image.getHeight());
36045 paragraph.setHeight(image.getHeight());
36051 enter: function(e, el)
36053 e.preventDefault();
36055 if(this.bgimage.length){
36056 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36057 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36061 leave: function(e, el)
36063 e.preventDefault();
36065 if(this.bgimage.length){
36066 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36067 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36082 * @class Roo.bootstrap.NumberField
36083 * @extends Roo.bootstrap.Input
36084 * Bootstrap NumberField class
36090 * Create a new NumberField
36091 * @param {Object} config The config object
36094 Roo.bootstrap.NumberField = function(config){
36095 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36098 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36101 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36103 allowDecimals : true,
36105 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36107 decimalSeparator : ".",
36109 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36111 decimalPrecision : 2,
36113 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36115 allowNegative : true,
36118 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36122 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36124 minValue : Number.NEGATIVE_INFINITY,
36126 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36128 maxValue : Number.MAX_VALUE,
36130 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36132 minText : "The minimum value for this field is {0}",
36134 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36136 maxText : "The maximum value for this field is {0}",
36138 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36139 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36141 nanText : "{0} is not a valid number",
36143 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36145 thousandsDelimiter : false,
36147 * @cfg {String} valueAlign alignment of value
36149 valueAlign : "left",
36151 getAutoCreate : function()
36153 var hiddenInput = {
36157 cls: 'hidden-number-input'
36161 hiddenInput.name = this.name;
36166 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36168 this.name = hiddenInput.name;
36170 if(cfg.cn.length > 0) {
36171 cfg.cn.push(hiddenInput);
36178 initEvents : function()
36180 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36182 var allowed = "0123456789";
36184 if(this.allowDecimals){
36185 allowed += this.decimalSeparator;
36188 if(this.allowNegative){
36192 if(this.thousandsDelimiter) {
36196 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36198 var keyPress = function(e){
36200 var k = e.getKey();
36202 var c = e.getCharCode();
36205 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36206 allowed.indexOf(String.fromCharCode(c)) === -1
36212 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36216 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36221 this.el.on("keypress", keyPress, this);
36224 validateValue : function(value)
36227 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36231 var num = this.parseValue(value);
36234 this.markInvalid(String.format(this.nanText, value));
36238 if(num < this.minValue){
36239 this.markInvalid(String.format(this.minText, this.minValue));
36243 if(num > this.maxValue){
36244 this.markInvalid(String.format(this.maxText, this.maxValue));
36251 getValue : function()
36253 var v = this.hiddenEl().getValue();
36255 return this.fixPrecision(this.parseValue(v));
36258 parseValue : function(value)
36260 if(this.thousandsDelimiter) {
36262 r = new RegExp(",", "g");
36263 value = value.replace(r, "");
36266 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36267 return isNaN(value) ? '' : value;
36270 fixPrecision : function(value)
36272 if(this.thousandsDelimiter) {
36274 r = new RegExp(",", "g");
36275 value = value.replace(r, "");
36278 var nan = isNaN(value);
36280 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36281 return nan ? '' : value;
36283 return parseFloat(value).toFixed(this.decimalPrecision);
36286 setValue : function(v)
36288 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36294 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36296 this.inputEl().dom.value = (v == '') ? '' :
36297 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36299 if(!this.allowZero && v === '0') {
36300 this.hiddenEl().dom.value = '';
36301 this.inputEl().dom.value = '';
36308 decimalPrecisionFcn : function(v)
36310 return Math.floor(v);
36313 beforeBlur : function()
36315 var v = this.parseValue(this.getRawValue());
36317 if(v || v === 0 || v === ''){
36322 hiddenEl : function()
36324 return this.el.select('input.hidden-number-input',true).first();
36336 * @class Roo.bootstrap.DocumentSlider
36337 * @extends Roo.bootstrap.Component
36338 * Bootstrap DocumentSlider class
36341 * Create a new DocumentViewer
36342 * @param {Object} config The config object
36345 Roo.bootstrap.DocumentSlider = function(config){
36346 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36353 * Fire after initEvent
36354 * @param {Roo.bootstrap.DocumentSlider} this
36359 * Fire after update
36360 * @param {Roo.bootstrap.DocumentSlider} this
36366 * @param {Roo.bootstrap.DocumentSlider} this
36372 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36378 getAutoCreate : function()
36382 cls : 'roo-document-slider',
36386 cls : 'roo-document-slider-header',
36390 cls : 'roo-document-slider-header-title'
36396 cls : 'roo-document-slider-body',
36400 cls : 'roo-document-slider-prev',
36404 cls : 'fa fa-chevron-left'
36410 cls : 'roo-document-slider-thumb',
36414 cls : 'roo-document-slider-image'
36420 cls : 'roo-document-slider-next',
36424 cls : 'fa fa-chevron-right'
36436 initEvents : function()
36438 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36439 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36441 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36442 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36444 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36445 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36447 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36448 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36450 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36451 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36453 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36454 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36456 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36457 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36459 this.thumbEl.on('click', this.onClick, this);
36461 this.prevIndicator.on('click', this.prev, this);
36463 this.nextIndicator.on('click', this.next, this);
36467 initial : function()
36469 if(this.files.length){
36470 this.indicator = 1;
36474 this.fireEvent('initial', this);
36477 update : function()
36479 this.imageEl.attr('src', this.files[this.indicator - 1]);
36481 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36483 this.prevIndicator.show();
36485 if(this.indicator == 1){
36486 this.prevIndicator.hide();
36489 this.nextIndicator.show();
36491 if(this.indicator == this.files.length){
36492 this.nextIndicator.hide();
36495 this.thumbEl.scrollTo('top');
36497 this.fireEvent('update', this);
36500 onClick : function(e)
36502 e.preventDefault();
36504 this.fireEvent('click', this);
36509 e.preventDefault();
36511 this.indicator = Math.max(1, this.indicator - 1);
36518 e.preventDefault();
36520 this.indicator = Math.min(this.files.length, this.indicator + 1);
36534 * @class Roo.bootstrap.RadioSet
36535 * @extends Roo.bootstrap.Input
36536 * Bootstrap RadioSet class
36537 * @cfg {String} indicatorpos (left|right) default left
36538 * @cfg {Boolean} inline (true|false) inline the element (default true)
36539 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36541 * Create a new RadioSet
36542 * @param {Object} config The config object
36545 Roo.bootstrap.RadioSet = function(config){
36547 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36551 Roo.bootstrap.RadioSet.register(this);
36556 * Fires when the element is checked or unchecked.
36557 * @param {Roo.bootstrap.RadioSet} this This radio
36558 * @param {Roo.bootstrap.Radio} item The checked item
36563 * Fires when the element is click.
36564 * @param {Roo.bootstrap.RadioSet} this This radio set
36565 * @param {Roo.bootstrap.Radio} item The checked item
36566 * @param {Roo.EventObject} e The event object
36573 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36581 indicatorpos : 'left',
36583 getAutoCreate : function()
36587 cls : 'roo-radio-set-label',
36591 html : this.fieldLabel
36595 if (Roo.bootstrap.version == 3) {
36598 if(this.indicatorpos == 'left'){
36601 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36602 tooltip : 'This field is required'
36607 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36608 tooltip : 'This field is required'
36614 cls : 'roo-radio-set-items'
36617 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36619 if (align === 'left' && this.fieldLabel.length) {
36622 cls : "roo-radio-set-right",
36628 if(this.labelWidth > 12){
36629 label.style = "width: " + this.labelWidth + 'px';
36632 if(this.labelWidth < 13 && this.labelmd == 0){
36633 this.labelmd = this.labelWidth;
36636 if(this.labellg > 0){
36637 label.cls += ' col-lg-' + this.labellg;
36638 items.cls += ' col-lg-' + (12 - this.labellg);
36641 if(this.labelmd > 0){
36642 label.cls += ' col-md-' + this.labelmd;
36643 items.cls += ' col-md-' + (12 - this.labelmd);
36646 if(this.labelsm > 0){
36647 label.cls += ' col-sm-' + this.labelsm;
36648 items.cls += ' col-sm-' + (12 - this.labelsm);
36651 if(this.labelxs > 0){
36652 label.cls += ' col-xs-' + this.labelxs;
36653 items.cls += ' col-xs-' + (12 - this.labelxs);
36659 cls : 'roo-radio-set',
36663 cls : 'roo-radio-set-input',
36666 value : this.value ? this.value : ''
36673 if(this.weight.length){
36674 cfg.cls += ' roo-radio-' + this.weight;
36678 cfg.cls += ' roo-radio-set-inline';
36682 ['xs','sm','md','lg'].map(function(size){
36683 if (settings[size]) {
36684 cfg.cls += ' col-' + size + '-' + settings[size];
36692 initEvents : function()
36694 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36695 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36697 if(!this.fieldLabel.length){
36698 this.labelEl.hide();
36701 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36702 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36704 this.indicator = this.indicatorEl();
36706 if(this.indicator){
36707 this.indicator.addClass('invisible');
36710 this.originalValue = this.getValue();
36714 inputEl: function ()
36716 return this.el.select('.roo-radio-set-input', true).first();
36719 getChildContainer : function()
36721 return this.itemsEl;
36724 register : function(item)
36726 this.radioes.push(item);
36730 validate : function()
36732 if(this.getVisibilityEl().hasClass('hidden')){
36738 Roo.each(this.radioes, function(i){
36747 if(this.allowBlank) {
36751 if(this.disabled || valid){
36756 this.markInvalid();
36761 markValid : function()
36763 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36764 this.indicatorEl().removeClass('visible');
36765 this.indicatorEl().addClass('invisible');
36769 if (Roo.bootstrap.version == 3) {
36770 this.el.removeClass([this.invalidClass, this.validClass]);
36771 this.el.addClass(this.validClass);
36773 this.el.removeClass(['is-invalid','is-valid']);
36774 this.el.addClass(['is-valid']);
36776 this.fireEvent('valid', this);
36779 markInvalid : function(msg)
36781 if(this.allowBlank || this.disabled){
36785 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36786 this.indicatorEl().removeClass('invisible');
36787 this.indicatorEl().addClass('visible');
36789 if (Roo.bootstrap.version == 3) {
36790 this.el.removeClass([this.invalidClass, this.validClass]);
36791 this.el.addClass(this.invalidClass);
36793 this.el.removeClass(['is-invalid','is-valid']);
36794 this.el.addClass(['is-invalid']);
36797 this.fireEvent('invalid', this, msg);
36801 setValue : function(v, suppressEvent)
36803 if(this.value === v){
36810 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36813 Roo.each(this.radioes, function(i){
36815 i.el.removeClass('checked');
36818 Roo.each(this.radioes, function(i){
36820 if(i.value === v || i.value.toString() === v.toString()){
36822 i.el.addClass('checked');
36824 if(suppressEvent !== true){
36825 this.fireEvent('check', this, i);
36836 clearInvalid : function(){
36838 if(!this.el || this.preventMark){
36842 this.el.removeClass([this.invalidClass]);
36844 this.fireEvent('valid', this);
36849 Roo.apply(Roo.bootstrap.RadioSet, {
36853 register : function(set)
36855 this.groups[set.name] = set;
36858 get: function(name)
36860 if (typeof(this.groups[name]) == 'undefined') {
36864 return this.groups[name] ;
36870 * Ext JS Library 1.1.1
36871 * Copyright(c) 2006-2007, Ext JS, LLC.
36873 * Originally Released Under LGPL - original licence link has changed is not relivant.
36876 * <script type="text/javascript">
36881 * @class Roo.bootstrap.SplitBar
36882 * @extends Roo.util.Observable
36883 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36887 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36888 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36889 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36890 split.minSize = 100;
36891 split.maxSize = 600;
36892 split.animate = true;
36893 split.on('moved', splitterMoved);
36896 * Create a new SplitBar
36897 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36898 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36899 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36900 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36901 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36902 position of the SplitBar).
36904 Roo.bootstrap.SplitBar = function(cfg){
36909 // dragElement : elm
36910 // resizingElement: el,
36912 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36913 // placement : Roo.bootstrap.SplitBar.LEFT ,
36914 // existingProxy ???
36917 this.el = Roo.get(cfg.dragElement, true);
36918 this.el.dom.unselectable = "on";
36920 this.resizingEl = Roo.get(cfg.resizingElement, true);
36924 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36925 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36928 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36931 * The minimum size of the resizing element. (Defaults to 0)
36937 * The maximum size of the resizing element. (Defaults to 2000)
36940 this.maxSize = 2000;
36943 * Whether to animate the transition to the new size
36946 this.animate = false;
36949 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36952 this.useShim = false;
36957 if(!cfg.existingProxy){
36959 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36961 this.proxy = Roo.get(cfg.existingProxy).dom;
36964 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36967 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36970 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36973 this.dragSpecs = {};
36976 * @private The adapter to use to positon and resize elements
36978 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36979 this.adapter.init(this);
36981 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36983 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36984 this.el.addClass("roo-splitbar-h");
36987 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36988 this.el.addClass("roo-splitbar-v");
36994 * Fires when the splitter is moved (alias for {@link #event-moved})
36995 * @param {Roo.bootstrap.SplitBar} this
36996 * @param {Number} newSize the new width or height
37001 * Fires when the splitter is moved
37002 * @param {Roo.bootstrap.SplitBar} this
37003 * @param {Number} newSize the new width or height
37007 * @event beforeresize
37008 * Fires before the splitter is dragged
37009 * @param {Roo.bootstrap.SplitBar} this
37011 "beforeresize" : true,
37013 "beforeapply" : true
37016 Roo.util.Observable.call(this);
37019 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37020 onStartProxyDrag : function(x, y){
37021 this.fireEvent("beforeresize", this);
37023 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37025 o.enableDisplayMode("block");
37026 // all splitbars share the same overlay
37027 Roo.bootstrap.SplitBar.prototype.overlay = o;
37029 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37030 this.overlay.show();
37031 Roo.get(this.proxy).setDisplayed("block");
37032 var size = this.adapter.getElementSize(this);
37033 this.activeMinSize = this.getMinimumSize();;
37034 this.activeMaxSize = this.getMaximumSize();;
37035 var c1 = size - this.activeMinSize;
37036 var c2 = Math.max(this.activeMaxSize - size, 0);
37037 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37038 this.dd.resetConstraints();
37039 this.dd.setXConstraint(
37040 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37041 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37043 this.dd.setYConstraint(0, 0);
37045 this.dd.resetConstraints();
37046 this.dd.setXConstraint(0, 0);
37047 this.dd.setYConstraint(
37048 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37049 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37052 this.dragSpecs.startSize = size;
37053 this.dragSpecs.startPoint = [x, y];
37054 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37058 * @private Called after the drag operation by the DDProxy
37060 onEndProxyDrag : function(e){
37061 Roo.get(this.proxy).setDisplayed(false);
37062 var endPoint = Roo.lib.Event.getXY(e);
37064 this.overlay.hide();
37067 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37068 newSize = this.dragSpecs.startSize +
37069 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37070 endPoint[0] - this.dragSpecs.startPoint[0] :
37071 this.dragSpecs.startPoint[0] - endPoint[0]
37074 newSize = this.dragSpecs.startSize +
37075 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37076 endPoint[1] - this.dragSpecs.startPoint[1] :
37077 this.dragSpecs.startPoint[1] - endPoint[1]
37080 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37081 if(newSize != this.dragSpecs.startSize){
37082 if(this.fireEvent('beforeapply', this, newSize) !== false){
37083 this.adapter.setElementSize(this, newSize);
37084 this.fireEvent("moved", this, newSize);
37085 this.fireEvent("resize", this, newSize);
37091 * Get the adapter this SplitBar uses
37092 * @return The adapter object
37094 getAdapter : function(){
37095 return this.adapter;
37099 * Set the adapter this SplitBar uses
37100 * @param {Object} adapter A SplitBar adapter object
37102 setAdapter : function(adapter){
37103 this.adapter = adapter;
37104 this.adapter.init(this);
37108 * Gets the minimum size for the resizing element
37109 * @return {Number} The minimum size
37111 getMinimumSize : function(){
37112 return this.minSize;
37116 * Sets the minimum size for the resizing element
37117 * @param {Number} minSize The minimum size
37119 setMinimumSize : function(minSize){
37120 this.minSize = minSize;
37124 * Gets the maximum size for the resizing element
37125 * @return {Number} The maximum size
37127 getMaximumSize : function(){
37128 return this.maxSize;
37132 * Sets the maximum size for the resizing element
37133 * @param {Number} maxSize The maximum size
37135 setMaximumSize : function(maxSize){
37136 this.maxSize = maxSize;
37140 * Sets the initialize size for the resizing element
37141 * @param {Number} size The initial size
37143 setCurrentSize : function(size){
37144 var oldAnimate = this.animate;
37145 this.animate = false;
37146 this.adapter.setElementSize(this, size);
37147 this.animate = oldAnimate;
37151 * Destroy this splitbar.
37152 * @param {Boolean} removeEl True to remove the element
37154 destroy : function(removeEl){
37156 this.shim.remove();
37159 this.proxy.parentNode.removeChild(this.proxy);
37167 * @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.
37169 Roo.bootstrap.SplitBar.createProxy = function(dir){
37170 var proxy = new Roo.Element(document.createElement("div"));
37171 proxy.unselectable();
37172 var cls = 'roo-splitbar-proxy';
37173 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37174 document.body.appendChild(proxy.dom);
37179 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37180 * Default Adapter. It assumes the splitter and resizing element are not positioned
37181 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37183 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37186 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37187 // do nothing for now
37188 init : function(s){
37192 * Called before drag operations to get the current size of the resizing element.
37193 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37195 getElementSize : function(s){
37196 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37197 return s.resizingEl.getWidth();
37199 return s.resizingEl.getHeight();
37204 * Called after drag operations to set the size of the resizing element.
37205 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37206 * @param {Number} newSize The new size to set
37207 * @param {Function} onComplete A function to be invoked when resizing is complete
37209 setElementSize : function(s, newSize, onComplete){
37210 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37212 s.resizingEl.setWidth(newSize);
37214 onComplete(s, newSize);
37217 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37222 s.resizingEl.setHeight(newSize);
37224 onComplete(s, newSize);
37227 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37234 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37235 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37236 * Adapter that moves the splitter element to align with the resized sizing element.
37237 * Used with an absolute positioned SplitBar.
37238 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37239 * document.body, make sure you assign an id to the body element.
37241 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37242 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37243 this.container = Roo.get(container);
37246 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37247 init : function(s){
37248 this.basic.init(s);
37251 getElementSize : function(s){
37252 return this.basic.getElementSize(s);
37255 setElementSize : function(s, newSize, onComplete){
37256 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37259 moveSplitter : function(s){
37260 var yes = Roo.bootstrap.SplitBar;
37261 switch(s.placement){
37263 s.el.setX(s.resizingEl.getRight());
37266 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37269 s.el.setY(s.resizingEl.getBottom());
37272 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37279 * Orientation constant - Create a vertical SplitBar
37283 Roo.bootstrap.SplitBar.VERTICAL = 1;
37286 * Orientation constant - Create a horizontal SplitBar
37290 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37293 * Placement constant - The resizing element is to the left of the splitter element
37297 Roo.bootstrap.SplitBar.LEFT = 1;
37300 * Placement constant - The resizing element is to the right of the splitter element
37304 Roo.bootstrap.SplitBar.RIGHT = 2;
37307 * Placement constant - The resizing element is positioned above the splitter element
37311 Roo.bootstrap.SplitBar.TOP = 3;
37314 * Placement constant - The resizing element is positioned under splitter element
37318 Roo.bootstrap.SplitBar.BOTTOM = 4;
37319 Roo.namespace("Roo.bootstrap.layout");/*
37321 * Ext JS Library 1.1.1
37322 * Copyright(c) 2006-2007, Ext JS, LLC.
37324 * Originally Released Under LGPL - original licence link has changed is not relivant.
37327 * <script type="text/javascript">
37331 * @class Roo.bootstrap.layout.Manager
37332 * @extends Roo.bootstrap.Component
37333 * Base class for layout managers.
37335 Roo.bootstrap.layout.Manager = function(config)
37337 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37343 /** false to disable window resize monitoring @type Boolean */
37344 this.monitorWindowResize = true;
37349 * Fires when a layout is performed.
37350 * @param {Roo.LayoutManager} this
37354 * @event regionresized
37355 * Fires when the user resizes a region.
37356 * @param {Roo.LayoutRegion} region The resized region
37357 * @param {Number} newSize The new size (width for east/west, height for north/south)
37359 "regionresized" : true,
37361 * @event regioncollapsed
37362 * Fires when a region is collapsed.
37363 * @param {Roo.LayoutRegion} region The collapsed region
37365 "regioncollapsed" : true,
37367 * @event regionexpanded
37368 * Fires when a region is expanded.
37369 * @param {Roo.LayoutRegion} region The expanded region
37371 "regionexpanded" : true
37373 this.updating = false;
37376 this.el = Roo.get(config.el);
37382 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37387 monitorWindowResize : true,
37393 onRender : function(ct, position)
37396 this.el = Roo.get(ct);
37399 //this.fireEvent('render',this);
37403 initEvents: function()
37407 // ie scrollbar fix
37408 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37409 document.body.scroll = "no";
37410 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37411 this.el.position('relative');
37413 this.id = this.el.id;
37414 this.el.addClass("roo-layout-container");
37415 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37416 if(this.el.dom != document.body ) {
37417 this.el.on('resize', this.layout,this);
37418 this.el.on('show', this.layout,this);
37424 * Returns true if this layout is currently being updated
37425 * @return {Boolean}
37427 isUpdating : function(){
37428 return this.updating;
37432 * Suspend the LayoutManager from doing auto-layouts while
37433 * making multiple add or remove calls
37435 beginUpdate : function(){
37436 this.updating = true;
37440 * Restore auto-layouts and optionally disable the manager from performing a layout
37441 * @param {Boolean} noLayout true to disable a layout update
37443 endUpdate : function(noLayout){
37444 this.updating = false;
37450 layout: function(){
37454 onRegionResized : function(region, newSize){
37455 this.fireEvent("regionresized", region, newSize);
37459 onRegionCollapsed : function(region){
37460 this.fireEvent("regioncollapsed", region);
37463 onRegionExpanded : function(region){
37464 this.fireEvent("regionexpanded", region);
37468 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37469 * performs box-model adjustments.
37470 * @return {Object} The size as an object {width: (the width), height: (the height)}
37472 getViewSize : function()
37475 if(this.el.dom != document.body){
37476 size = this.el.getSize();
37478 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37480 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37481 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37486 * Returns the Element this layout is bound to.
37487 * @return {Roo.Element}
37489 getEl : function(){
37494 * Returns the specified region.
37495 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37496 * @return {Roo.LayoutRegion}
37498 getRegion : function(target){
37499 return this.regions[target.toLowerCase()];
37502 onWindowResize : function(){
37503 if(this.monitorWindowResize){
37510 * Ext JS Library 1.1.1
37511 * Copyright(c) 2006-2007, Ext JS, LLC.
37513 * Originally Released Under LGPL - original licence link has changed is not relivant.
37516 * <script type="text/javascript">
37519 * @class Roo.bootstrap.layout.Border
37520 * @extends Roo.bootstrap.layout.Manager
37521 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37522 * please see: examples/bootstrap/nested.html<br><br>
37524 <b>The container the layout is rendered into can be either the body element or any other element.
37525 If it is not the body element, the container needs to either be an absolute positioned element,
37526 or you will need to add "position:relative" to the css of the container. You will also need to specify
37527 the container size if it is not the body element.</b>
37530 * Create a new Border
37531 * @param {Object} config Configuration options
37533 Roo.bootstrap.layout.Border = function(config){
37534 config = config || {};
37535 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37539 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37540 if(config[region]){
37541 config[region].region = region;
37542 this.addRegion(config[region]);
37548 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37550 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37552 parent : false, // this might point to a 'nest' or a ???
37555 * Creates and adds a new region if it doesn't already exist.
37556 * @param {String} target The target region key (north, south, east, west or center).
37557 * @param {Object} config The regions config object
37558 * @return {BorderLayoutRegion} The new region
37560 addRegion : function(config)
37562 if(!this.regions[config.region]){
37563 var r = this.factory(config);
37564 this.bindRegion(r);
37566 return this.regions[config.region];
37570 bindRegion : function(r){
37571 this.regions[r.config.region] = r;
37573 r.on("visibilitychange", this.layout, this);
37574 r.on("paneladded", this.layout, this);
37575 r.on("panelremoved", this.layout, this);
37576 r.on("invalidated", this.layout, this);
37577 r.on("resized", this.onRegionResized, this);
37578 r.on("collapsed", this.onRegionCollapsed, this);
37579 r.on("expanded", this.onRegionExpanded, this);
37583 * Performs a layout update.
37585 layout : function()
37587 if(this.updating) {
37591 // render all the rebions if they have not been done alreayd?
37592 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37593 if(this.regions[region] && !this.regions[region].bodyEl){
37594 this.regions[region].onRender(this.el)
37598 var size = this.getViewSize();
37599 var w = size.width;
37600 var h = size.height;
37605 //var x = 0, y = 0;
37607 var rs = this.regions;
37608 var north = rs["north"];
37609 var south = rs["south"];
37610 var west = rs["west"];
37611 var east = rs["east"];
37612 var center = rs["center"];
37613 //if(this.hideOnLayout){ // not supported anymore
37614 //c.el.setStyle("display", "none");
37616 if(north && north.isVisible()){
37617 var b = north.getBox();
37618 var m = north.getMargins();
37619 b.width = w - (m.left+m.right);
37622 centerY = b.height + b.y + m.bottom;
37623 centerH -= centerY;
37624 north.updateBox(this.safeBox(b));
37626 if(south && south.isVisible()){
37627 var b = south.getBox();
37628 var m = south.getMargins();
37629 b.width = w - (m.left+m.right);
37631 var totalHeight = (b.height + m.top + m.bottom);
37632 b.y = h - totalHeight + m.top;
37633 centerH -= totalHeight;
37634 south.updateBox(this.safeBox(b));
37636 if(west && west.isVisible()){
37637 var b = west.getBox();
37638 var m = west.getMargins();
37639 b.height = centerH - (m.top+m.bottom);
37641 b.y = centerY + m.top;
37642 var totalWidth = (b.width + m.left + m.right);
37643 centerX += totalWidth;
37644 centerW -= totalWidth;
37645 west.updateBox(this.safeBox(b));
37647 if(east && east.isVisible()){
37648 var b = east.getBox();
37649 var m = east.getMargins();
37650 b.height = centerH - (m.top+m.bottom);
37651 var totalWidth = (b.width + m.left + m.right);
37652 b.x = w - totalWidth + m.left;
37653 b.y = centerY + m.top;
37654 centerW -= totalWidth;
37655 east.updateBox(this.safeBox(b));
37658 var m = center.getMargins();
37660 x: centerX + m.left,
37661 y: centerY + m.top,
37662 width: centerW - (m.left+m.right),
37663 height: centerH - (m.top+m.bottom)
37665 //if(this.hideOnLayout){
37666 //center.el.setStyle("display", "block");
37668 center.updateBox(this.safeBox(centerBox));
37671 this.fireEvent("layout", this);
37675 safeBox : function(box){
37676 box.width = Math.max(0, box.width);
37677 box.height = Math.max(0, box.height);
37682 * Adds a ContentPanel (or subclass) to this layout.
37683 * @param {String} target The target region key (north, south, east, west or center).
37684 * @param {Roo.ContentPanel} panel The panel to add
37685 * @return {Roo.ContentPanel} The added panel
37687 add : function(target, panel){
37689 target = target.toLowerCase();
37690 return this.regions[target].add(panel);
37694 * Remove a ContentPanel (or subclass) to this layout.
37695 * @param {String} target The target region key (north, south, east, west or center).
37696 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37697 * @return {Roo.ContentPanel} The removed panel
37699 remove : function(target, panel){
37700 target = target.toLowerCase();
37701 return this.regions[target].remove(panel);
37705 * Searches all regions for a panel with the specified id
37706 * @param {String} panelId
37707 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37709 findPanel : function(panelId){
37710 var rs = this.regions;
37711 for(var target in rs){
37712 if(typeof rs[target] != "function"){
37713 var p = rs[target].getPanel(panelId);
37723 * Searches all regions for a panel with the specified id and activates (shows) it.
37724 * @param {String/ContentPanel} panelId The panels id or the panel itself
37725 * @return {Roo.ContentPanel} The shown panel or null
37727 showPanel : function(panelId) {
37728 var rs = this.regions;
37729 for(var target in rs){
37730 var r = rs[target];
37731 if(typeof r != "function"){
37732 if(r.hasPanel(panelId)){
37733 return r.showPanel(panelId);
37741 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37742 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37745 restoreState : function(provider){
37747 provider = Roo.state.Manager;
37749 var sm = new Roo.LayoutStateManager();
37750 sm.init(this, provider);
37756 * Adds a xtype elements to the layout.
37760 xtype : 'ContentPanel',
37767 xtype : 'NestedLayoutPanel',
37773 items : [ ... list of content panels or nested layout panels.. ]
37777 * @param {Object} cfg Xtype definition of item to add.
37779 addxtype : function(cfg)
37781 // basically accepts a pannel...
37782 // can accept a layout region..!?!?
37783 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37786 // theory? children can only be panels??
37788 //if (!cfg.xtype.match(/Panel$/)) {
37793 if (typeof(cfg.region) == 'undefined') {
37794 Roo.log("Failed to add Panel, region was not set");
37798 var region = cfg.region;
37804 xitems = cfg.items;
37809 if ( region == 'center') {
37810 Roo.log("Center: " + cfg.title);
37816 case 'Content': // ContentPanel (el, cfg)
37817 case 'Scroll': // ContentPanel (el, cfg)
37819 cfg.autoCreate = cfg.autoCreate || true;
37820 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37822 // var el = this.el.createChild();
37823 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37826 this.add(region, ret);
37830 case 'TreePanel': // our new panel!
37831 cfg.el = this.el.createChild();
37832 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37833 this.add(region, ret);
37838 // create a new Layout (which is a Border Layout...
37840 var clayout = cfg.layout;
37841 clayout.el = this.el.createChild();
37842 clayout.items = clayout.items || [];
37846 // replace this exitems with the clayout ones..
37847 xitems = clayout.items;
37849 // force background off if it's in center...
37850 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37851 cfg.background = false;
37853 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37856 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37857 //console.log('adding nested layout panel ' + cfg.toSource());
37858 this.add(region, ret);
37859 nb = {}; /// find first...
37864 // needs grid and region
37866 //var el = this.getRegion(region).el.createChild();
37868 *var el = this.el.createChild();
37869 // create the grid first...
37870 cfg.grid.container = el;
37871 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37874 if (region == 'center' && this.active ) {
37875 cfg.background = false;
37878 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37880 this.add(region, ret);
37882 if (cfg.background) {
37883 // render grid on panel activation (if panel background)
37884 ret.on('activate', function(gp) {
37885 if (!gp.grid.rendered) {
37886 // gp.grid.render(el);
37890 // cfg.grid.render(el);
37896 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37897 // it was the old xcomponent building that caused this before.
37898 // espeically if border is the top element in the tree.
37908 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37910 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37911 this.add(region, ret);
37915 throw "Can not add '" + cfg.xtype + "' to Border";
37921 this.beginUpdate();
37925 Roo.each(xitems, function(i) {
37926 region = nb && i.region ? i.region : false;
37928 var add = ret.addxtype(i);
37931 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37932 if (!i.background) {
37933 abn[region] = nb[region] ;
37940 // make the last non-background panel active..
37941 //if (nb) { Roo.log(abn); }
37944 for(var r in abn) {
37945 region = this.getRegion(r);
37947 // tried using nb[r], but it does not work..
37949 region.showPanel(abn[r]);
37960 factory : function(cfg)
37963 var validRegions = Roo.bootstrap.layout.Border.regions;
37965 var target = cfg.region;
37968 var r = Roo.bootstrap.layout;
37972 return new r.North(cfg);
37974 return new r.South(cfg);
37976 return new r.East(cfg);
37978 return new r.West(cfg);
37980 return new r.Center(cfg);
37982 throw 'Layout region "'+target+'" not supported.';
37989 * Ext JS Library 1.1.1
37990 * Copyright(c) 2006-2007, Ext JS, LLC.
37992 * Originally Released Under LGPL - original licence link has changed is not relivant.
37995 * <script type="text/javascript">
37999 * @class Roo.bootstrap.layout.Basic
38000 * @extends Roo.util.Observable
38001 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38002 * and does not have a titlebar, tabs or any other features. All it does is size and position
38003 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38004 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38005 * @cfg {string} region the region that it inhabits..
38006 * @cfg {bool} skipConfig skip config?
38010 Roo.bootstrap.layout.Basic = function(config){
38012 this.mgr = config.mgr;
38014 this.position = config.region;
38016 var skipConfig = config.skipConfig;
38020 * @scope Roo.BasicLayoutRegion
38024 * @event beforeremove
38025 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38026 * @param {Roo.LayoutRegion} this
38027 * @param {Roo.ContentPanel} panel The panel
38028 * @param {Object} e The cancel event object
38030 "beforeremove" : true,
38032 * @event invalidated
38033 * Fires when the layout for this region is changed.
38034 * @param {Roo.LayoutRegion} this
38036 "invalidated" : true,
38038 * @event visibilitychange
38039 * Fires when this region is shown or hidden
38040 * @param {Roo.LayoutRegion} this
38041 * @param {Boolean} visibility true or false
38043 "visibilitychange" : true,
38045 * @event paneladded
38046 * Fires when a panel is added.
38047 * @param {Roo.LayoutRegion} this
38048 * @param {Roo.ContentPanel} panel The panel
38050 "paneladded" : true,
38052 * @event panelremoved
38053 * Fires when a panel is removed.
38054 * @param {Roo.LayoutRegion} this
38055 * @param {Roo.ContentPanel} panel The panel
38057 "panelremoved" : true,
38059 * @event beforecollapse
38060 * Fires when this region before collapse.
38061 * @param {Roo.LayoutRegion} this
38063 "beforecollapse" : true,
38066 * Fires when this region is collapsed.
38067 * @param {Roo.LayoutRegion} this
38069 "collapsed" : true,
38072 * Fires when this region is expanded.
38073 * @param {Roo.LayoutRegion} this
38078 * Fires when this region is slid into view.
38079 * @param {Roo.LayoutRegion} this
38081 "slideshow" : true,
38084 * Fires when this region slides out of view.
38085 * @param {Roo.LayoutRegion} this
38087 "slidehide" : true,
38089 * @event panelactivated
38090 * Fires when a panel is activated.
38091 * @param {Roo.LayoutRegion} this
38092 * @param {Roo.ContentPanel} panel The activated panel
38094 "panelactivated" : true,
38097 * Fires when the user resizes this region.
38098 * @param {Roo.LayoutRegion} this
38099 * @param {Number} newSize The new size (width for east/west, height for north/south)
38103 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38104 this.panels = new Roo.util.MixedCollection();
38105 this.panels.getKey = this.getPanelId.createDelegate(this);
38107 this.activePanel = null;
38108 // ensure listeners are added...
38110 if (config.listeners || config.events) {
38111 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38112 listeners : config.listeners || {},
38113 events : config.events || {}
38117 if(skipConfig !== true){
38118 this.applyConfig(config);
38122 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38124 getPanelId : function(p){
38128 applyConfig : function(config){
38129 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38130 this.config = config;
38135 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38136 * the width, for horizontal (north, south) the height.
38137 * @param {Number} newSize The new width or height
38139 resizeTo : function(newSize){
38140 var el = this.el ? this.el :
38141 (this.activePanel ? this.activePanel.getEl() : null);
38143 switch(this.position){
38146 el.setWidth(newSize);
38147 this.fireEvent("resized", this, newSize);
38151 el.setHeight(newSize);
38152 this.fireEvent("resized", this, newSize);
38158 getBox : function(){
38159 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38162 getMargins : function(){
38163 return this.margins;
38166 updateBox : function(box){
38168 var el = this.activePanel.getEl();
38169 el.dom.style.left = box.x + "px";
38170 el.dom.style.top = box.y + "px";
38171 this.activePanel.setSize(box.width, box.height);
38175 * Returns the container element for this region.
38176 * @return {Roo.Element}
38178 getEl : function(){
38179 return this.activePanel;
38183 * Returns true if this region is currently visible.
38184 * @return {Boolean}
38186 isVisible : function(){
38187 return this.activePanel ? true : false;
38190 setActivePanel : function(panel){
38191 panel = this.getPanel(panel);
38192 if(this.activePanel && this.activePanel != panel){
38193 this.activePanel.setActiveState(false);
38194 this.activePanel.getEl().setLeftTop(-10000,-10000);
38196 this.activePanel = panel;
38197 panel.setActiveState(true);
38199 panel.setSize(this.box.width, this.box.height);
38201 this.fireEvent("panelactivated", this, panel);
38202 this.fireEvent("invalidated");
38206 * Show the specified panel.
38207 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38208 * @return {Roo.ContentPanel} The shown panel or null
38210 showPanel : function(panel){
38211 panel = this.getPanel(panel);
38213 this.setActivePanel(panel);
38219 * Get the active panel for this region.
38220 * @return {Roo.ContentPanel} The active panel or null
38222 getActivePanel : function(){
38223 return this.activePanel;
38227 * Add the passed ContentPanel(s)
38228 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38229 * @return {Roo.ContentPanel} The panel added (if only one was added)
38231 add : function(panel){
38232 if(arguments.length > 1){
38233 for(var i = 0, len = arguments.length; i < len; i++) {
38234 this.add(arguments[i]);
38238 if(this.hasPanel(panel)){
38239 this.showPanel(panel);
38242 var el = panel.getEl();
38243 if(el.dom.parentNode != this.mgr.el.dom){
38244 this.mgr.el.dom.appendChild(el.dom);
38246 if(panel.setRegion){
38247 panel.setRegion(this);
38249 this.panels.add(panel);
38250 el.setStyle("position", "absolute");
38251 if(!panel.background){
38252 this.setActivePanel(panel);
38253 if(this.config.initialSize && this.panels.getCount()==1){
38254 this.resizeTo(this.config.initialSize);
38257 this.fireEvent("paneladded", this, panel);
38262 * Returns true if the panel is in this region.
38263 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38264 * @return {Boolean}
38266 hasPanel : function(panel){
38267 if(typeof panel == "object"){ // must be panel obj
38268 panel = panel.getId();
38270 return this.getPanel(panel) ? true : false;
38274 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38275 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38276 * @param {Boolean} preservePanel Overrides the config preservePanel option
38277 * @return {Roo.ContentPanel} The panel that was removed
38279 remove : function(panel, preservePanel){
38280 panel = this.getPanel(panel);
38285 this.fireEvent("beforeremove", this, panel, e);
38286 if(e.cancel === true){
38289 var panelId = panel.getId();
38290 this.panels.removeKey(panelId);
38295 * Returns the panel specified or null if it's not in this region.
38296 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38297 * @return {Roo.ContentPanel}
38299 getPanel : function(id){
38300 if(typeof id == "object"){ // must be panel obj
38303 return this.panels.get(id);
38307 * Returns this regions position (north/south/east/west/center).
38310 getPosition: function(){
38311 return this.position;
38315 * Ext JS Library 1.1.1
38316 * Copyright(c) 2006-2007, Ext JS, LLC.
38318 * Originally Released Under LGPL - original licence link has changed is not relivant.
38321 * <script type="text/javascript">
38325 * @class Roo.bootstrap.layout.Region
38326 * @extends Roo.bootstrap.layout.Basic
38327 * This class represents a region in a layout manager.
38329 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38330 * @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})
38331 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38332 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38333 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38334 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38335 * @cfg {String} title The title for the region (overrides panel titles)
38336 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38337 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38338 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38339 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38340 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38341 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38342 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38343 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38344 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38345 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38347 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38348 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38349 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38350 * @cfg {Number} width For East/West panels
38351 * @cfg {Number} height For North/South panels
38352 * @cfg {Boolean} split To show the splitter
38353 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38355 * @cfg {string} cls Extra CSS classes to add to region
38357 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38358 * @cfg {string} region the region that it inhabits..
38361 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38362 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38364 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38365 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38366 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38368 Roo.bootstrap.layout.Region = function(config)
38370 this.applyConfig(config);
38372 var mgr = config.mgr;
38373 var pos = config.region;
38374 config.skipConfig = true;
38375 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38378 this.onRender(mgr.el);
38381 this.visible = true;
38382 this.collapsed = false;
38383 this.unrendered_panels = [];
38386 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38388 position: '', // set by wrapper (eg. north/south etc..)
38389 unrendered_panels : null, // unrendered panels.
38391 tabPosition : false,
38393 mgr: false, // points to 'Border'
38396 createBody : function(){
38397 /** This region's body element
38398 * @type Roo.Element */
38399 this.bodyEl = this.el.createChild({
38401 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38405 onRender: function(ctr, pos)
38407 var dh = Roo.DomHelper;
38408 /** This region's container element
38409 * @type Roo.Element */
38410 this.el = dh.append(ctr.dom, {
38412 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38414 /** This region's title element
38415 * @type Roo.Element */
38417 this.titleEl = dh.append(this.el.dom, {
38419 unselectable: "on",
38420 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38422 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38423 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38427 this.titleEl.enableDisplayMode();
38428 /** This region's title text element
38429 * @type HTMLElement */
38430 this.titleTextEl = this.titleEl.dom.firstChild;
38431 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38433 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38434 this.closeBtn.enableDisplayMode();
38435 this.closeBtn.on("click", this.closeClicked, this);
38436 this.closeBtn.hide();
38438 this.createBody(this.config);
38439 if(this.config.hideWhenEmpty){
38441 this.on("paneladded", this.validateVisibility, this);
38442 this.on("panelremoved", this.validateVisibility, this);
38444 if(this.autoScroll){
38445 this.bodyEl.setStyle("overflow", "auto");
38447 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38449 //if(c.titlebar !== false){
38450 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38451 this.titleEl.hide();
38453 this.titleEl.show();
38454 if(this.config.title){
38455 this.titleTextEl.innerHTML = this.config.title;
38459 if(this.config.collapsed){
38460 this.collapse(true);
38462 if(this.config.hidden){
38466 if (this.unrendered_panels && this.unrendered_panels.length) {
38467 for (var i =0;i< this.unrendered_panels.length; i++) {
38468 this.add(this.unrendered_panels[i]);
38470 this.unrendered_panels = null;
38476 applyConfig : function(c)
38479 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38480 var dh = Roo.DomHelper;
38481 if(c.titlebar !== false){
38482 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38483 this.collapseBtn.on("click", this.collapse, this);
38484 this.collapseBtn.enableDisplayMode();
38486 if(c.showPin === true || this.showPin){
38487 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38488 this.stickBtn.enableDisplayMode();
38489 this.stickBtn.on("click", this.expand, this);
38490 this.stickBtn.hide();
38495 /** This region's collapsed element
38496 * @type Roo.Element */
38499 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38500 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38503 if(c.floatable !== false){
38504 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38505 this.collapsedEl.on("click", this.collapseClick, this);
38508 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38509 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38510 id: "message", unselectable: "on", style:{"float":"left"}});
38511 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38513 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38514 this.expandBtn.on("click", this.expand, this);
38518 if(this.collapseBtn){
38519 this.collapseBtn.setVisible(c.collapsible == true);
38522 this.cmargins = c.cmargins || this.cmargins ||
38523 (this.position == "west" || this.position == "east" ?
38524 {top: 0, left: 2, right:2, bottom: 0} :
38525 {top: 2, left: 0, right:0, bottom: 2});
38527 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38530 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38532 this.autoScroll = c.autoScroll || false;
38537 this.duration = c.duration || .30;
38538 this.slideDuration = c.slideDuration || .45;
38543 * Returns true if this region is currently visible.
38544 * @return {Boolean}
38546 isVisible : function(){
38547 return this.visible;
38551 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38552 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38554 //setCollapsedTitle : function(title){
38555 // title = title || " ";
38556 // if(this.collapsedTitleTextEl){
38557 // this.collapsedTitleTextEl.innerHTML = title;
38561 getBox : function(){
38563 // if(!this.collapsed){
38564 b = this.el.getBox(false, true);
38566 // b = this.collapsedEl.getBox(false, true);
38571 getMargins : function(){
38572 return this.margins;
38573 //return this.collapsed ? this.cmargins : this.margins;
38576 highlight : function(){
38577 this.el.addClass("x-layout-panel-dragover");
38580 unhighlight : function(){
38581 this.el.removeClass("x-layout-panel-dragover");
38584 updateBox : function(box)
38586 if (!this.bodyEl) {
38587 return; // not rendered yet..
38591 if(!this.collapsed){
38592 this.el.dom.style.left = box.x + "px";
38593 this.el.dom.style.top = box.y + "px";
38594 this.updateBody(box.width, box.height);
38596 this.collapsedEl.dom.style.left = box.x + "px";
38597 this.collapsedEl.dom.style.top = box.y + "px";
38598 this.collapsedEl.setSize(box.width, box.height);
38601 this.tabs.autoSizeTabs();
38605 updateBody : function(w, h)
38608 this.el.setWidth(w);
38609 w -= this.el.getBorderWidth("rl");
38610 if(this.config.adjustments){
38611 w += this.config.adjustments[0];
38614 if(h !== null && h > 0){
38615 this.el.setHeight(h);
38616 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38617 h -= this.el.getBorderWidth("tb");
38618 if(this.config.adjustments){
38619 h += this.config.adjustments[1];
38621 this.bodyEl.setHeight(h);
38623 h = this.tabs.syncHeight(h);
38626 if(this.panelSize){
38627 w = w !== null ? w : this.panelSize.width;
38628 h = h !== null ? h : this.panelSize.height;
38630 if(this.activePanel){
38631 var el = this.activePanel.getEl();
38632 w = w !== null ? w : el.getWidth();
38633 h = h !== null ? h : el.getHeight();
38634 this.panelSize = {width: w, height: h};
38635 this.activePanel.setSize(w, h);
38637 if(Roo.isIE && this.tabs){
38638 this.tabs.el.repaint();
38643 * Returns the container element for this region.
38644 * @return {Roo.Element}
38646 getEl : function(){
38651 * Hides this region.
38654 //if(!this.collapsed){
38655 this.el.dom.style.left = "-2000px";
38658 // this.collapsedEl.dom.style.left = "-2000px";
38659 // this.collapsedEl.hide();
38661 this.visible = false;
38662 this.fireEvent("visibilitychange", this, false);
38666 * Shows this region if it was previously hidden.
38669 //if(!this.collapsed){
38672 // this.collapsedEl.show();
38674 this.visible = true;
38675 this.fireEvent("visibilitychange", this, true);
38678 closeClicked : function(){
38679 if(this.activePanel){
38680 this.remove(this.activePanel);
38684 collapseClick : function(e){
38686 e.stopPropagation();
38689 e.stopPropagation();
38695 * Collapses this region.
38696 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38699 collapse : function(skipAnim, skipCheck = false){
38700 if(this.collapsed) {
38704 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38706 this.collapsed = true;
38708 this.split.el.hide();
38710 if(this.config.animate && skipAnim !== true){
38711 this.fireEvent("invalidated", this);
38712 this.animateCollapse();
38714 this.el.setLocation(-20000,-20000);
38716 this.collapsedEl.show();
38717 this.fireEvent("collapsed", this);
38718 this.fireEvent("invalidated", this);
38724 animateCollapse : function(){
38729 * Expands this region if it was previously collapsed.
38730 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38731 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38734 expand : function(e, skipAnim){
38736 e.stopPropagation();
38738 if(!this.collapsed || this.el.hasActiveFx()) {
38742 this.afterSlideIn();
38745 this.collapsed = false;
38746 if(this.config.animate && skipAnim !== true){
38747 this.animateExpand();
38751 this.split.el.show();
38753 this.collapsedEl.setLocation(-2000,-2000);
38754 this.collapsedEl.hide();
38755 this.fireEvent("invalidated", this);
38756 this.fireEvent("expanded", this);
38760 animateExpand : function(){
38764 initTabs : function()
38766 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38768 var ts = new Roo.bootstrap.panel.Tabs({
38769 el: this.bodyEl.dom,
38771 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38772 disableTooltips: this.config.disableTabTips,
38773 toolbar : this.config.toolbar
38776 if(this.config.hideTabs){
38777 ts.stripWrap.setDisplayed(false);
38780 ts.resizeTabs = this.config.resizeTabs === true;
38781 ts.minTabWidth = this.config.minTabWidth || 40;
38782 ts.maxTabWidth = this.config.maxTabWidth || 250;
38783 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38784 ts.monitorResize = false;
38785 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38786 ts.bodyEl.addClass('roo-layout-tabs-body');
38787 this.panels.each(this.initPanelAsTab, this);
38790 initPanelAsTab : function(panel){
38791 var ti = this.tabs.addTab(
38795 this.config.closeOnTab && panel.isClosable(),
38798 if(panel.tabTip !== undefined){
38799 ti.setTooltip(panel.tabTip);
38801 ti.on("activate", function(){
38802 this.setActivePanel(panel);
38805 if(this.config.closeOnTab){
38806 ti.on("beforeclose", function(t, e){
38808 this.remove(panel);
38812 panel.tabItem = ti;
38817 updatePanelTitle : function(panel, title)
38819 if(this.activePanel == panel){
38820 this.updateTitle(title);
38823 var ti = this.tabs.getTab(panel.getEl().id);
38825 if(panel.tabTip !== undefined){
38826 ti.setTooltip(panel.tabTip);
38831 updateTitle : function(title){
38832 if(this.titleTextEl && !this.config.title){
38833 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38837 setActivePanel : function(panel)
38839 panel = this.getPanel(panel);
38840 if(this.activePanel && this.activePanel != panel){
38841 if(this.activePanel.setActiveState(false) === false){
38845 this.activePanel = panel;
38846 panel.setActiveState(true);
38847 if(this.panelSize){
38848 panel.setSize(this.panelSize.width, this.panelSize.height);
38851 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38853 this.updateTitle(panel.getTitle());
38855 this.fireEvent("invalidated", this);
38857 this.fireEvent("panelactivated", this, panel);
38861 * Shows the specified panel.
38862 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38863 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38865 showPanel : function(panel)
38867 panel = this.getPanel(panel);
38870 var tab = this.tabs.getTab(panel.getEl().id);
38871 if(tab.isHidden()){
38872 this.tabs.unhideTab(tab.id);
38876 this.setActivePanel(panel);
38883 * Get the active panel for this region.
38884 * @return {Roo.ContentPanel} The active panel or null
38886 getActivePanel : function(){
38887 return this.activePanel;
38890 validateVisibility : function(){
38891 if(this.panels.getCount() < 1){
38892 this.updateTitle(" ");
38893 this.closeBtn.hide();
38896 if(!this.isVisible()){
38903 * Adds the passed ContentPanel(s) to this region.
38904 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38905 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38907 add : function(panel)
38909 if(arguments.length > 1){
38910 for(var i = 0, len = arguments.length; i < len; i++) {
38911 this.add(arguments[i]);
38916 // if we have not been rendered yet, then we can not really do much of this..
38917 if (!this.bodyEl) {
38918 this.unrendered_panels.push(panel);
38925 if(this.hasPanel(panel)){
38926 this.showPanel(panel);
38929 panel.setRegion(this);
38930 this.panels.add(panel);
38931 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38932 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38933 // and hide them... ???
38934 this.bodyEl.dom.appendChild(panel.getEl().dom);
38935 if(panel.background !== true){
38936 this.setActivePanel(panel);
38938 this.fireEvent("paneladded", this, panel);
38945 this.initPanelAsTab(panel);
38949 if(panel.background !== true){
38950 this.tabs.activate(panel.getEl().id);
38952 this.fireEvent("paneladded", this, panel);
38957 * Hides the tab for the specified panel.
38958 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38960 hidePanel : function(panel){
38961 if(this.tabs && (panel = this.getPanel(panel))){
38962 this.tabs.hideTab(panel.getEl().id);
38967 * Unhides the tab for a previously hidden panel.
38968 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38970 unhidePanel : function(panel){
38971 if(this.tabs && (panel = this.getPanel(panel))){
38972 this.tabs.unhideTab(panel.getEl().id);
38976 clearPanels : function(){
38977 while(this.panels.getCount() > 0){
38978 this.remove(this.panels.first());
38983 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38984 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38985 * @param {Boolean} preservePanel Overrides the config preservePanel option
38986 * @return {Roo.ContentPanel} The panel that was removed
38988 remove : function(panel, preservePanel)
38990 panel = this.getPanel(panel);
38995 this.fireEvent("beforeremove", this, panel, e);
38996 if(e.cancel === true){
38999 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39000 var panelId = panel.getId();
39001 this.panels.removeKey(panelId);
39003 document.body.appendChild(panel.getEl().dom);
39006 this.tabs.removeTab(panel.getEl().id);
39007 }else if (!preservePanel){
39008 this.bodyEl.dom.removeChild(panel.getEl().dom);
39010 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39011 var p = this.panels.first();
39012 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39013 tempEl.appendChild(p.getEl().dom);
39014 this.bodyEl.update("");
39015 this.bodyEl.dom.appendChild(p.getEl().dom);
39017 this.updateTitle(p.getTitle());
39019 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39020 this.setActivePanel(p);
39022 panel.setRegion(null);
39023 if(this.activePanel == panel){
39024 this.activePanel = null;
39026 if(this.config.autoDestroy !== false && preservePanel !== true){
39027 try{panel.destroy();}catch(e){}
39029 this.fireEvent("panelremoved", this, panel);
39034 * Returns the TabPanel component used by this region
39035 * @return {Roo.TabPanel}
39037 getTabs : function(){
39041 createTool : function(parentEl, className){
39042 var btn = Roo.DomHelper.append(parentEl, {
39044 cls: "x-layout-tools-button",
39047 cls: "roo-layout-tools-button-inner " + className,
39051 btn.addClassOnOver("roo-layout-tools-button-over");
39056 * Ext JS Library 1.1.1
39057 * Copyright(c) 2006-2007, Ext JS, LLC.
39059 * Originally Released Under LGPL - original licence link has changed is not relivant.
39062 * <script type="text/javascript">
39068 * @class Roo.SplitLayoutRegion
39069 * @extends Roo.LayoutRegion
39070 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39072 Roo.bootstrap.layout.Split = function(config){
39073 this.cursor = config.cursor;
39074 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39077 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39079 splitTip : "Drag to resize.",
39080 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39081 useSplitTips : false,
39083 applyConfig : function(config){
39084 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39087 onRender : function(ctr,pos) {
39089 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39090 if(!this.config.split){
39095 var splitEl = Roo.DomHelper.append(ctr.dom, {
39097 id: this.el.id + "-split",
39098 cls: "roo-layout-split roo-layout-split-"+this.position,
39101 /** The SplitBar for this region
39102 * @type Roo.SplitBar */
39103 // does not exist yet...
39104 Roo.log([this.position, this.orientation]);
39106 this.split = new Roo.bootstrap.SplitBar({
39107 dragElement : splitEl,
39108 resizingElement: this.el,
39109 orientation : this.orientation
39112 this.split.on("moved", this.onSplitMove, this);
39113 this.split.useShim = this.config.useShim === true;
39114 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39115 if(this.useSplitTips){
39116 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39118 //if(config.collapsible){
39119 // this.split.el.on("dblclick", this.collapse, this);
39122 if(typeof this.config.minSize != "undefined"){
39123 this.split.minSize = this.config.minSize;
39125 if(typeof this.config.maxSize != "undefined"){
39126 this.split.maxSize = this.config.maxSize;
39128 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39129 this.hideSplitter();
39134 getHMaxSize : function(){
39135 var cmax = this.config.maxSize || 10000;
39136 var center = this.mgr.getRegion("center");
39137 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39140 getVMaxSize : function(){
39141 var cmax = this.config.maxSize || 10000;
39142 var center = this.mgr.getRegion("center");
39143 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39146 onSplitMove : function(split, newSize){
39147 this.fireEvent("resized", this, newSize);
39151 * Returns the {@link Roo.SplitBar} for this region.
39152 * @return {Roo.SplitBar}
39154 getSplitBar : function(){
39159 this.hideSplitter();
39160 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39163 hideSplitter : function(){
39165 this.split.el.setLocation(-2000,-2000);
39166 this.split.el.hide();
39172 this.split.el.show();
39174 Roo.bootstrap.layout.Split.superclass.show.call(this);
39177 beforeSlide: function(){
39178 if(Roo.isGecko){// firefox overflow auto bug workaround
39179 this.bodyEl.clip();
39181 this.tabs.bodyEl.clip();
39183 if(this.activePanel){
39184 this.activePanel.getEl().clip();
39186 if(this.activePanel.beforeSlide){
39187 this.activePanel.beforeSlide();
39193 afterSlide : function(){
39194 if(Roo.isGecko){// firefox overflow auto bug workaround
39195 this.bodyEl.unclip();
39197 this.tabs.bodyEl.unclip();
39199 if(this.activePanel){
39200 this.activePanel.getEl().unclip();
39201 if(this.activePanel.afterSlide){
39202 this.activePanel.afterSlide();
39208 initAutoHide : function(){
39209 if(this.autoHide !== false){
39210 if(!this.autoHideHd){
39211 var st = new Roo.util.DelayedTask(this.slideIn, this);
39212 this.autoHideHd = {
39213 "mouseout": function(e){
39214 if(!e.within(this.el, true)){
39218 "mouseover" : function(e){
39224 this.el.on(this.autoHideHd);
39228 clearAutoHide : function(){
39229 if(this.autoHide !== false){
39230 this.el.un("mouseout", this.autoHideHd.mouseout);
39231 this.el.un("mouseover", this.autoHideHd.mouseover);
39235 clearMonitor : function(){
39236 Roo.get(document).un("click", this.slideInIf, this);
39239 // these names are backwards but not changed for compat
39240 slideOut : function(){
39241 if(this.isSlid || this.el.hasActiveFx()){
39244 this.isSlid = true;
39245 if(this.collapseBtn){
39246 this.collapseBtn.hide();
39248 this.closeBtnState = this.closeBtn.getStyle('display');
39249 this.closeBtn.hide();
39251 this.stickBtn.show();
39254 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39255 this.beforeSlide();
39256 this.el.setStyle("z-index", 10001);
39257 this.el.slideIn(this.getSlideAnchor(), {
39258 callback: function(){
39260 this.initAutoHide();
39261 Roo.get(document).on("click", this.slideInIf, this);
39262 this.fireEvent("slideshow", this);
39269 afterSlideIn : function(){
39270 this.clearAutoHide();
39271 this.isSlid = false;
39272 this.clearMonitor();
39273 this.el.setStyle("z-index", "");
39274 if(this.collapseBtn){
39275 this.collapseBtn.show();
39277 this.closeBtn.setStyle('display', this.closeBtnState);
39279 this.stickBtn.hide();
39281 this.fireEvent("slidehide", this);
39284 slideIn : function(cb){
39285 if(!this.isSlid || this.el.hasActiveFx()){
39289 this.isSlid = false;
39290 this.beforeSlide();
39291 this.el.slideOut(this.getSlideAnchor(), {
39292 callback: function(){
39293 this.el.setLeftTop(-10000, -10000);
39295 this.afterSlideIn();
39303 slideInIf : function(e){
39304 if(!e.within(this.el)){
39309 animateCollapse : function(){
39310 this.beforeSlide();
39311 this.el.setStyle("z-index", 20000);
39312 var anchor = this.getSlideAnchor();
39313 this.el.slideOut(anchor, {
39314 callback : function(){
39315 this.el.setStyle("z-index", "");
39316 this.collapsedEl.slideIn(anchor, {duration:.3});
39318 this.el.setLocation(-10000,-10000);
39320 this.fireEvent("collapsed", this);
39327 animateExpand : function(){
39328 this.beforeSlide();
39329 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39330 this.el.setStyle("z-index", 20000);
39331 this.collapsedEl.hide({
39334 this.el.slideIn(this.getSlideAnchor(), {
39335 callback : function(){
39336 this.el.setStyle("z-index", "");
39339 this.split.el.show();
39341 this.fireEvent("invalidated", this);
39342 this.fireEvent("expanded", this);
39370 getAnchor : function(){
39371 return this.anchors[this.position];
39374 getCollapseAnchor : function(){
39375 return this.canchors[this.position];
39378 getSlideAnchor : function(){
39379 return this.sanchors[this.position];
39382 getAlignAdj : function(){
39383 var cm = this.cmargins;
39384 switch(this.position){
39400 getExpandAdj : function(){
39401 var c = this.collapsedEl, cm = this.cmargins;
39402 switch(this.position){
39404 return [-(cm.right+c.getWidth()+cm.left), 0];
39407 return [cm.right+c.getWidth()+cm.left, 0];
39410 return [0, -(cm.top+cm.bottom+c.getHeight())];
39413 return [0, cm.top+cm.bottom+c.getHeight()];
39419 * Ext JS Library 1.1.1
39420 * Copyright(c) 2006-2007, Ext JS, LLC.
39422 * Originally Released Under LGPL - original licence link has changed is not relivant.
39425 * <script type="text/javascript">
39428 * These classes are private internal classes
39430 Roo.bootstrap.layout.Center = function(config){
39431 config.region = "center";
39432 Roo.bootstrap.layout.Region.call(this, config);
39433 this.visible = true;
39434 this.minWidth = config.minWidth || 20;
39435 this.minHeight = config.minHeight || 20;
39438 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39440 // center panel can't be hidden
39444 // center panel can't be hidden
39447 getMinWidth: function(){
39448 return this.minWidth;
39451 getMinHeight: function(){
39452 return this.minHeight;
39466 Roo.bootstrap.layout.North = function(config)
39468 config.region = 'north';
39469 config.cursor = 'n-resize';
39471 Roo.bootstrap.layout.Split.call(this, config);
39475 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39476 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39477 this.split.el.addClass("roo-layout-split-v");
39479 //var size = config.initialSize || config.height;
39480 //if(this.el && typeof size != "undefined"){
39481 // this.el.setHeight(size);
39484 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39486 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39489 onRender : function(ctr, pos)
39491 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39492 var size = this.config.initialSize || this.config.height;
39493 if(this.el && typeof size != "undefined"){
39494 this.el.setHeight(size);
39499 getBox : function(){
39500 if(this.collapsed){
39501 return this.collapsedEl.getBox();
39503 var box = this.el.getBox();
39505 box.height += this.split.el.getHeight();
39510 updateBox : function(box){
39511 if(this.split && !this.collapsed){
39512 box.height -= this.split.el.getHeight();
39513 this.split.el.setLeft(box.x);
39514 this.split.el.setTop(box.y+box.height);
39515 this.split.el.setWidth(box.width);
39517 if(this.collapsed){
39518 this.updateBody(box.width, null);
39520 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39528 Roo.bootstrap.layout.South = function(config){
39529 config.region = 'south';
39530 config.cursor = 's-resize';
39531 Roo.bootstrap.layout.Split.call(this, config);
39533 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39534 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39535 this.split.el.addClass("roo-layout-split-v");
39540 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39541 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39543 onRender : function(ctr, pos)
39545 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39546 var size = this.config.initialSize || this.config.height;
39547 if(this.el && typeof size != "undefined"){
39548 this.el.setHeight(size);
39553 getBox : function(){
39554 if(this.collapsed){
39555 return this.collapsedEl.getBox();
39557 var box = this.el.getBox();
39559 var sh = this.split.el.getHeight();
39566 updateBox : function(box){
39567 if(this.split && !this.collapsed){
39568 var sh = this.split.el.getHeight();
39571 this.split.el.setLeft(box.x);
39572 this.split.el.setTop(box.y-sh);
39573 this.split.el.setWidth(box.width);
39575 if(this.collapsed){
39576 this.updateBody(box.width, null);
39578 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39582 Roo.bootstrap.layout.East = function(config){
39583 config.region = "east";
39584 config.cursor = "e-resize";
39585 Roo.bootstrap.layout.Split.call(this, config);
39587 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39588 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39589 this.split.el.addClass("roo-layout-split-h");
39593 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39594 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39596 onRender : function(ctr, pos)
39598 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39599 var size = this.config.initialSize || this.config.width;
39600 if(this.el && typeof size != "undefined"){
39601 this.el.setWidth(size);
39606 getBox : function(){
39607 if(this.collapsed){
39608 return this.collapsedEl.getBox();
39610 var box = this.el.getBox();
39612 var sw = this.split.el.getWidth();
39619 updateBox : function(box){
39620 if(this.split && !this.collapsed){
39621 var sw = this.split.el.getWidth();
39623 this.split.el.setLeft(box.x);
39624 this.split.el.setTop(box.y);
39625 this.split.el.setHeight(box.height);
39628 if(this.collapsed){
39629 this.updateBody(null, box.height);
39631 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39635 Roo.bootstrap.layout.West = function(config){
39636 config.region = "west";
39637 config.cursor = "w-resize";
39639 Roo.bootstrap.layout.Split.call(this, config);
39641 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39642 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39643 this.split.el.addClass("roo-layout-split-h");
39647 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39648 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39650 onRender: function(ctr, pos)
39652 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39653 var size = this.config.initialSize || this.config.width;
39654 if(typeof size != "undefined"){
39655 this.el.setWidth(size);
39659 getBox : function(){
39660 if(this.collapsed){
39661 return this.collapsedEl.getBox();
39663 var box = this.el.getBox();
39664 if (box.width == 0) {
39665 box.width = this.config.width; // kludge?
39668 box.width += this.split.el.getWidth();
39673 updateBox : function(box){
39674 if(this.split && !this.collapsed){
39675 var sw = this.split.el.getWidth();
39677 this.split.el.setLeft(box.x+box.width);
39678 this.split.el.setTop(box.y);
39679 this.split.el.setHeight(box.height);
39681 if(this.collapsed){
39682 this.updateBody(null, box.height);
39684 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39686 });Roo.namespace("Roo.bootstrap.panel");/*
39688 * Ext JS Library 1.1.1
39689 * Copyright(c) 2006-2007, Ext JS, LLC.
39691 * Originally Released Under LGPL - original licence link has changed is not relivant.
39694 * <script type="text/javascript">
39697 * @class Roo.ContentPanel
39698 * @extends Roo.util.Observable
39699 * A basic ContentPanel element.
39700 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39701 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39702 * @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
39703 * @cfg {Boolean} closable True if the panel can be closed/removed
39704 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39705 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39706 * @cfg {Toolbar} toolbar A toolbar for this panel
39707 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39708 * @cfg {String} title The title for this panel
39709 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39710 * @cfg {String} url Calls {@link #setUrl} with this value
39711 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39712 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39713 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39714 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39715 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39716 * @cfg {Boolean} badges render the badges
39717 * @cfg {String} cls extra classes to use
39718 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39721 * Create a new ContentPanel.
39722 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39723 * @param {String/Object} config A string to set only the title or a config object
39724 * @param {String} content (optional) Set the HTML content for this panel
39725 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39727 Roo.bootstrap.panel.Content = function( config){
39729 this.tpl = config.tpl || false;
39731 var el = config.el;
39732 var content = config.content;
39734 if(config.autoCreate){ // xtype is available if this is called from factory
39737 this.el = Roo.get(el);
39738 if(!this.el && config && config.autoCreate){
39739 if(typeof config.autoCreate == "object"){
39740 if(!config.autoCreate.id){
39741 config.autoCreate.id = config.id||el;
39743 this.el = Roo.DomHelper.append(document.body,
39744 config.autoCreate, true);
39748 cls: (config.cls || '') +
39749 (config.background ? ' bg-' + config.background : '') +
39750 " roo-layout-inactive-content",
39753 if (config.iframe) {
39757 style : 'border: 0px',
39758 src : 'about:blank'
39764 elcfg.html = config.html;
39768 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39769 if (config.iframe) {
39770 this.iframeEl = this.el.select('iframe',true).first();
39775 this.closable = false;
39776 this.loaded = false;
39777 this.active = false;
39780 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39782 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39784 this.wrapEl = this.el; //this.el.wrap();
39786 if (config.toolbar.items) {
39787 ti = config.toolbar.items ;
39788 delete config.toolbar.items ;
39792 this.toolbar.render(this.wrapEl, 'before');
39793 for(var i =0;i < ti.length;i++) {
39794 // Roo.log(['add child', items[i]]);
39795 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39797 this.toolbar.items = nitems;
39798 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39799 delete config.toolbar;
39803 // xtype created footer. - not sure if will work as we normally have to render first..
39804 if (this.footer && !this.footer.el && this.footer.xtype) {
39805 if (!this.wrapEl) {
39806 this.wrapEl = this.el.wrap();
39809 this.footer.container = this.wrapEl.createChild();
39811 this.footer = Roo.factory(this.footer, Roo);
39816 if(typeof config == "string"){
39817 this.title = config;
39819 Roo.apply(this, config);
39823 this.resizeEl = Roo.get(this.resizeEl, true);
39825 this.resizeEl = this.el;
39827 // handle view.xtype
39835 * Fires when this panel is activated.
39836 * @param {Roo.ContentPanel} this
39840 * @event deactivate
39841 * Fires when this panel is activated.
39842 * @param {Roo.ContentPanel} this
39844 "deactivate" : true,
39848 * Fires when this panel is resized if fitToFrame is true.
39849 * @param {Roo.ContentPanel} this
39850 * @param {Number} width The width after any component adjustments
39851 * @param {Number} height The height after any component adjustments
39857 * Fires when this tab is created
39858 * @param {Roo.ContentPanel} this
39869 if(this.autoScroll && !this.iframe){
39870 this.resizeEl.setStyle("overflow", "auto");
39872 // fix randome scrolling
39873 //this.el.on('scroll', function() {
39874 // Roo.log('fix random scolling');
39875 // this.scrollTo('top',0);
39878 content = content || this.content;
39880 this.setContent(content);
39882 if(config && config.url){
39883 this.setUrl(this.url, this.params, this.loadOnce);
39888 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39890 if (this.view && typeof(this.view.xtype) != 'undefined') {
39891 this.view.el = this.el.appendChild(document.createElement("div"));
39892 this.view = Roo.factory(this.view);
39893 this.view.render && this.view.render(false, '');
39897 this.fireEvent('render', this);
39900 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39910 setRegion : function(region){
39911 this.region = region;
39912 this.setActiveClass(region && !this.background);
39916 setActiveClass: function(state)
39919 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39920 this.el.setStyle('position','relative');
39922 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39923 this.el.setStyle('position', 'absolute');
39928 * Returns the toolbar for this Panel if one was configured.
39929 * @return {Roo.Toolbar}
39931 getToolbar : function(){
39932 return this.toolbar;
39935 setActiveState : function(active)
39937 this.active = active;
39938 this.setActiveClass(active);
39940 if(this.fireEvent("deactivate", this) === false){
39945 this.fireEvent("activate", this);
39949 * Updates this panel's element (not for iframe)
39950 * @param {String} content The new content
39951 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39953 setContent : function(content, loadScripts){
39958 this.el.update(content, loadScripts);
39961 ignoreResize : function(w, h){
39962 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39965 this.lastSize = {width: w, height: h};
39970 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39971 * @return {Roo.UpdateManager} The UpdateManager
39973 getUpdateManager : function(){
39977 return this.el.getUpdateManager();
39980 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39981 * Does not work with IFRAME contents
39982 * @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:
39985 url: "your-url.php",
39986 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39987 callback: yourFunction,
39988 scope: yourObject, //(optional scope)
39991 text: "Loading...",
39997 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39998 * 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.
39999 * @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}
40000 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40001 * @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.
40002 * @return {Roo.ContentPanel} this
40010 var um = this.el.getUpdateManager();
40011 um.update.apply(um, arguments);
40017 * 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.
40018 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40019 * @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)
40020 * @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)
40021 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40023 setUrl : function(url, params, loadOnce){
40025 this.iframeEl.dom.src = url;
40029 if(this.refreshDelegate){
40030 this.removeListener("activate", this.refreshDelegate);
40032 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40033 this.on("activate", this.refreshDelegate);
40034 return this.el.getUpdateManager();
40037 _handleRefresh : function(url, params, loadOnce){
40038 if(!loadOnce || !this.loaded){
40039 var updater = this.el.getUpdateManager();
40040 updater.update(url, params, this._setLoaded.createDelegate(this));
40044 _setLoaded : function(){
40045 this.loaded = true;
40049 * Returns this panel's id
40052 getId : function(){
40057 * Returns this panel's element - used by regiosn to add.
40058 * @return {Roo.Element}
40060 getEl : function(){
40061 return this.wrapEl || this.el;
40066 adjustForComponents : function(width, height)
40068 //Roo.log('adjustForComponents ');
40069 if(this.resizeEl != this.el){
40070 width -= this.el.getFrameWidth('lr');
40071 height -= this.el.getFrameWidth('tb');
40074 var te = this.toolbar.getEl();
40075 te.setWidth(width);
40076 height -= te.getHeight();
40079 var te = this.footer.getEl();
40080 te.setWidth(width);
40081 height -= te.getHeight();
40085 if(this.adjustments){
40086 width += this.adjustments[0];
40087 height += this.adjustments[1];
40089 return {"width": width, "height": height};
40092 setSize : function(width, height){
40093 if(this.fitToFrame && !this.ignoreResize(width, height)){
40094 if(this.fitContainer && this.resizeEl != this.el){
40095 this.el.setSize(width, height);
40097 var size = this.adjustForComponents(width, height);
40099 this.iframeEl.setSize(width,height);
40102 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40103 this.fireEvent('resize', this, size.width, size.height);
40110 * Returns this panel's title
40113 getTitle : function(){
40115 if (typeof(this.title) != 'object') {
40120 for (var k in this.title) {
40121 if (!this.title.hasOwnProperty(k)) {
40125 if (k.indexOf('-') >= 0) {
40126 var s = k.split('-');
40127 for (var i = 0; i<s.length; i++) {
40128 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40131 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40138 * Set this panel's title
40139 * @param {String} title
40141 setTitle : function(title){
40142 this.title = title;
40144 this.region.updatePanelTitle(this, title);
40149 * Returns true is this panel was configured to be closable
40150 * @return {Boolean}
40152 isClosable : function(){
40153 return this.closable;
40156 beforeSlide : function(){
40158 this.resizeEl.clip();
40161 afterSlide : function(){
40163 this.resizeEl.unclip();
40167 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40168 * Will fail silently if the {@link #setUrl} method has not been called.
40169 * This does not activate the panel, just updates its content.
40171 refresh : function(){
40172 if(this.refreshDelegate){
40173 this.loaded = false;
40174 this.refreshDelegate();
40179 * Destroys this panel
40181 destroy : function(){
40182 this.el.removeAllListeners();
40183 var tempEl = document.createElement("span");
40184 tempEl.appendChild(this.el.dom);
40185 tempEl.innerHTML = "";
40191 * form - if the content panel contains a form - this is a reference to it.
40192 * @type {Roo.form.Form}
40196 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40197 * This contains a reference to it.
40203 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40213 * @param {Object} cfg Xtype definition of item to add.
40217 getChildContainer: function () {
40218 return this.getEl();
40223 var ret = new Roo.factory(cfg);
40228 if (cfg.xtype.match(/^Form$/)) {
40231 //if (this.footer) {
40232 // el = this.footer.container.insertSibling(false, 'before');
40234 el = this.el.createChild();
40237 this.form = new Roo.form.Form(cfg);
40240 if ( this.form.allItems.length) {
40241 this.form.render(el.dom);
40245 // should only have one of theses..
40246 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40247 // views.. should not be just added - used named prop 'view''
40249 cfg.el = this.el.appendChild(document.createElement("div"));
40252 var ret = new Roo.factory(cfg);
40254 ret.render && ret.render(false, ''); // render blank..
40264 * @class Roo.bootstrap.panel.Grid
40265 * @extends Roo.bootstrap.panel.Content
40267 * Create a new GridPanel.
40268 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40269 * @param {Object} config A the config object
40275 Roo.bootstrap.panel.Grid = function(config)
40279 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40280 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40282 config.el = this.wrapper;
40283 //this.el = this.wrapper;
40285 if (config.container) {
40286 // ctor'ed from a Border/panel.grid
40289 this.wrapper.setStyle("overflow", "hidden");
40290 this.wrapper.addClass('roo-grid-container');
40295 if(config.toolbar){
40296 var tool_el = this.wrapper.createChild();
40297 this.toolbar = Roo.factory(config.toolbar);
40299 if (config.toolbar.items) {
40300 ti = config.toolbar.items ;
40301 delete config.toolbar.items ;
40305 this.toolbar.render(tool_el);
40306 for(var i =0;i < ti.length;i++) {
40307 // Roo.log(['add child', items[i]]);
40308 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40310 this.toolbar.items = nitems;
40312 delete config.toolbar;
40315 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40316 config.grid.scrollBody = true;;
40317 config.grid.monitorWindowResize = false; // turn off autosizing
40318 config.grid.autoHeight = false;
40319 config.grid.autoWidth = false;
40321 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40323 if (config.background) {
40324 // render grid on panel activation (if panel background)
40325 this.on('activate', function(gp) {
40326 if (!gp.grid.rendered) {
40327 gp.grid.render(this.wrapper);
40328 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40333 this.grid.render(this.wrapper);
40334 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40337 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40338 // ??? needed ??? config.el = this.wrapper;
40343 // xtype created footer. - not sure if will work as we normally have to render first..
40344 if (this.footer && !this.footer.el && this.footer.xtype) {
40346 var ctr = this.grid.getView().getFooterPanel(true);
40347 this.footer.dataSource = this.grid.dataSource;
40348 this.footer = Roo.factory(this.footer, Roo);
40349 this.footer.render(ctr);
40359 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40360 getId : function(){
40361 return this.grid.id;
40365 * Returns the grid for this panel
40366 * @return {Roo.bootstrap.Table}
40368 getGrid : function(){
40372 setSize : function(width, height){
40373 if(!this.ignoreResize(width, height)){
40374 var grid = this.grid;
40375 var size = this.adjustForComponents(width, height);
40376 // tfoot is not a footer?
40379 var gridel = grid.getGridEl();
40380 gridel.setSize(size.width, size.height);
40382 var tbd = grid.getGridEl().select('tbody', true).first();
40383 var thd = grid.getGridEl().select('thead',true).first();
40384 var tbf= grid.getGridEl().select('tfoot', true).first();
40387 size.height -= tbf.getHeight();
40390 size.height -= thd.getHeight();
40393 tbd.setSize(size.width, size.height );
40394 // this is for the account management tab -seems to work there.
40395 var thd = grid.getGridEl().select('thead',true).first();
40397 // tbd.setSize(size.width, size.height - thd.getHeight());
40406 beforeSlide : function(){
40407 this.grid.getView().scroller.clip();
40410 afterSlide : function(){
40411 this.grid.getView().scroller.unclip();
40414 destroy : function(){
40415 this.grid.destroy();
40417 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40422 * @class Roo.bootstrap.panel.Nest
40423 * @extends Roo.bootstrap.panel.Content
40425 * Create a new Panel, that can contain a layout.Border.
40428 * @param {Roo.BorderLayout} layout The layout for this panel
40429 * @param {String/Object} config A string to set only the title or a config object
40431 Roo.bootstrap.panel.Nest = function(config)
40433 // construct with only one argument..
40434 /* FIXME - implement nicer consturctors
40435 if (layout.layout) {
40437 layout = config.layout;
40438 delete config.layout;
40440 if (layout.xtype && !layout.getEl) {
40441 // then layout needs constructing..
40442 layout = Roo.factory(layout, Roo);
40446 config.el = config.layout.getEl();
40448 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40450 config.layout.monitorWindowResize = false; // turn off autosizing
40451 this.layout = config.layout;
40452 this.layout.getEl().addClass("roo-layout-nested-layout");
40453 this.layout.parent = this;
40460 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40462 setSize : function(width, height){
40463 if(!this.ignoreResize(width, height)){
40464 var size = this.adjustForComponents(width, height);
40465 var el = this.layout.getEl();
40466 if (size.height < 1) {
40467 el.setWidth(size.width);
40469 el.setSize(size.width, size.height);
40471 var touch = el.dom.offsetWidth;
40472 this.layout.layout();
40473 // ie requires a double layout on the first pass
40474 if(Roo.isIE && !this.initialized){
40475 this.initialized = true;
40476 this.layout.layout();
40481 // activate all subpanels if not currently active..
40483 setActiveState : function(active){
40484 this.active = active;
40485 this.setActiveClass(active);
40488 this.fireEvent("deactivate", this);
40492 this.fireEvent("activate", this);
40493 // not sure if this should happen before or after..
40494 if (!this.layout) {
40495 return; // should not happen..
40498 for (var r in this.layout.regions) {
40499 reg = this.layout.getRegion(r);
40500 if (reg.getActivePanel()) {
40501 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40502 reg.setActivePanel(reg.getActivePanel());
40505 if (!reg.panels.length) {
40508 reg.showPanel(reg.getPanel(0));
40517 * Returns the nested BorderLayout for this panel
40518 * @return {Roo.BorderLayout}
40520 getLayout : function(){
40521 return this.layout;
40525 * Adds a xtype elements to the layout of the nested panel
40529 xtype : 'ContentPanel',
40536 xtype : 'NestedLayoutPanel',
40542 items : [ ... list of content panels or nested layout panels.. ]
40546 * @param {Object} cfg Xtype definition of item to add.
40548 addxtype : function(cfg) {
40549 return this.layout.addxtype(cfg);
40554 * Ext JS Library 1.1.1
40555 * Copyright(c) 2006-2007, Ext JS, LLC.
40557 * Originally Released Under LGPL - original licence link has changed is not relivant.
40560 * <script type="text/javascript">
40563 * @class Roo.TabPanel
40564 * @extends Roo.util.Observable
40565 * A lightweight tab container.
40569 // basic tabs 1, built from existing content
40570 var tabs = new Roo.TabPanel("tabs1");
40571 tabs.addTab("script", "View Script");
40572 tabs.addTab("markup", "View Markup");
40573 tabs.activate("script");
40575 // more advanced tabs, built from javascript
40576 var jtabs = new Roo.TabPanel("jtabs");
40577 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40579 // set up the UpdateManager
40580 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40581 var updater = tab2.getUpdateManager();
40582 updater.setDefaultUrl("ajax1.htm");
40583 tab2.on('activate', updater.refresh, updater, true);
40585 // Use setUrl for Ajax loading
40586 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40587 tab3.setUrl("ajax2.htm", null, true);
40590 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40593 jtabs.activate("jtabs-1");
40596 * Create a new TabPanel.
40597 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40598 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40600 Roo.bootstrap.panel.Tabs = function(config){
40602 * The container element for this TabPanel.
40603 * @type Roo.Element
40605 this.el = Roo.get(config.el);
40608 if(typeof config == "boolean"){
40609 this.tabPosition = config ? "bottom" : "top";
40611 Roo.apply(this, config);
40615 if(this.tabPosition == "bottom"){
40616 // if tabs are at the bottom = create the body first.
40617 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40618 this.el.addClass("roo-tabs-bottom");
40620 // next create the tabs holders
40622 if (this.tabPosition == "west"){
40624 var reg = this.region; // fake it..
40626 if (!reg.mgr.parent) {
40629 reg = reg.mgr.parent.region;
40631 Roo.log("got nest?");
40633 if (reg.mgr.getRegion('west')) {
40634 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40635 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40636 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40637 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40638 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40646 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40647 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40648 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40649 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40654 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40657 // finally - if tabs are at the top, then create the body last..
40658 if(this.tabPosition != "bottom"){
40659 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40660 * @type Roo.Element
40662 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40663 this.el.addClass("roo-tabs-top");
40667 this.bodyEl.setStyle("position", "relative");
40669 this.active = null;
40670 this.activateDelegate = this.activate.createDelegate(this);
40675 * Fires when the active tab changes
40676 * @param {Roo.TabPanel} this
40677 * @param {Roo.TabPanelItem} activePanel The new active tab
40681 * @event beforetabchange
40682 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40683 * @param {Roo.TabPanel} this
40684 * @param {Object} e Set cancel to true on this object to cancel the tab change
40685 * @param {Roo.TabPanelItem} tab The tab being changed to
40687 "beforetabchange" : true
40690 Roo.EventManager.onWindowResize(this.onResize, this);
40691 this.cpad = this.el.getPadding("lr");
40692 this.hiddenCount = 0;
40695 // toolbar on the tabbar support...
40696 if (this.toolbar) {
40697 alert("no toolbar support yet");
40698 this.toolbar = false;
40700 var tcfg = this.toolbar;
40701 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40702 this.toolbar = new Roo.Toolbar(tcfg);
40703 if (Roo.isSafari) {
40704 var tbl = tcfg.container.child('table', true);
40705 tbl.setAttribute('width', '100%');
40713 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40716 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40718 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40720 tabPosition : "top",
40722 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40724 currentTabWidth : 0,
40726 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40730 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40734 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40736 preferredTabWidth : 175,
40738 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40740 resizeTabs : false,
40742 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40744 monitorResize : true,
40746 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40748 toolbar : false, // set by caller..
40750 region : false, /// set by caller
40752 disableTooltips : true, // not used yet...
40755 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40756 * @param {String} id The id of the div to use <b>or create</b>
40757 * @param {String} text The text for the tab
40758 * @param {String} content (optional) Content to put in the TabPanelItem body
40759 * @param {Boolean} closable (optional) True to create a close icon on the tab
40760 * @return {Roo.TabPanelItem} The created TabPanelItem
40762 addTab : function(id, text, content, closable, tpl)
40764 var item = new Roo.bootstrap.panel.TabItem({
40768 closable : closable,
40771 this.addTabItem(item);
40773 item.setContent(content);
40779 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40780 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40781 * @return {Roo.TabPanelItem}
40783 getTab : function(id){
40784 return this.items[id];
40788 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40789 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40791 hideTab : function(id){
40792 var t = this.items[id];
40795 this.hiddenCount++;
40796 this.autoSizeTabs();
40801 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40802 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40804 unhideTab : function(id){
40805 var t = this.items[id];
40807 t.setHidden(false);
40808 this.hiddenCount--;
40809 this.autoSizeTabs();
40814 * Adds an existing {@link Roo.TabPanelItem}.
40815 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40817 addTabItem : function(item)
40819 this.items[item.id] = item;
40820 this.items.push(item);
40821 this.autoSizeTabs();
40822 // if(this.resizeTabs){
40823 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40824 // this.autoSizeTabs();
40826 // item.autoSize();
40831 * Removes a {@link Roo.TabPanelItem}.
40832 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40834 removeTab : function(id){
40835 var items = this.items;
40836 var tab = items[id];
40837 if(!tab) { return; }
40838 var index = items.indexOf(tab);
40839 if(this.active == tab && items.length > 1){
40840 var newTab = this.getNextAvailable(index);
40845 this.stripEl.dom.removeChild(tab.pnode.dom);
40846 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40847 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40849 items.splice(index, 1);
40850 delete this.items[tab.id];
40851 tab.fireEvent("close", tab);
40852 tab.purgeListeners();
40853 this.autoSizeTabs();
40856 getNextAvailable : function(start){
40857 var items = this.items;
40859 // look for a next tab that will slide over to
40860 // replace the one being removed
40861 while(index < items.length){
40862 var item = items[++index];
40863 if(item && !item.isHidden()){
40867 // if one isn't found select the previous tab (on the left)
40870 var item = items[--index];
40871 if(item && !item.isHidden()){
40879 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40880 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40882 disableTab : function(id){
40883 var tab = this.items[id];
40884 if(tab && this.active != tab){
40890 * Enables a {@link Roo.TabPanelItem} that is disabled.
40891 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40893 enableTab : function(id){
40894 var tab = this.items[id];
40899 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40900 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40901 * @return {Roo.TabPanelItem} The TabPanelItem.
40903 activate : function(id)
40905 //Roo.log('activite:' + id);
40907 var tab = this.items[id];
40911 if(tab == this.active || tab.disabled){
40915 this.fireEvent("beforetabchange", this, e, tab);
40916 if(e.cancel !== true && !tab.disabled){
40918 this.active.hide();
40920 this.active = this.items[id];
40921 this.active.show();
40922 this.fireEvent("tabchange", this, this.active);
40928 * Gets the active {@link Roo.TabPanelItem}.
40929 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40931 getActiveTab : function(){
40932 return this.active;
40936 * Updates the tab body element to fit the height of the container element
40937 * for overflow scrolling
40938 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40940 syncHeight : function(targetHeight){
40941 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40942 var bm = this.bodyEl.getMargins();
40943 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40944 this.bodyEl.setHeight(newHeight);
40948 onResize : function(){
40949 if(this.monitorResize){
40950 this.autoSizeTabs();
40955 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40957 beginUpdate : function(){
40958 this.updating = true;
40962 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40964 endUpdate : function(){
40965 this.updating = false;
40966 this.autoSizeTabs();
40970 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40972 autoSizeTabs : function()
40974 var count = this.items.length;
40975 var vcount = count - this.hiddenCount;
40978 this.stripEl.hide();
40980 this.stripEl.show();
40983 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40988 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40989 var availWidth = Math.floor(w / vcount);
40990 var b = this.stripBody;
40991 if(b.getWidth() > w){
40992 var tabs = this.items;
40993 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40994 if(availWidth < this.minTabWidth){
40995 /*if(!this.sleft){ // incomplete scrolling code
40996 this.createScrollButtons();
40999 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41002 if(this.currentTabWidth < this.preferredTabWidth){
41003 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41009 * Returns the number of tabs in this TabPanel.
41012 getCount : function(){
41013 return this.items.length;
41017 * Resizes all the tabs to the passed width
41018 * @param {Number} The new width
41020 setTabWidth : function(width){
41021 this.currentTabWidth = width;
41022 for(var i = 0, len = this.items.length; i < len; i++) {
41023 if(!this.items[i].isHidden()) {
41024 this.items[i].setWidth(width);
41030 * Destroys this TabPanel
41031 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41033 destroy : function(removeEl){
41034 Roo.EventManager.removeResizeListener(this.onResize, this);
41035 for(var i = 0, len = this.items.length; i < len; i++){
41036 this.items[i].purgeListeners();
41038 if(removeEl === true){
41039 this.el.update("");
41044 createStrip : function(container)
41046 var strip = document.createElement("nav");
41047 strip.className = Roo.bootstrap.version == 4 ?
41048 "navbar-light bg-light" :
41049 "navbar navbar-default"; //"x-tabs-wrap";
41050 container.appendChild(strip);
41054 createStripList : function(strip)
41056 // div wrapper for retard IE
41057 // returns the "tr" element.
41058 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41059 //'<div class="x-tabs-strip-wrap">'+
41060 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41061 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41062 return strip.firstChild; //.firstChild.firstChild.firstChild;
41064 createBody : function(container)
41066 var body = document.createElement("div");
41067 Roo.id(body, "tab-body");
41068 //Roo.fly(body).addClass("x-tabs-body");
41069 Roo.fly(body).addClass("tab-content");
41070 container.appendChild(body);
41073 createItemBody :function(bodyEl, id){
41074 var body = Roo.getDom(id);
41076 body = document.createElement("div");
41079 //Roo.fly(body).addClass("x-tabs-item-body");
41080 Roo.fly(body).addClass("tab-pane");
41081 bodyEl.insertBefore(body, bodyEl.firstChild);
41085 createStripElements : function(stripEl, text, closable, tpl)
41087 var td = document.createElement("li"); // was td..
41088 td.className = 'nav-item';
41090 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41093 stripEl.appendChild(td);
41095 td.className = "x-tabs-closable";
41096 if(!this.closeTpl){
41097 this.closeTpl = new Roo.Template(
41098 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41099 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41100 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41103 var el = this.closeTpl.overwrite(td, {"text": text});
41104 var close = el.getElementsByTagName("div")[0];
41105 var inner = el.getElementsByTagName("em")[0];
41106 return {"el": el, "close": close, "inner": inner};
41109 // not sure what this is..
41110 // if(!this.tabTpl){
41111 //this.tabTpl = new Roo.Template(
41112 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41113 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41115 // this.tabTpl = new Roo.Template(
41116 // '<a href="#">' +
41117 // '<span unselectable="on"' +
41118 // (this.disableTooltips ? '' : ' title="{text}"') +
41119 // ' >{text}</span></a>'
41125 var template = tpl || this.tabTpl || false;
41128 template = new Roo.Template(
41129 Roo.bootstrap.version == 4 ?
41131 '<a class="nav-link" href="#" unselectable="on"' +
41132 (this.disableTooltips ? '' : ' title="{text}"') +
41135 '<a class="nav-link" href="#">' +
41136 '<span unselectable="on"' +
41137 (this.disableTooltips ? '' : ' title="{text}"') +
41138 ' >{text}</span></a>'
41143 switch (typeof(template)) {
41147 template = new Roo.Template(template);
41153 var el = template.overwrite(td, {"text": text});
41155 var inner = el.getElementsByTagName("span")[0];
41157 return {"el": el, "inner": inner};
41165 * @class Roo.TabPanelItem
41166 * @extends Roo.util.Observable
41167 * Represents an individual item (tab plus body) in a TabPanel.
41168 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41169 * @param {String} id The id of this TabPanelItem
41170 * @param {String} text The text for the tab of this TabPanelItem
41171 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41173 Roo.bootstrap.panel.TabItem = function(config){
41175 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41176 * @type Roo.TabPanel
41178 this.tabPanel = config.panel;
41180 * The id for this TabPanelItem
41183 this.id = config.id;
41185 this.disabled = false;
41187 this.text = config.text;
41189 this.loaded = false;
41190 this.closable = config.closable;
41193 * The body element for this TabPanelItem.
41194 * @type Roo.Element
41196 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41197 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41198 this.bodyEl.setStyle("display", "block");
41199 this.bodyEl.setStyle("zoom", "1");
41200 //this.hideAction();
41202 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41204 this.el = Roo.get(els.el);
41205 this.inner = Roo.get(els.inner, true);
41206 this.textEl = Roo.bootstrap.version == 4 ?
41207 this.el : Roo.get(this.el.dom.firstChild, true);
41209 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41210 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41213 // this.el.on("mousedown", this.onTabMouseDown, this);
41214 this.el.on("click", this.onTabClick, this);
41216 if(config.closable){
41217 var c = Roo.get(els.close, true);
41218 c.dom.title = this.closeText;
41219 c.addClassOnOver("close-over");
41220 c.on("click", this.closeClick, this);
41226 * Fires when this tab becomes the active tab.
41227 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41228 * @param {Roo.TabPanelItem} this
41232 * @event beforeclose
41233 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41234 * @param {Roo.TabPanelItem} this
41235 * @param {Object} e Set cancel to true on this object to cancel the close.
41237 "beforeclose": true,
41240 * Fires when this tab is closed.
41241 * @param {Roo.TabPanelItem} this
41245 * @event deactivate
41246 * Fires when this tab is no longer the active tab.
41247 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41248 * @param {Roo.TabPanelItem} this
41250 "deactivate" : true
41252 this.hidden = false;
41254 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41257 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41259 purgeListeners : function(){
41260 Roo.util.Observable.prototype.purgeListeners.call(this);
41261 this.el.removeAllListeners();
41264 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41267 this.status_node.addClass("active");
41270 this.tabPanel.stripWrap.repaint();
41272 this.fireEvent("activate", this.tabPanel, this);
41276 * Returns true if this tab is the active tab.
41277 * @return {Boolean}
41279 isActive : function(){
41280 return this.tabPanel.getActiveTab() == this;
41284 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41287 this.status_node.removeClass("active");
41289 this.fireEvent("deactivate", this.tabPanel, this);
41292 hideAction : function(){
41293 this.bodyEl.hide();
41294 this.bodyEl.setStyle("position", "absolute");
41295 this.bodyEl.setLeft("-20000px");
41296 this.bodyEl.setTop("-20000px");
41299 showAction : function(){
41300 this.bodyEl.setStyle("position", "relative");
41301 this.bodyEl.setTop("");
41302 this.bodyEl.setLeft("");
41303 this.bodyEl.show();
41307 * Set the tooltip for the tab.
41308 * @param {String} tooltip The tab's tooltip
41310 setTooltip : function(text){
41311 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41312 this.textEl.dom.qtip = text;
41313 this.textEl.dom.removeAttribute('title');
41315 this.textEl.dom.title = text;
41319 onTabClick : function(e){
41320 e.preventDefault();
41321 this.tabPanel.activate(this.id);
41324 onTabMouseDown : function(e){
41325 e.preventDefault();
41326 this.tabPanel.activate(this.id);
41329 getWidth : function(){
41330 return this.inner.getWidth();
41333 setWidth : function(width){
41334 var iwidth = width - this.linode.getPadding("lr");
41335 this.inner.setWidth(iwidth);
41336 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41337 this.linode.setWidth(width);
41341 * Show or hide the tab
41342 * @param {Boolean} hidden True to hide or false to show.
41344 setHidden : function(hidden){
41345 this.hidden = hidden;
41346 this.linode.setStyle("display", hidden ? "none" : "");
41350 * Returns true if this tab is "hidden"
41351 * @return {Boolean}
41353 isHidden : function(){
41354 return this.hidden;
41358 * Returns the text for this tab
41361 getText : function(){
41365 autoSize : function(){
41366 //this.el.beginMeasure();
41367 this.textEl.setWidth(1);
41369 * #2804 [new] Tabs in Roojs
41370 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41372 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41373 //this.el.endMeasure();
41377 * Sets the text for the tab (Note: this also sets the tooltip text)
41378 * @param {String} text The tab's text and tooltip
41380 setText : function(text){
41382 this.textEl.update(text);
41383 this.setTooltip(text);
41384 //if(!this.tabPanel.resizeTabs){
41385 // this.autoSize();
41389 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41391 activate : function(){
41392 this.tabPanel.activate(this.id);
41396 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41398 disable : function(){
41399 if(this.tabPanel.active != this){
41400 this.disabled = true;
41401 this.status_node.addClass("disabled");
41406 * Enables this TabPanelItem if it was previously disabled.
41408 enable : function(){
41409 this.disabled = false;
41410 this.status_node.removeClass("disabled");
41414 * Sets the content for this TabPanelItem.
41415 * @param {String} content The content
41416 * @param {Boolean} loadScripts true to look for and load scripts
41418 setContent : function(content, loadScripts){
41419 this.bodyEl.update(content, loadScripts);
41423 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41424 * @return {Roo.UpdateManager} The UpdateManager
41426 getUpdateManager : function(){
41427 return this.bodyEl.getUpdateManager();
41431 * Set a URL to be used to load the content for this TabPanelItem.
41432 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41433 * @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)
41434 * @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)
41435 * @return {Roo.UpdateManager} The UpdateManager
41437 setUrl : function(url, params, loadOnce){
41438 if(this.refreshDelegate){
41439 this.un('activate', this.refreshDelegate);
41441 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41442 this.on("activate", this.refreshDelegate);
41443 return this.bodyEl.getUpdateManager();
41447 _handleRefresh : function(url, params, loadOnce){
41448 if(!loadOnce || !this.loaded){
41449 var updater = this.bodyEl.getUpdateManager();
41450 updater.update(url, params, this._setLoaded.createDelegate(this));
41455 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41456 * Will fail silently if the setUrl method has not been called.
41457 * This does not activate the panel, just updates its content.
41459 refresh : function(){
41460 if(this.refreshDelegate){
41461 this.loaded = false;
41462 this.refreshDelegate();
41467 _setLoaded : function(){
41468 this.loaded = true;
41472 closeClick : function(e){
41475 this.fireEvent("beforeclose", this, o);
41476 if(o.cancel !== true){
41477 this.tabPanel.removeTab(this.id);
41481 * The text displayed in the tooltip for the close icon.
41484 closeText : "Close this tab"
41487 * This script refer to:
41488 * Title: International Telephone Input
41489 * Author: Jack O'Connor
41490 * Code version: v12.1.12
41491 * Availability: https://github.com/jackocnr/intl-tel-input.git
41494 Roo.bootstrap.PhoneInputData = function() {
41497 "Afghanistan (افغانستان)",
41502 "Albania (Shqipëri)",
41507 "Algeria (الجزائر)",
41532 "Antigua and Barbuda",
41542 "Armenia (Հայաստան)",
41558 "Austria (Österreich)",
41563 "Azerbaijan (Azərbaycan)",
41573 "Bahrain (البحرين)",
41578 "Bangladesh (বাংলাদেশ)",
41588 "Belarus (Беларусь)",
41593 "Belgium (België)",
41623 "Bosnia and Herzegovina (Босна и Херцеговина)",
41638 "British Indian Ocean Territory",
41643 "British Virgin Islands",
41653 "Bulgaria (България)",
41663 "Burundi (Uburundi)",
41668 "Cambodia (កម្ពុជា)",
41673 "Cameroon (Cameroun)",
41682 ["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"]
41685 "Cape Verde (Kabu Verdi)",
41690 "Caribbean Netherlands",
41701 "Central African Republic (République centrafricaine)",
41721 "Christmas Island",
41727 "Cocos (Keeling) Islands",
41738 "Comoros (جزر القمر)",
41743 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41748 "Congo (Republic) (Congo-Brazzaville)",
41768 "Croatia (Hrvatska)",
41789 "Czech Republic (Česká republika)",
41794 "Denmark (Danmark)",
41809 "Dominican Republic (República Dominicana)",
41813 ["809", "829", "849"]
41831 "Equatorial Guinea (Guinea Ecuatorial)",
41851 "Falkland Islands (Islas Malvinas)",
41856 "Faroe Islands (Føroyar)",
41877 "French Guiana (Guyane française)",
41882 "French Polynesia (Polynésie française)",
41897 "Georgia (საქართველო)",
41902 "Germany (Deutschland)",
41922 "Greenland (Kalaallit Nunaat)",
41959 "Guinea-Bissau (Guiné Bissau)",
41984 "Hungary (Magyarország)",
41989 "Iceland (Ísland)",
42009 "Iraq (العراق)",
42025 "Israel (ישראל)",
42052 "Jordan (الأردن)",
42057 "Kazakhstan (Казахстан)",
42078 "Kuwait (الكويت)",
42083 "Kyrgyzstan (Кыргызстан)",
42093 "Latvia (Latvija)",
42098 "Lebanon (لبنان)",
42113 "Libya (ليبيا)",
42123 "Lithuania (Lietuva)",
42138 "Macedonia (FYROM) (Македонија)",
42143 "Madagascar (Madagasikara)",
42173 "Marshall Islands",
42183 "Mauritania (موريتانيا)",
42188 "Mauritius (Moris)",
42209 "Moldova (Republica Moldova)",
42219 "Mongolia (Монгол)",
42224 "Montenegro (Crna Gora)",
42234 "Morocco (المغرب)",
42240 "Mozambique (Moçambique)",
42245 "Myanmar (Burma) (မြန်မာ)",
42250 "Namibia (Namibië)",
42265 "Netherlands (Nederland)",
42270 "New Caledonia (Nouvelle-Calédonie)",
42305 "North Korea (조선 민주주의 인민 공화국)",
42310 "Northern Mariana Islands",
42326 "Pakistan (پاکستان)",
42336 "Palestine (فلسطين)",
42346 "Papua New Guinea",
42388 "Réunion (La Réunion)",
42394 "Romania (România)",
42410 "Saint Barthélemy",
42421 "Saint Kitts and Nevis",
42431 "Saint Martin (Saint-Martin (partie française))",
42437 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42442 "Saint Vincent and the Grenadines",
42457 "São Tomé and Príncipe (São Tomé e Príncipe)",
42462 "Saudi Arabia (المملكة العربية السعودية)",
42467 "Senegal (Sénégal)",
42497 "Slovakia (Slovensko)",
42502 "Slovenia (Slovenija)",
42512 "Somalia (Soomaaliya)",
42522 "South Korea (대한민국)",
42527 "South Sudan (جنوب السودان)",
42537 "Sri Lanka (ශ්රී ලංකාව)",
42542 "Sudan (السودان)",
42552 "Svalbard and Jan Mayen",
42563 "Sweden (Sverige)",
42568 "Switzerland (Schweiz)",
42573 "Syria (سوريا)",
42618 "Trinidad and Tobago",
42623 "Tunisia (تونس)",
42628 "Turkey (Türkiye)",
42638 "Turks and Caicos Islands",
42648 "U.S. Virgin Islands",
42658 "Ukraine (Україна)",
42663 "United Arab Emirates (الإمارات العربية المتحدة)",
42685 "Uzbekistan (Oʻzbekiston)",
42695 "Vatican City (Città del Vaticano)",
42706 "Vietnam (Việt Nam)",
42711 "Wallis and Futuna (Wallis-et-Futuna)",
42716 "Western Sahara (الصحراء الغربية)",
42722 "Yemen (اليمن)",
42746 * This script refer to:
42747 * Title: International Telephone Input
42748 * Author: Jack O'Connor
42749 * Code version: v12.1.12
42750 * Availability: https://github.com/jackocnr/intl-tel-input.git
42754 * @class Roo.bootstrap.PhoneInput
42755 * @extends Roo.bootstrap.TriggerField
42756 * An input with International dial-code selection
42758 * @cfg {String} defaultDialCode default '+852'
42759 * @cfg {Array} preferedCountries default []
42762 * Create a new PhoneInput.
42763 * @param {Object} config Configuration options
42766 Roo.bootstrap.PhoneInput = function(config) {
42767 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42770 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42772 listWidth: undefined,
42774 selectedClass: 'active',
42776 invalidClass : "has-warning",
42778 validClass: 'has-success',
42780 allowed: '0123456789',
42785 * @cfg {String} defaultDialCode The default dial code when initializing the input
42787 defaultDialCode: '+852',
42790 * @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
42792 preferedCountries: false,
42794 getAutoCreate : function()
42796 var data = Roo.bootstrap.PhoneInputData();
42797 var align = this.labelAlign || this.parentLabelAlign();
42800 this.allCountries = [];
42801 this.dialCodeMapping = [];
42803 for (var i = 0; i < data.length; i++) {
42805 this.allCountries[i] = {
42809 priority: c[3] || 0,
42810 areaCodes: c[4] || null
42812 this.dialCodeMapping[c[2]] = {
42815 priority: c[3] || 0,
42816 areaCodes: c[4] || null
42828 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42829 maxlength: this.max_length,
42830 cls : 'form-control tel-input',
42831 autocomplete: 'new-password'
42834 var hiddenInput = {
42837 cls: 'hidden-tel-input'
42841 hiddenInput.name = this.name;
42844 if (this.disabled) {
42845 input.disabled = true;
42848 var flag_container = {
42865 cls: this.hasFeedback ? 'has-feedback' : '',
42871 cls: 'dial-code-holder',
42878 cls: 'roo-select2-container input-group',
42885 if (this.fieldLabel.length) {
42888 tooltip: 'This field is required'
42894 cls: 'control-label',
42900 html: this.fieldLabel
42903 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42909 if(this.indicatorpos == 'right') {
42910 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42917 if(align == 'left') {
42925 if(this.labelWidth > 12){
42926 label.style = "width: " + this.labelWidth + 'px';
42928 if(this.labelWidth < 13 && this.labelmd == 0){
42929 this.labelmd = this.labelWidth;
42931 if(this.labellg > 0){
42932 label.cls += ' col-lg-' + this.labellg;
42933 input.cls += ' col-lg-' + (12 - this.labellg);
42935 if(this.labelmd > 0){
42936 label.cls += ' col-md-' + this.labelmd;
42937 container.cls += ' col-md-' + (12 - this.labelmd);
42939 if(this.labelsm > 0){
42940 label.cls += ' col-sm-' + this.labelsm;
42941 container.cls += ' col-sm-' + (12 - this.labelsm);
42943 if(this.labelxs > 0){
42944 label.cls += ' col-xs-' + this.labelxs;
42945 container.cls += ' col-xs-' + (12 - this.labelxs);
42955 var settings = this;
42957 ['xs','sm','md','lg'].map(function(size){
42958 if (settings[size]) {
42959 cfg.cls += ' col-' + size + '-' + settings[size];
42963 this.store = new Roo.data.Store({
42964 proxy : new Roo.data.MemoryProxy({}),
42965 reader : new Roo.data.JsonReader({
42976 'name' : 'dialCode',
42980 'name' : 'priority',
42984 'name' : 'areaCodes',
42991 if(!this.preferedCountries) {
42992 this.preferedCountries = [
42999 var p = this.preferedCountries.reverse();
43002 for (var i = 0; i < p.length; i++) {
43003 for (var j = 0; j < this.allCountries.length; j++) {
43004 if(this.allCountries[j].iso2 == p[i]) {
43005 var t = this.allCountries[j];
43006 this.allCountries.splice(j,1);
43007 this.allCountries.unshift(t);
43013 this.store.proxy.data = {
43015 data: this.allCountries
43021 initEvents : function()
43024 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43026 this.indicator = this.indicatorEl();
43027 this.flag = this.flagEl();
43028 this.dialCodeHolder = this.dialCodeHolderEl();
43030 this.trigger = this.el.select('div.flag-box',true).first();
43031 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43036 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43037 _this.list.setWidth(lw);
43040 this.list.on('mouseover', this.onViewOver, this);
43041 this.list.on('mousemove', this.onViewMove, this);
43042 this.inputEl().on("keyup", this.onKeyUp, this);
43043 this.inputEl().on("keypress", this.onKeyPress, this);
43045 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43047 this.view = new Roo.View(this.list, this.tpl, {
43048 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43051 this.view.on('click', this.onViewClick, this);
43052 this.setValue(this.defaultDialCode);
43055 onTriggerClick : function(e)
43057 Roo.log('trigger click');
43062 if(this.isExpanded()){
43064 this.hasFocus = false;
43066 this.store.load({});
43067 this.hasFocus = true;
43072 isExpanded : function()
43074 return this.list.isVisible();
43077 collapse : function()
43079 if(!this.isExpanded()){
43083 Roo.get(document).un('mousedown', this.collapseIf, this);
43084 Roo.get(document).un('mousewheel', this.collapseIf, this);
43085 this.fireEvent('collapse', this);
43089 expand : function()
43093 if(this.isExpanded() || !this.hasFocus){
43097 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43098 this.list.setWidth(lw);
43101 this.restrictHeight();
43103 Roo.get(document).on('mousedown', this.collapseIf, this);
43104 Roo.get(document).on('mousewheel', this.collapseIf, this);
43106 this.fireEvent('expand', this);
43109 restrictHeight : function()
43111 this.list.alignTo(this.inputEl(), this.listAlign);
43112 this.list.alignTo(this.inputEl(), this.listAlign);
43115 onViewOver : function(e, t)
43117 if(this.inKeyMode){
43120 var item = this.view.findItemFromChild(t);
43123 var index = this.view.indexOf(item);
43124 this.select(index, false);
43129 onViewClick : function(view, doFocus, el, e)
43131 var index = this.view.getSelectedIndexes()[0];
43133 var r = this.store.getAt(index);
43136 this.onSelect(r, index);
43138 if(doFocus !== false && !this.blockFocus){
43139 this.inputEl().focus();
43143 onViewMove : function(e, t)
43145 this.inKeyMode = false;
43148 select : function(index, scrollIntoView)
43150 this.selectedIndex = index;
43151 this.view.select(index);
43152 if(scrollIntoView !== false){
43153 var el = this.view.getNode(index);
43155 this.list.scrollChildIntoView(el, false);
43160 createList : function()
43162 this.list = Roo.get(document.body).createChild({
43164 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43165 style: 'display:none'
43168 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43171 collapseIf : function(e)
43173 var in_combo = e.within(this.el);
43174 var in_list = e.within(this.list);
43175 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43177 if (in_combo || in_list || is_list) {
43183 onSelect : function(record, index)
43185 if(this.fireEvent('beforeselect', this, record, index) !== false){
43187 this.setFlagClass(record.data.iso2);
43188 this.setDialCode(record.data.dialCode);
43189 this.hasFocus = false;
43191 this.fireEvent('select', this, record, index);
43195 flagEl : function()
43197 var flag = this.el.select('div.flag',true).first();
43204 dialCodeHolderEl : function()
43206 var d = this.el.select('input.dial-code-holder',true).first();
43213 setDialCode : function(v)
43215 this.dialCodeHolder.dom.value = '+'+v;
43218 setFlagClass : function(n)
43220 this.flag.dom.className = 'flag '+n;
43223 getValue : function()
43225 var v = this.inputEl().getValue();
43226 if(this.dialCodeHolder) {
43227 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43232 setValue : function(v)
43234 var d = this.getDialCode(v);
43236 //invalid dial code
43237 if(v.length == 0 || !d || d.length == 0) {
43239 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43240 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43246 this.setFlagClass(this.dialCodeMapping[d].iso2);
43247 this.setDialCode(d);
43248 this.inputEl().dom.value = v.replace('+'+d,'');
43249 this.hiddenEl().dom.value = this.getValue();
43254 getDialCode : function(v)
43258 if (v.length == 0) {
43259 return this.dialCodeHolder.dom.value;
43263 if (v.charAt(0) != "+") {
43266 var numericChars = "";
43267 for (var i = 1; i < v.length; i++) {
43268 var c = v.charAt(i);
43271 if (this.dialCodeMapping[numericChars]) {
43272 dialCode = v.substr(1, i);
43274 if (numericChars.length == 4) {
43284 this.setValue(this.defaultDialCode);
43288 hiddenEl : function()
43290 return this.el.select('input.hidden-tel-input',true).first();
43293 // after setting val
43294 onKeyUp : function(e){
43295 this.setValue(this.getValue());
43298 onKeyPress : function(e){
43299 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43306 * @class Roo.bootstrap.MoneyField
43307 * @extends Roo.bootstrap.ComboBox
43308 * Bootstrap MoneyField class
43311 * Create a new MoneyField.
43312 * @param {Object} config Configuration options
43315 Roo.bootstrap.MoneyField = function(config) {
43317 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43321 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43324 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43326 allowDecimals : true,
43328 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43330 decimalSeparator : ".",
43332 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43334 decimalPrecision : 0,
43336 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43338 allowNegative : true,
43340 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43344 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43346 minValue : Number.NEGATIVE_INFINITY,
43348 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43350 maxValue : Number.MAX_VALUE,
43352 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43354 minText : "The minimum value for this field is {0}",
43356 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43358 maxText : "The maximum value for this field is {0}",
43360 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43361 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43363 nanText : "{0} is not a valid number",
43365 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43369 * @cfg {String} defaults currency of the MoneyField
43370 * value should be in lkey
43372 defaultCurrency : false,
43374 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43376 thousandsDelimiter : false,
43378 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43389 getAutoCreate : function()
43391 var align = this.labelAlign || this.parentLabelAlign();
43403 cls : 'form-control roo-money-amount-input',
43404 autocomplete: 'new-password'
43407 var hiddenInput = {
43411 cls: 'hidden-number-input'
43414 if(this.max_length) {
43415 input.maxlength = this.max_length;
43419 hiddenInput.name = this.name;
43422 if (this.disabled) {
43423 input.disabled = true;
43426 var clg = 12 - this.inputlg;
43427 var cmd = 12 - this.inputmd;
43428 var csm = 12 - this.inputsm;
43429 var cxs = 12 - this.inputxs;
43433 cls : 'row roo-money-field',
43437 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43441 cls: 'roo-select2-container input-group',
43445 cls : 'form-control roo-money-currency-input',
43446 autocomplete: 'new-password',
43448 name : this.currencyName
43452 cls : 'input-group-addon',
43466 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43470 cls: this.hasFeedback ? 'has-feedback' : '',
43481 if (this.fieldLabel.length) {
43484 tooltip: 'This field is required'
43490 cls: 'control-label',
43496 html: this.fieldLabel
43499 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43505 if(this.indicatorpos == 'right') {
43506 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43513 if(align == 'left') {
43521 if(this.labelWidth > 12){
43522 label.style = "width: " + this.labelWidth + 'px';
43524 if(this.labelWidth < 13 && this.labelmd == 0){
43525 this.labelmd = this.labelWidth;
43527 if(this.labellg > 0){
43528 label.cls += ' col-lg-' + this.labellg;
43529 input.cls += ' col-lg-' + (12 - this.labellg);
43531 if(this.labelmd > 0){
43532 label.cls += ' col-md-' + this.labelmd;
43533 container.cls += ' col-md-' + (12 - this.labelmd);
43535 if(this.labelsm > 0){
43536 label.cls += ' col-sm-' + this.labelsm;
43537 container.cls += ' col-sm-' + (12 - this.labelsm);
43539 if(this.labelxs > 0){
43540 label.cls += ' col-xs-' + this.labelxs;
43541 container.cls += ' col-xs-' + (12 - this.labelxs);
43552 var settings = this;
43554 ['xs','sm','md','lg'].map(function(size){
43555 if (settings[size]) {
43556 cfg.cls += ' col-' + size + '-' + settings[size];
43563 initEvents : function()
43565 this.indicator = this.indicatorEl();
43567 this.initCurrencyEvent();
43569 this.initNumberEvent();
43572 initCurrencyEvent : function()
43575 throw "can not find store for combo";
43578 this.store = Roo.factory(this.store, Roo.data);
43579 this.store.parent = this;
43583 this.triggerEl = this.el.select('.input-group-addon', true).first();
43585 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43590 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43591 _this.list.setWidth(lw);
43594 this.list.on('mouseover', this.onViewOver, this);
43595 this.list.on('mousemove', this.onViewMove, this);
43596 this.list.on('scroll', this.onViewScroll, this);
43599 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43602 this.view = new Roo.View(this.list, this.tpl, {
43603 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43606 this.view.on('click', this.onViewClick, this);
43608 this.store.on('beforeload', this.onBeforeLoad, this);
43609 this.store.on('load', this.onLoad, this);
43610 this.store.on('loadexception', this.onLoadException, this);
43612 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43613 "up" : function(e){
43614 this.inKeyMode = true;
43618 "down" : function(e){
43619 if(!this.isExpanded()){
43620 this.onTriggerClick();
43622 this.inKeyMode = true;
43627 "enter" : function(e){
43630 if(this.fireEvent("specialkey", this, e)){
43631 this.onViewClick(false);
43637 "esc" : function(e){
43641 "tab" : function(e){
43644 if(this.fireEvent("specialkey", this, e)){
43645 this.onViewClick(false);
43653 doRelay : function(foo, bar, hname){
43654 if(hname == 'down' || this.scope.isExpanded()){
43655 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43663 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43667 initNumberEvent : function(e)
43669 this.inputEl().on("keydown" , this.fireKey, this);
43670 this.inputEl().on("focus", this.onFocus, this);
43671 this.inputEl().on("blur", this.onBlur, this);
43673 this.inputEl().relayEvent('keyup', this);
43675 if(this.indicator){
43676 this.indicator.addClass('invisible');
43679 this.originalValue = this.getValue();
43681 if(this.validationEvent == 'keyup'){
43682 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43683 this.inputEl().on('keyup', this.filterValidation, this);
43685 else if(this.validationEvent !== false){
43686 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43689 if(this.selectOnFocus){
43690 this.on("focus", this.preFocus, this);
43693 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43694 this.inputEl().on("keypress", this.filterKeys, this);
43696 this.inputEl().relayEvent('keypress', this);
43699 var allowed = "0123456789";
43701 if(this.allowDecimals){
43702 allowed += this.decimalSeparator;
43705 if(this.allowNegative){
43709 if(this.thousandsDelimiter) {
43713 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43715 var keyPress = function(e){
43717 var k = e.getKey();
43719 var c = e.getCharCode();
43722 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43723 allowed.indexOf(String.fromCharCode(c)) === -1
43729 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43733 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43738 this.inputEl().on("keypress", keyPress, this);
43742 onTriggerClick : function(e)
43749 this.loadNext = false;
43751 if(this.isExpanded()){
43756 this.hasFocus = true;
43758 if(this.triggerAction == 'all') {
43759 this.doQuery(this.allQuery, true);
43763 this.doQuery(this.getRawValue());
43766 getCurrency : function()
43768 var v = this.currencyEl().getValue();
43773 restrictHeight : function()
43775 this.list.alignTo(this.currencyEl(), this.listAlign);
43776 this.list.alignTo(this.currencyEl(), this.listAlign);
43779 onViewClick : function(view, doFocus, el, e)
43781 var index = this.view.getSelectedIndexes()[0];
43783 var r = this.store.getAt(index);
43786 this.onSelect(r, index);
43790 onSelect : function(record, index){
43792 if(this.fireEvent('beforeselect', this, record, index) !== false){
43794 this.setFromCurrencyData(index > -1 ? record.data : false);
43798 this.fireEvent('select', this, record, index);
43802 setFromCurrencyData : function(o)
43806 this.lastCurrency = o;
43808 if (this.currencyField) {
43809 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43811 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43814 this.lastSelectionText = currency;
43816 //setting default currency
43817 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43818 this.setCurrency(this.defaultCurrency);
43822 this.setCurrency(currency);
43825 setFromData : function(o)
43829 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43831 this.setFromCurrencyData(c);
43836 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43838 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43841 this.setValue(value);
43845 setCurrency : function(v)
43847 this.currencyValue = v;
43850 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43855 setValue : function(v)
43857 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43863 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43865 this.inputEl().dom.value = (v == '') ? '' :
43866 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43868 if(!this.allowZero && v === '0') {
43869 this.hiddenEl().dom.value = '';
43870 this.inputEl().dom.value = '';
43877 getRawValue : function()
43879 var v = this.inputEl().getValue();
43884 getValue : function()
43886 return this.fixPrecision(this.parseValue(this.getRawValue()));
43889 parseValue : function(value)
43891 if(this.thousandsDelimiter) {
43893 r = new RegExp(",", "g");
43894 value = value.replace(r, "");
43897 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43898 return isNaN(value) ? '' : value;
43902 fixPrecision : function(value)
43904 if(this.thousandsDelimiter) {
43906 r = new RegExp(",", "g");
43907 value = value.replace(r, "");
43910 var nan = isNaN(value);
43912 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43913 return nan ? '' : value;
43915 return parseFloat(value).toFixed(this.decimalPrecision);
43918 decimalPrecisionFcn : function(v)
43920 return Math.floor(v);
43923 validateValue : function(value)
43925 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43929 var num = this.parseValue(value);
43932 this.markInvalid(String.format(this.nanText, value));
43936 if(num < this.minValue){
43937 this.markInvalid(String.format(this.minText, this.minValue));
43941 if(num > this.maxValue){
43942 this.markInvalid(String.format(this.maxText, this.maxValue));
43949 validate : function()
43951 if(this.disabled || this.allowBlank){
43956 var currency = this.getCurrency();
43958 if(this.validateValue(this.getRawValue()) && currency.length){
43963 this.markInvalid();
43967 getName: function()
43972 beforeBlur : function()
43978 var v = this.parseValue(this.getRawValue());
43985 onBlur : function()
43989 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43990 //this.el.removeClass(this.focusClass);
43993 this.hasFocus = false;
43995 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43999 var v = this.getValue();
44001 if(String(v) !== String(this.startValue)){
44002 this.fireEvent('change', this, v, this.startValue);
44005 this.fireEvent("blur", this);
44008 inputEl : function()
44010 return this.el.select('.roo-money-amount-input', true).first();
44013 currencyEl : function()
44015 return this.el.select('.roo-money-currency-input', true).first();
44018 hiddenEl : function()
44020 return this.el.select('input.hidden-number-input',true).first();
44024 * @class Roo.bootstrap.BezierSignature
44025 * @extends Roo.bootstrap.Component
44026 * Bootstrap BezierSignature class
44027 * This script refer to:
44028 * Title: Signature Pad
44030 * Availability: https://github.com/szimek/signature_pad
44033 * Create a new BezierSignature
44034 * @param {Object} config The config object
44037 Roo.bootstrap.BezierSignature = function(config){
44038 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44044 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44051 mouse_btn_down: true,
44054 * @cfg {int} canvas height
44056 canvas_height: '200px',
44059 * @cfg {float|function} Radius of a single dot.
44064 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44069 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44074 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44079 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44084 * @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.
44086 bg_color: 'rgba(0, 0, 0, 0)',
44089 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44091 dot_color: 'black',
44094 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44096 velocity_filter_weight: 0.7,
44099 * @cfg {function} Callback when stroke begin.
44104 * @cfg {function} Callback when stroke end.
44108 getAutoCreate : function()
44110 var cls = 'roo-signature column';
44113 cls += ' ' + this.cls;
44123 for(var i = 0; i < col_sizes.length; i++) {
44124 if(this[col_sizes[i]]) {
44125 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44135 cls: 'roo-signature-body',
44139 cls: 'roo-signature-body-canvas',
44140 height: this.canvas_height,
44141 width: this.canvas_width
44148 style: 'display: none'
44156 initEvents: function()
44158 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44160 var canvas = this.canvasEl();
44162 // mouse && touch event swapping...
44163 canvas.dom.style.touchAction = 'none';
44164 canvas.dom.style.msTouchAction = 'none';
44166 this.mouse_btn_down = false;
44167 canvas.on('mousedown', this._handleMouseDown, this);
44168 canvas.on('mousemove', this._handleMouseMove, this);
44169 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44171 if (window.PointerEvent) {
44172 canvas.on('pointerdown', this._handleMouseDown, this);
44173 canvas.on('pointermove', this._handleMouseMove, this);
44174 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44177 if ('ontouchstart' in window) {
44178 canvas.on('touchstart', this._handleTouchStart, this);
44179 canvas.on('touchmove', this._handleTouchMove, this);
44180 canvas.on('touchend', this._handleTouchEnd, this);
44183 Roo.EventManager.onWindowResize(this.resize, this, true);
44185 // file input event
44186 this.fileEl().on('change', this.uploadImage, this);
44193 resize: function(){
44195 var canvas = this.canvasEl().dom;
44196 var ctx = this.canvasElCtx();
44197 var img_data = false;
44199 if(canvas.width > 0) {
44200 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44202 // setting canvas width will clean img data
44205 var style = window.getComputedStyle ?
44206 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44208 var padding_left = parseInt(style.paddingLeft) || 0;
44209 var padding_right = parseInt(style.paddingRight) || 0;
44211 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44214 ctx.putImageData(img_data, 0, 0);
44218 _handleMouseDown: function(e)
44220 if (e.browserEvent.which === 1) {
44221 this.mouse_btn_down = true;
44222 this.strokeBegin(e);
44226 _handleMouseMove: function (e)
44228 if (this.mouse_btn_down) {
44229 this.strokeMoveUpdate(e);
44233 _handleMouseUp: function (e)
44235 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44236 this.mouse_btn_down = false;
44241 _handleTouchStart: function (e) {
44243 e.preventDefault();
44244 if (e.browserEvent.targetTouches.length === 1) {
44245 // var touch = e.browserEvent.changedTouches[0];
44246 // this.strokeBegin(touch);
44248 this.strokeBegin(e); // assume e catching the correct xy...
44252 _handleTouchMove: function (e) {
44253 e.preventDefault();
44254 // var touch = event.targetTouches[0];
44255 // _this._strokeMoveUpdate(touch);
44256 this.strokeMoveUpdate(e);
44259 _handleTouchEnd: function (e) {
44260 var wasCanvasTouched = e.target === this.canvasEl().dom;
44261 if (wasCanvasTouched) {
44262 e.preventDefault();
44263 // var touch = event.changedTouches[0];
44264 // _this._strokeEnd(touch);
44269 reset: function () {
44270 this._lastPoints = [];
44271 this._lastVelocity = 0;
44272 this._lastWidth = (this.min_width + this.max_width) / 2;
44273 this.canvasElCtx().fillStyle = this.dot_color;
44276 strokeMoveUpdate: function(e)
44278 this.strokeUpdate(e);
44280 if (this.throttle) {
44281 this.throttleStroke(this.strokeUpdate, this.throttle);
44284 this.strokeUpdate(e);
44288 strokeBegin: function(e)
44290 var newPointGroup = {
44291 color: this.dot_color,
44295 if (typeof this.onBegin === 'function') {
44299 this.curve_data.push(newPointGroup);
44301 this.strokeUpdate(e);
44304 strokeUpdate: function(e)
44306 var rect = this.canvasEl().dom.getBoundingClientRect();
44307 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44308 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44309 var lastPoints = lastPointGroup.points;
44310 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44311 var isLastPointTooClose = lastPoint
44312 ? point.distanceTo(lastPoint) <= this.min_distance
44314 var color = lastPointGroup.color;
44315 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44316 var curve = this.addPoint(point);
44318 this.drawDot({color: color, point: point});
44321 this.drawCurve({color: color, curve: curve});
44331 strokeEnd: function(e)
44333 this.strokeUpdate(e);
44334 if (typeof this.onEnd === 'function') {
44339 addPoint: function (point) {
44340 var _lastPoints = this._lastPoints;
44341 _lastPoints.push(point);
44342 if (_lastPoints.length > 2) {
44343 if (_lastPoints.length === 3) {
44344 _lastPoints.unshift(_lastPoints[0]);
44346 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44347 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44348 _lastPoints.shift();
44354 calculateCurveWidths: function (startPoint, endPoint) {
44355 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44356 (1 - this.velocity_filter_weight) * this._lastVelocity;
44358 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44361 start: this._lastWidth
44364 this._lastVelocity = velocity;
44365 this._lastWidth = newWidth;
44369 drawDot: function (_a) {
44370 var color = _a.color, point = _a.point;
44371 var ctx = this.canvasElCtx();
44372 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44374 this.drawCurveSegment(point.x, point.y, width);
44376 ctx.fillStyle = color;
44380 drawCurve: function (_a) {
44381 var color = _a.color, curve = _a.curve;
44382 var ctx = this.canvasElCtx();
44383 var widthDelta = curve.endWidth - curve.startWidth;
44384 var drawSteps = Math.floor(curve.length()) * 2;
44386 ctx.fillStyle = color;
44387 for (var i = 0; i < drawSteps; i += 1) {
44388 var t = i / drawSteps;
44394 var x = uuu * curve.startPoint.x;
44395 x += 3 * uu * t * curve.control1.x;
44396 x += 3 * u * tt * curve.control2.x;
44397 x += ttt * curve.endPoint.x;
44398 var y = uuu * curve.startPoint.y;
44399 y += 3 * uu * t * curve.control1.y;
44400 y += 3 * u * tt * curve.control2.y;
44401 y += ttt * curve.endPoint.y;
44402 var width = curve.startWidth + ttt * widthDelta;
44403 this.drawCurveSegment(x, y, width);
44409 drawCurveSegment: function (x, y, width) {
44410 var ctx = this.canvasElCtx();
44412 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44413 this.is_empty = false;
44418 var ctx = this.canvasElCtx();
44419 var canvas = this.canvasEl().dom;
44420 ctx.fillStyle = this.bg_color;
44421 ctx.clearRect(0, 0, canvas.width, canvas.height);
44422 ctx.fillRect(0, 0, canvas.width, canvas.height);
44423 this.curve_data = [];
44425 this.is_empty = true;
44430 return this.el.select('input',true).first();
44433 canvasEl: function()
44435 return this.el.select('canvas',true).first();
44438 canvasElCtx: function()
44440 return this.el.select('canvas',true).first().dom.getContext('2d');
44443 getImage: function(type)
44445 if(this.is_empty) {
44450 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44453 drawFromImage: function(img_src)
44455 var img = new Image();
44457 img.onload = function(){
44458 this.canvasElCtx().drawImage(img, 0, 0);
44463 this.is_empty = false;
44466 selectImage: function()
44468 this.fileEl().dom.click();
44471 uploadImage: function(e)
44473 var reader = new FileReader();
44475 reader.onload = function(e){
44476 var img = new Image();
44477 img.onload = function(){
44479 this.canvasElCtx().drawImage(img, 0, 0);
44481 img.src = e.target.result;
44484 reader.readAsDataURL(e.target.files[0]);
44487 // Bezier Point Constructor
44488 Point: (function () {
44489 function Point(x, y, time) {
44492 this.time = time || Date.now();
44494 Point.prototype.distanceTo = function (start) {
44495 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44497 Point.prototype.equals = function (other) {
44498 return this.x === other.x && this.y === other.y && this.time === other.time;
44500 Point.prototype.velocityFrom = function (start) {
44501 return this.time !== start.time
44502 ? this.distanceTo(start) / (this.time - start.time)
44509 // Bezier Constructor
44510 Bezier: (function () {
44511 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44512 this.startPoint = startPoint;
44513 this.control2 = control2;
44514 this.control1 = control1;
44515 this.endPoint = endPoint;
44516 this.startWidth = startWidth;
44517 this.endWidth = endWidth;
44519 Bezier.fromPoints = function (points, widths, scope) {
44520 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44521 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44522 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44524 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44525 var dx1 = s1.x - s2.x;
44526 var dy1 = s1.y - s2.y;
44527 var dx2 = s2.x - s3.x;
44528 var dy2 = s2.y - s3.y;
44529 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44530 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44531 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44532 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44533 var dxm = m1.x - m2.x;
44534 var dym = m1.y - m2.y;
44535 var k = l2 / (l1 + l2);
44536 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44537 var tx = s2.x - cm.x;
44538 var ty = s2.y - cm.y;
44540 c1: new scope.Point(m1.x + tx, m1.y + ty),
44541 c2: new scope.Point(m2.x + tx, m2.y + ty)
44544 Bezier.prototype.length = function () {
44549 for (var i = 0; i <= steps; i += 1) {
44551 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44552 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44554 var xdiff = cx - px;
44555 var ydiff = cy - py;
44556 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44563 Bezier.prototype.point = function (t, start, c1, c2, end) {
44564 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44565 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44566 + (3.0 * c2 * (1.0 - t) * t * t)
44567 + (end * t * t * t);
44572 throttleStroke: function(fn, wait) {
44573 if (wait === void 0) { wait = 250; }
44575 var timeout = null;
44579 var later = function () {
44580 previous = Date.now();
44582 result = fn.apply(storedContext, storedArgs);
44584 storedContext = null;
44588 return function wrapper() {
44590 for (var _i = 0; _i < arguments.length; _i++) {
44591 args[_i] = arguments[_i];
44593 var now = Date.now();
44594 var remaining = wait - (now - previous);
44595 storedContext = this;
44597 if (remaining <= 0 || remaining > wait) {
44599 clearTimeout(timeout);
44603 result = fn.apply(storedContext, storedArgs);
44605 storedContext = null;
44609 else if (!timeout) {
44610 timeout = window.setTimeout(later, remaining);