4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
21 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
24 * Do not use directly - it does not do anything..
25 * @param {Object} config The config object
30 Roo.bootstrap.Component = function(config){
31 Roo.bootstrap.Component.superclass.constructor.call(this, config);
35 * @event childrenrendered
36 * Fires when the children have been rendered..
37 * @param {Roo.bootstrap.Component} this
39 "childrenrendered" : true
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
51 allowDomMove : false, // to stop relocations in parent onRender...
61 * Initialize Events for the element
63 initEvents : function() { },
69 can_build_overlaid : true,
71 container_method : false,
78 // returns the parent component..
79 return Roo.ComponentMgr.get(this.parentId)
85 onRender : function(ct, position)
87 // Roo.log("Call onRender: " + this.xtype);
89 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
92 if (this.el.attr('xtype')) {
93 this.el.attr('xtypex', this.el.attr('xtype'));
94 this.el.dom.removeAttribute('xtype');
104 var cfg = Roo.apply({}, this.getAutoCreate());
106 cfg.id = this.id || Roo.id();
108 // fill in the extra attributes
109 if (this.xattr && typeof(this.xattr) =='object') {
110 for (var i in this.xattr) {
111 cfg[i] = this.xattr[i];
116 cfg.dataId = this.dataId;
120 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
123 if (this.style) { // fixme needs to support more complex style data.
124 cfg.style = this.style;
128 cfg.name = this.name;
131 this.el = ct.createChild(cfg, position);
134 this.tooltipEl().attr('tooltip', this.tooltip);
137 if(this.tabIndex !== undefined){
138 this.el.dom.setAttribute('tabIndex', this.tabIndex);
145 * Fetch the element to add children to
146 * @return {Roo.Element} defaults to this.el
148 getChildContainer : function()
153 * Fetch the element to display the tooltip on.
154 * @return {Roo.Element} defaults to this.el
156 tooltipEl : function()
161 addxtype : function(tree,cntr)
165 cn = Roo.factory(tree);
166 //Roo.log(['addxtype', cn]);
168 cn.parentType = this.xtype; //??
169 cn.parentId = this.id;
171 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172 if (typeof(cn.container_method) == 'string') {
173 cntr = cn.container_method;
177 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
179 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
181 var build_from_html = Roo.XComponent.build_from_html;
183 var is_body = (tree.xtype == 'Body') ;
185 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
187 var self_cntr_el = Roo.get(this[cntr](false));
189 // do not try and build conditional elements
190 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
194 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196 return this.addxtypeChild(tree,cntr, is_body);
199 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
202 return this.addxtypeChild(Roo.apply({}, tree),cntr);
205 Roo.log('skipping render');
211 if (!build_from_html) {
215 // this i think handles overlaying multiple children of the same type
216 // with the sam eelement.. - which might be buggy..
218 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
224 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
228 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
235 addxtypeChild : function (tree, cntr, is_body)
237 Roo.debug && Roo.log('addxtypeChild:' + cntr);
239 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
242 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243 (typeof(tree['flexy:foreach']) != 'undefined');
247 skip_children = false;
248 // render the element if it's not BODY.
251 // if parent was disabled, then do not try and create the children..
252 if(!this[cntr](true)){
257 cn = Roo.factory(tree);
259 cn.parentType = this.xtype; //??
260 cn.parentId = this.id;
262 var build_from_html = Roo.XComponent.build_from_html;
265 // does the container contain child eleemnts with 'xtype' attributes.
266 // that match this xtype..
267 // note - when we render we create these as well..
268 // so we should check to see if body has xtype set.
269 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
271 var self_cntr_el = Roo.get(this[cntr](false));
272 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
274 //Roo.log(Roo.XComponent.build_from_html);
275 //Roo.log("got echild:");
278 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279 // and are not displayed -this causes this to use up the wrong element when matching.
280 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
283 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
290 //echild.dom.removeAttribute('xtype');
292 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293 Roo.debug && Roo.log(self_cntr_el);
294 Roo.debug && Roo.log(echild);
295 Roo.debug && Roo.log(cn);
301 // if object has flexy:if - then it may or may not be rendered.
302 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
303 // skip a flexy if element.
304 Roo.debug && Roo.log('skipping render');
305 Roo.debug && Roo.log(tree);
307 Roo.debug && Roo.log('skipping all children');
308 skip_children = true;
313 // actually if flexy:foreach is found, we really want to create
314 // multiple copies here...
316 //Roo.log(this[cntr]());
317 // some elements do not have render methods.. like the layouts...
319 if(this[cntr](true) === false){
324 cn.render && cn.render(this[cntr](true));
327 // then add the element..
334 if (typeof (tree.menu) != 'undefined') {
335 tree.menu.parentType = cn.xtype;
336 tree.menu.triggerEl = cn.el;
337 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
341 if (!tree.items || !tree.items.length) {
343 //Roo.log(["no children", this]);
348 var items = tree.items;
351 //Roo.log(items.length);
353 if (!skip_children) {
354 for(var i =0;i < items.length;i++) {
355 // Roo.log(['add child', items[i]]);
356 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
362 //Roo.log("fire childrenrendered");
364 cn.fireEvent('childrenrendered', this);
370 * Set the element that will be used to show or hide
372 setVisibilityEl : function(el)
374 this.visibilityEl = el;
378 * Get the element that will be used to show or hide
380 getVisibilityEl : function()
382 if (typeof(this.visibilityEl) == 'object') {
383 return this.visibilityEl;
386 if (typeof(this.visibilityEl) == 'string') {
387 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
394 * Show a component - removes 'hidden' class
398 if(!this.getVisibilityEl()){
402 this.getVisibilityEl().removeClass('hidden');
407 * Hide a component - adds 'hidden' class
411 if(!this.getVisibilityEl()){
415 this.getVisibilityEl().addClass('hidden');
428 * @class Roo.bootstrap.Body
429 * @extends Roo.bootstrap.Component
430 * Bootstrap Body class
434 * @param {Object} config The config object
437 Roo.bootstrap.Body = function(config){
439 config = config || {};
441 Roo.bootstrap.Body.superclass.constructor.call(this, config);
442 this.el = Roo.get(config.el ? config.el : document.body );
443 if (this.cls && this.cls.length) {
444 Roo.get(document.body).addClass(this.cls);
448 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
450 is_body : true,// just to make sure it's constructed?
455 onRender : function(ct, position)
457 /* Roo.log("Roo.bootstrap.Body - onRender");
458 if (this.cls && this.cls.length) {
459 Roo.get(document.body).addClass(this.cls);
478 * @class Roo.bootstrap.ButtonGroup
479 * @extends Roo.bootstrap.Component
480 * Bootstrap ButtonGroup class
481 * @cfg {String} size lg | sm | xs (default empty normal)
482 * @cfg {String} align vertical | justified (default none)
483 * @cfg {String} direction up | down (default down)
484 * @cfg {Boolean} toolbar false | true
485 * @cfg {Boolean} btn true | false
490 * @param {Object} config The config object
493 Roo.bootstrap.ButtonGroup = function(config){
494 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
497 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
505 getAutoCreate : function(){
511 cfg.html = this.html || cfg.html;
522 if (['vertical','justified'].indexOf(this.align)!==-1) {
523 cfg.cls = 'btn-group-' + this.align;
525 if (this.align == 'justified') {
526 console.log(this.items);
530 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
531 cfg.cls += ' btn-group-' + this.size;
534 if (this.direction == 'up') {
535 cfg.cls += ' dropup' ;
551 * @class Roo.bootstrap.Button
552 * @extends Roo.bootstrap.Component
553 * Bootstrap Button class
554 * @cfg {String} html The button content
555 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
556 * @cfg {String} size ( lg | sm | xs)
557 * @cfg {String} tag ( a | input | submit)
558 * @cfg {String} href empty or href
559 * @cfg {Boolean} disabled default false;
560 * @cfg {Boolean} isClose default false;
561 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
562 * @cfg {String} badge text for badge
563 * @cfg {String} theme default
564 * @cfg {Boolean} inverse
565 * @cfg {Boolean} toggle is it a slidy toggle button
566 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
567 * @cfg {String} ontext text for on toggle state
568 * @cfg {String} offtext text for off toggle state
569 * @cfg {Boolean} defaulton
570 * @cfg {Boolean} preventDefault default true
571 * @cfg {Boolean} removeClass remove the standard class..
572 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
575 * Create a new button
576 * @param {Object} config The config object
580 Roo.bootstrap.Button = function(config){
581 Roo.bootstrap.Button.superclass.constructor.call(this, config);
582 this.weightClass = ["btn-default",
594 * When a butotn is pressed
595 * @param {Roo.bootstrap.Button} btn
596 * @param {Roo.EventObject} e
601 * After the button has been toggles
602 * @param {Roo.bootstrap.Button} btn
603 * @param {Roo.EventObject} e
604 * @param {boolean} pressed (also available as button.pressed)
610 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
628 preventDefault: true,
637 getAutoCreate : function(){
645 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
646 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
651 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
653 if (this.toggle == true) {
656 cls: 'slider-frame roo-button',
661 'data-off-text':'OFF',
662 cls: 'slider-button',
668 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
669 cfg.cls += ' '+this.weight;
678 cfg["aria-hidden"] = true;
680 cfg.html = "×";
686 if (this.theme==='default') {
687 cfg.cls = 'btn roo-button';
689 //if (this.parentType != 'Navbar') {
690 this.weight = this.weight.length ? this.weight : 'default';
692 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
694 cfg.cls += ' btn-' + this.weight;
696 } else if (this.theme==='glow') {
699 cfg.cls = 'btn-glow roo-button';
701 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
703 cfg.cls += ' ' + this.weight;
709 this.cls += ' inverse';
713 if (this.active || this.pressed === true) {
714 cfg.cls += ' active';
718 cfg.disabled = 'disabled';
722 Roo.log('changing to ul' );
724 this.glyphicon = 'caret';
727 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
729 //gsRoo.log(this.parentType);
730 if (this.parentType === 'Navbar' && !this.parent().bar) {
731 Roo.log('changing to li?');
740 href : this.href || '#'
743 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
744 cfg.cls += ' dropdown';
751 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
753 if (this.glyphicon) {
754 cfg.html = ' ' + cfg.html;
759 cls: 'glyphicon glyphicon-' + this.glyphicon
769 // cfg.cls='btn roo-button';
773 var value = cfg.html;
778 cls: 'glyphicon glyphicon-' + this.glyphicon,
797 cfg.cls += ' dropdown';
798 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
801 if (cfg.tag !== 'a' && this.href !== '') {
802 throw "Tag must be a to set href.";
803 } else if (this.href.length > 0) {
804 cfg.href = this.href;
807 if(this.removeClass){
812 cfg.target = this.target;
817 initEvents: function() {
818 // Roo.log('init events?');
819 // Roo.log(this.el.dom);
822 if (typeof (this.menu) != 'undefined') {
823 this.menu.parentType = this.xtype;
824 this.menu.triggerEl = this.el;
825 this.addxtype(Roo.apply({}, this.menu));
829 if (this.el.hasClass('roo-button')) {
830 this.el.on('click', this.onClick, this);
832 this.el.select('.roo-button').on('click', this.onClick, this);
835 if(this.removeClass){
836 this.el.on('click', this.onClick, this);
839 this.el.enableDisplayMode();
842 onClick : function(e)
849 Roo.log('button on click ');
850 if(this.preventDefault){
854 if (this.pressed === true || this.pressed === false) {
855 this.pressed = !this.pressed;
856 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
857 this.fireEvent('toggle', this, e, this.pressed);
861 this.fireEvent('click', this, e);
865 * Enables this button
869 this.disabled = false;
870 this.el.removeClass('disabled');
874 * Disable this button
878 this.disabled = true;
879 this.el.addClass('disabled');
882 * sets the active state on/off,
883 * @param {Boolean} state (optional) Force a particular state
885 setActive : function(v) {
887 this.el[v ? 'addClass' : 'removeClass']('active');
891 * toggles the current active state
893 toggleActive : function()
895 var active = this.el.hasClass('active');
896 this.setActive(!active);
901 * get the current active state
902 * @return {boolean} true if it's active
904 isActive : function()
906 return this.el.hasClass('active');
909 * set the text of the first selected button
911 setText : function(str)
913 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
916 * get the text of the first selected button
920 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
931 setWeight : function(str)
933 this.el.removeClass(this.weightClass);
934 this.el.addClass('btn-' + str);
948 * @class Roo.bootstrap.Column
949 * @extends Roo.bootstrap.Component
950 * Bootstrap Column class
951 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
952 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
953 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
954 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
955 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
956 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
957 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
958 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
961 * @cfg {Boolean} hidden (true|false) hide the element
962 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
963 * @cfg {String} fa (ban|check|...) font awesome icon
964 * @cfg {Number} fasize (1|2|....) font awsome size
966 * @cfg {String} icon (info-sign|check|...) glyphicon name
968 * @cfg {String} html content of column.
971 * Create a new Column
972 * @param {Object} config The config object
975 Roo.bootstrap.Column = function(config){
976 Roo.bootstrap.Column.superclass.constructor.call(this, config);
979 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
997 getAutoCreate : function(){
998 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1006 ['xs','sm','md','lg'].map(function(size){
1007 //Roo.log( size + ':' + settings[size]);
1009 if (settings[size+'off'] !== false) {
1010 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1013 if (settings[size] === false) {
1017 if (!settings[size]) { // 0 = hidden
1018 cfg.cls += ' hidden-' + size;
1021 cfg.cls += ' col-' + size + '-' + settings[size];
1026 cfg.cls += ' hidden';
1029 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1030 cfg.cls +=' alert alert-' + this.alert;
1034 if (this.html.length) {
1035 cfg.html = this.html;
1039 if (this.fasize > 1) {
1040 fasize = ' fa-' + this.fasize + 'x';
1042 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1047 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1066 * @class Roo.bootstrap.Container
1067 * @extends Roo.bootstrap.Component
1068 * Bootstrap Container class
1069 * @cfg {Boolean} jumbotron is it a jumbotron element
1070 * @cfg {String} html content of element
1071 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1072 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1073 * @cfg {String} header content of header (for panel)
1074 * @cfg {String} footer content of footer (for panel)
1075 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1076 * @cfg {String} tag (header|aside|section) type of HTML tag.
1077 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1078 * @cfg {String} fa font awesome icon
1079 * @cfg {String} icon (info-sign|check|...) glyphicon name
1080 * @cfg {Boolean} hidden (true|false) hide the element
1081 * @cfg {Boolean} expandable (true|false) default false
1082 * @cfg {Boolean} expanded (true|false) default true
1083 * @cfg {String} rheader contet on the right of header
1084 * @cfg {Boolean} clickable (true|false) default false
1088 * Create a new Container
1089 * @param {Object} config The config object
1092 Roo.bootstrap.Container = function(config){
1093 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1099 * After the panel has been expand
1101 * @param {Roo.bootstrap.Container} this
1106 * After the panel has been collapsed
1108 * @param {Roo.bootstrap.Container} this
1113 * When a element is chick
1114 * @param {Roo.bootstrap.Container} this
1115 * @param {Roo.EventObject} e
1121 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1139 getChildContainer : function() {
1145 if (this.panel.length) {
1146 return this.el.select('.panel-body',true).first();
1153 getAutoCreate : function(){
1156 tag : this.tag || 'div',
1160 if (this.jumbotron) {
1161 cfg.cls = 'jumbotron';
1166 // - this is applied by the parent..
1168 // cfg.cls = this.cls + '';
1171 if (this.sticky.length) {
1173 var bd = Roo.get(document.body);
1174 if (!bd.hasClass('bootstrap-sticky')) {
1175 bd.addClass('bootstrap-sticky');
1176 Roo.select('html',true).setStyle('height', '100%');
1179 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1183 if (this.well.length) {
1184 switch (this.well) {
1187 cfg.cls +=' well well-' +this.well;
1196 cfg.cls += ' hidden';
1200 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1201 cfg.cls +=' alert alert-' + this.alert;
1206 if (this.panel.length) {
1207 cfg.cls += ' panel panel-' + this.panel;
1209 if (this.header.length) {
1213 if(this.expandable){
1215 cfg.cls = cfg.cls + ' expandable';
1219 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1227 cls : 'panel-title',
1228 html : (this.expandable ? ' ' : '') + this.header
1232 cls: 'panel-header-right',
1238 cls : 'panel-heading',
1239 style : this.expandable ? 'cursor: pointer' : '',
1247 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1252 if (this.footer.length) {
1254 cls : 'panel-footer',
1263 body.html = this.html || cfg.html;
1264 // prefix with the icons..
1266 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1269 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1274 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1275 cfg.cls = 'container';
1281 initEvents: function()
1283 if(this.expandable){
1284 var headerEl = this.headerEl();
1287 headerEl.on('click', this.onToggleClick, this);
1292 this.el.on('click', this.onClick, this);
1297 onToggleClick : function()
1299 var headerEl = this.headerEl();
1315 if(this.fireEvent('expand', this)) {
1317 this.expanded = true;
1319 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1321 this.el.select('.panel-body',true).first().removeClass('hide');
1323 var toggleEl = this.toggleEl();
1329 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1334 collapse : function()
1336 if(this.fireEvent('collapse', this)) {
1338 this.expanded = false;
1340 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1341 this.el.select('.panel-body',true).first().addClass('hide');
1343 var toggleEl = this.toggleEl();
1349 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1353 toggleEl : function()
1355 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1359 return this.el.select('.panel-heading .fa',true).first();
1362 headerEl : function()
1364 if(!this.el || !this.panel.length || !this.header.length){
1368 return this.el.select('.panel-heading',true).first()
1373 if(!this.el || !this.panel.length){
1377 return this.el.select('.panel-body',true).first()
1380 titleEl : function()
1382 if(!this.el || !this.panel.length || !this.header.length){
1386 return this.el.select('.panel-title',true).first();
1389 setTitle : function(v)
1391 var titleEl = this.titleEl();
1397 titleEl.dom.innerHTML = v;
1400 getTitle : function()
1403 var titleEl = this.titleEl();
1409 return titleEl.dom.innerHTML;
1412 setRightTitle : function(v)
1414 var t = this.el.select('.panel-header-right',true).first();
1420 t.dom.innerHTML = v;
1423 onClick : function(e)
1427 this.fireEvent('click', this, e);
1440 * @class Roo.bootstrap.Img
1441 * @extends Roo.bootstrap.Component
1442 * Bootstrap Img class
1443 * @cfg {Boolean} imgResponsive false | true
1444 * @cfg {String} border rounded | circle | thumbnail
1445 * @cfg {String} src image source
1446 * @cfg {String} alt image alternative text
1447 * @cfg {String} href a tag href
1448 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1449 * @cfg {String} xsUrl xs image source
1450 * @cfg {String} smUrl sm image source
1451 * @cfg {String} mdUrl md image source
1452 * @cfg {String} lgUrl lg image source
1455 * Create a new Input
1456 * @param {Object} config The config object
1459 Roo.bootstrap.Img = function(config){
1460 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1466 * The img click event for the img.
1467 * @param {Roo.EventObject} e
1473 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1475 imgResponsive: true,
1485 getAutoCreate : function()
1487 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1488 return this.createSingleImg();
1493 cls: 'roo-image-responsive-group',
1498 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1500 if(!_this[size + 'Url']){
1506 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1507 html: _this.html || cfg.html,
1508 src: _this[size + 'Url']
1511 img.cls += ' roo-image-responsive-' + size;
1513 var s = ['xs', 'sm', 'md', 'lg'];
1515 s.splice(s.indexOf(size), 1);
1517 Roo.each(s, function(ss){
1518 img.cls += ' hidden-' + ss;
1521 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1522 cfg.cls += ' img-' + _this.border;
1526 cfg.alt = _this.alt;
1539 a.target = _this.target;
1543 cfg.cn.push((_this.href) ? a : img);
1550 createSingleImg : function()
1554 cls: (this.imgResponsive) ? 'img-responsive' : '',
1556 src : 'about:blank' // just incase src get's set to undefined?!?
1559 cfg.html = this.html || cfg.html;
1561 cfg.src = this.src || cfg.src;
1563 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1564 cfg.cls += ' img-' + this.border;
1581 a.target = this.target;
1586 return (this.href) ? a : cfg;
1589 initEvents: function()
1592 this.el.on('click', this.onClick, this);
1597 onClick : function(e)
1599 Roo.log('img onclick');
1600 this.fireEvent('click', this, e);
1603 * Sets the url of the image - used to update it
1604 * @param {String} url the url of the image
1607 setSrc : function(url)
1611 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1612 this.el.dom.src = url;
1616 this.el.select('img', true).first().dom.src = url;
1632 * @class Roo.bootstrap.Link
1633 * @extends Roo.bootstrap.Component
1634 * Bootstrap Link Class
1635 * @cfg {String} alt image alternative text
1636 * @cfg {String} href a tag href
1637 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1638 * @cfg {String} html the content of the link.
1639 * @cfg {String} anchor name for the anchor link
1640 * @cfg {String} fa - favicon
1642 * @cfg {Boolean} preventDefault (true | false) default false
1646 * Create a new Input
1647 * @param {Object} config The config object
1650 Roo.bootstrap.Link = function(config){
1651 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1657 * The img click event for the img.
1658 * @param {Roo.EventObject} e
1664 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1668 preventDefault: false,
1674 getAutoCreate : function()
1676 var html = this.html || '';
1678 if (this.fa !== false) {
1679 html = '<i class="fa fa-' + this.fa + '"></i>';
1684 // anchor's do not require html/href...
1685 if (this.anchor === false) {
1687 cfg.href = this.href || '#';
1689 cfg.name = this.anchor;
1690 if (this.html !== false || this.fa !== false) {
1693 if (this.href !== false) {
1694 cfg.href = this.href;
1698 if(this.alt !== false){
1703 if(this.target !== false) {
1704 cfg.target = this.target;
1710 initEvents: function() {
1712 if(!this.href || this.preventDefault){
1713 this.el.on('click', this.onClick, this);
1717 onClick : function(e)
1719 if(this.preventDefault){
1722 //Roo.log('img onclick');
1723 this.fireEvent('click', this, e);
1736 * @class Roo.bootstrap.Header
1737 * @extends Roo.bootstrap.Component
1738 * Bootstrap Header class
1739 * @cfg {String} html content of header
1740 * @cfg {Number} level (1|2|3|4|5|6) default 1
1743 * Create a new Header
1744 * @param {Object} config The config object
1748 Roo.bootstrap.Header = function(config){
1749 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1752 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1760 getAutoCreate : function(){
1765 tag: 'h' + (1 *this.level),
1766 html: this.html || ''
1778 * Ext JS Library 1.1.1
1779 * Copyright(c) 2006-2007, Ext JS, LLC.
1781 * Originally Released Under LGPL - original licence link has changed is not relivant.
1784 * <script type="text/javascript">
1788 * @class Roo.bootstrap.MenuMgr
1789 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1792 Roo.bootstrap.MenuMgr = function(){
1793 var menus, active, groups = {}, attached = false, lastShow = new Date();
1795 // private - called when first menu is created
1798 active = new Roo.util.MixedCollection();
1799 Roo.get(document).addKeyListener(27, function(){
1800 if(active.length > 0){
1808 if(active && active.length > 0){
1809 var c = active.clone();
1819 if(active.length < 1){
1820 Roo.get(document).un("mouseup", onMouseDown);
1828 var last = active.last();
1829 lastShow = new Date();
1832 Roo.get(document).on("mouseup", onMouseDown);
1837 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1838 m.parentMenu.activeChild = m;
1839 }else if(last && last.isVisible()){
1840 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1845 function onBeforeHide(m){
1847 m.activeChild.hide();
1849 if(m.autoHideTimer){
1850 clearTimeout(m.autoHideTimer);
1851 delete m.autoHideTimer;
1856 function onBeforeShow(m){
1857 var pm = m.parentMenu;
1858 if(!pm && !m.allowOtherMenus){
1860 }else if(pm && pm.activeChild && active != m){
1861 pm.activeChild.hide();
1865 // private this should really trigger on mouseup..
1866 function onMouseDown(e){
1867 Roo.log("on Mouse Up");
1869 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1870 Roo.log("MenuManager hideAll");
1879 function onBeforeCheck(mi, state){
1881 var g = groups[mi.group];
1882 for(var i = 0, l = g.length; i < l; i++){
1884 g[i].setChecked(false);
1893 * Hides all menus that are currently visible
1895 hideAll : function(){
1900 register : function(menu){
1904 menus[menu.id] = menu;
1905 menu.on("beforehide", onBeforeHide);
1906 menu.on("hide", onHide);
1907 menu.on("beforeshow", onBeforeShow);
1908 menu.on("show", onShow);
1910 if(g && menu.events["checkchange"]){
1914 groups[g].push(menu);
1915 menu.on("checkchange", onCheck);
1920 * Returns a {@link Roo.menu.Menu} object
1921 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1922 * be used to generate and return a new Menu instance.
1924 get : function(menu){
1925 if(typeof menu == "string"){ // menu id
1927 }else if(menu.events){ // menu instance
1930 /*else if(typeof menu.length == 'number'){ // array of menu items?
1931 return new Roo.bootstrap.Menu({items:menu});
1932 }else{ // otherwise, must be a config
1933 return new Roo.bootstrap.Menu(menu);
1940 unregister : function(menu){
1941 delete menus[menu.id];
1942 menu.un("beforehide", onBeforeHide);
1943 menu.un("hide", onHide);
1944 menu.un("beforeshow", onBeforeShow);
1945 menu.un("show", onShow);
1947 if(g && menu.events["checkchange"]){
1948 groups[g].remove(menu);
1949 menu.un("checkchange", onCheck);
1954 registerCheckable : function(menuItem){
1955 var g = menuItem.group;
1960 groups[g].push(menuItem);
1961 menuItem.on("beforecheckchange", onBeforeCheck);
1966 unregisterCheckable : function(menuItem){
1967 var g = menuItem.group;
1969 groups[g].remove(menuItem);
1970 menuItem.un("beforecheckchange", onBeforeCheck);
1982 * @class Roo.bootstrap.Menu
1983 * @extends Roo.bootstrap.Component
1984 * Bootstrap Menu class - container for MenuItems
1985 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1986 * @cfg {bool} hidden if the menu should be hidden when rendered.
1987 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1988 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1992 * @param {Object} config The config object
1996 Roo.bootstrap.Menu = function(config){
1997 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1998 if (this.registerMenu && this.type != 'treeview') {
1999 Roo.bootstrap.MenuMgr.register(this);
2004 * Fires before this menu is displayed
2005 * @param {Roo.menu.Menu} this
2010 * Fires before this menu is hidden
2011 * @param {Roo.menu.Menu} this
2016 * Fires after this menu is displayed
2017 * @param {Roo.menu.Menu} this
2022 * Fires after this menu is hidden
2023 * @param {Roo.menu.Menu} this
2028 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2029 * @param {Roo.menu.Menu} this
2030 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2031 * @param {Roo.EventObject} e
2036 * Fires when the mouse is hovering over this menu
2037 * @param {Roo.menu.Menu} this
2038 * @param {Roo.EventObject} e
2039 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2044 * Fires when the mouse exits this menu
2045 * @param {Roo.menu.Menu} this
2046 * @param {Roo.EventObject} e
2047 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2052 * Fires when a menu item contained in this menu is clicked
2053 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2054 * @param {Roo.EventObject} e
2058 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2061 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2065 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2068 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2070 registerMenu : true,
2072 menuItems :false, // stores the menu items..
2082 getChildContainer : function() {
2086 getAutoCreate : function(){
2088 //if (['right'].indexOf(this.align)!==-1) {
2089 // cfg.cn[1].cls += ' pull-right'
2095 cls : 'dropdown-menu' ,
2096 style : 'z-index:1000'
2100 if (this.type === 'submenu') {
2101 cfg.cls = 'submenu active';
2103 if (this.type === 'treeview') {
2104 cfg.cls = 'treeview-menu';
2109 initEvents : function() {
2111 // Roo.log("ADD event");
2112 // Roo.log(this.triggerEl.dom);
2114 this.triggerEl.on('click', this.onTriggerClick, this);
2116 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2118 this.triggerEl.addClass('dropdown-toggle');
2121 this.el.on('touchstart' , this.onTouch, this);
2123 this.el.on('click' , this.onClick, this);
2125 this.el.on("mouseover", this.onMouseOver, this);
2126 this.el.on("mouseout", this.onMouseOut, this);
2130 findTargetItem : function(e)
2132 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2136 //Roo.log(t); Roo.log(t.id);
2138 //Roo.log(this.menuitems);
2139 return this.menuitems.get(t.id);
2141 //return this.items.get(t.menuItemId);
2147 onTouch : function(e)
2149 Roo.log("menu.onTouch");
2150 //e.stopEvent(); this make the user popdown broken
2154 onClick : function(e)
2156 Roo.log("menu.onClick");
2158 var t = this.findTargetItem(e);
2159 if(!t || t.isContainer){
2164 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2165 if(t == this.activeItem && t.shouldDeactivate(e)){
2166 this.activeItem.deactivate();
2167 delete this.activeItem;
2171 this.setActiveItem(t, true);
2179 Roo.log('pass click event');
2183 this.fireEvent("click", this, t, e);
2187 if(!t.href.length || t.href == '#'){
2188 (function() { _this.hide(); }).defer(100);
2193 onMouseOver : function(e){
2194 var t = this.findTargetItem(e);
2197 // if(t.canActivate && !t.disabled){
2198 // this.setActiveItem(t, true);
2202 this.fireEvent("mouseover", this, e, t);
2204 isVisible : function(){
2205 return !this.hidden;
2207 onMouseOut : function(e){
2208 var t = this.findTargetItem(e);
2211 // if(t == this.activeItem && t.shouldDeactivate(e)){
2212 // this.activeItem.deactivate();
2213 // delete this.activeItem;
2216 this.fireEvent("mouseout", this, e, t);
2221 * Displays this menu relative to another element
2222 * @param {String/HTMLElement/Roo.Element} element The element to align to
2223 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2224 * the element (defaults to this.defaultAlign)
2225 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2227 show : function(el, pos, parentMenu){
2228 this.parentMenu = parentMenu;
2232 this.fireEvent("beforeshow", this);
2233 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2236 * Displays this menu at a specific xy position
2237 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2238 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2240 showAt : function(xy, parentMenu, /* private: */_e){
2241 this.parentMenu = parentMenu;
2246 this.fireEvent("beforeshow", this);
2247 //xy = this.el.adjustForConstraints(xy);
2251 this.hideMenuItems();
2252 this.hidden = false;
2253 this.triggerEl.addClass('open');
2255 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2256 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2259 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2264 this.fireEvent("show", this);
2270 this.doFocus.defer(50, this);
2274 doFocus : function(){
2276 this.focusEl.focus();
2281 * Hides this menu and optionally all parent menus
2282 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2284 hide : function(deep)
2287 this.hideMenuItems();
2288 if(this.el && this.isVisible()){
2289 this.fireEvent("beforehide", this);
2290 if(this.activeItem){
2291 this.activeItem.deactivate();
2292 this.activeItem = null;
2294 this.triggerEl.removeClass('open');;
2296 this.fireEvent("hide", this);
2298 if(deep === true && this.parentMenu){
2299 this.parentMenu.hide(true);
2303 onTriggerClick : function(e)
2305 Roo.log('trigger click');
2307 var target = e.getTarget();
2309 Roo.log(target.nodeName.toLowerCase());
2311 if(target.nodeName.toLowerCase() === 'i'){
2317 onTriggerPress : function(e)
2319 Roo.log('trigger press');
2320 //Roo.log(e.getTarget());
2321 // Roo.log(this.triggerEl.dom);
2323 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2324 var pel = Roo.get(e.getTarget());
2325 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2326 Roo.log('is treeview or dropdown?');
2330 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2334 if (this.isVisible()) {
2339 this.show(this.triggerEl, false, false);
2342 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2349 hideMenuItems : function()
2351 Roo.log("hide Menu Items");
2355 //$(backdrop).remove()
2356 this.el.select('.open',true).each(function(aa) {
2358 aa.removeClass('open');
2359 //var parent = getParent($(this))
2360 //var relatedTarget = { relatedTarget: this }
2362 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2363 //if (e.isDefaultPrevented()) return
2364 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2367 addxtypeChild : function (tree, cntr) {
2368 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2370 this.menuitems.add(comp);
2382 this.getEl().dom.innerHTML = '';
2383 this.menuitems.clear();
2397 * @class Roo.bootstrap.MenuItem
2398 * @extends Roo.bootstrap.Component
2399 * Bootstrap MenuItem class
2400 * @cfg {String} html the menu label
2401 * @cfg {String} href the link
2402 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2403 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2404 * @cfg {Boolean} active used on sidebars to highlight active itesm
2405 * @cfg {String} fa favicon to show on left of menu item.
2406 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2410 * Create a new MenuItem
2411 * @param {Object} config The config object
2415 Roo.bootstrap.MenuItem = function(config){
2416 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2421 * The raw click event for the entire grid.
2422 * @param {Roo.bootstrap.MenuItem} this
2423 * @param {Roo.EventObject} e
2429 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2433 preventDefault: false,
2434 isContainer : false,
2438 getAutoCreate : function(){
2440 if(this.isContainer){
2443 cls: 'dropdown-menu-item'
2457 if (this.fa !== false) {
2460 cls : 'fa fa-' + this.fa
2469 cls: 'dropdown-menu-item',
2472 if (this.parent().type == 'treeview') {
2473 cfg.cls = 'treeview-menu';
2476 cfg.cls += ' active';
2481 anc.href = this.href || cfg.cn[0].href ;
2482 ctag.html = this.html || cfg.cn[0].html ;
2486 initEvents: function()
2488 if (this.parent().type == 'treeview') {
2489 this.el.select('a').on('click', this.onClick, this);
2493 this.menu.parentType = this.xtype;
2494 this.menu.triggerEl = this.el;
2495 this.menu = this.addxtype(Roo.apply({}, this.menu));
2499 onClick : function(e)
2501 Roo.log('item on click ');
2503 if(this.preventDefault){
2506 //this.parent().hideMenuItems();
2508 this.fireEvent('click', this, e);
2527 * @class Roo.bootstrap.MenuSeparator
2528 * @extends Roo.bootstrap.Component
2529 * Bootstrap MenuSeparator class
2532 * Create a new MenuItem
2533 * @param {Object} config The config object
2537 Roo.bootstrap.MenuSeparator = function(config){
2538 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2541 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2543 getAutoCreate : function(){
2562 * @class Roo.bootstrap.Modal
2563 * @extends Roo.bootstrap.Component
2564 * Bootstrap Modal class
2565 * @cfg {String} title Title of dialog
2566 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2567 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2568 * @cfg {Boolean} specificTitle default false
2569 * @cfg {Array} buttons Array of buttons or standard button set..
2570 * @cfg {String} buttonPosition (left|right|center) default right
2571 * @cfg {Boolean} animate default true
2572 * @cfg {Boolean} allow_close default true
2573 * @cfg {Boolean} fitwindow default false
2574 * @cfg {String} size (sm|lg) default empty
2578 * Create a new Modal Dialog
2579 * @param {Object} config The config object
2582 Roo.bootstrap.Modal = function(config){
2583 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2588 * The raw btnclick event for the button
2589 * @param {Roo.EventObject} e
2594 * Fire when dialog resize
2595 * @param {Roo.bootstrap.Modal} this
2596 * @param {Roo.EventObject} e
2600 this.buttons = this.buttons || [];
2603 this.tmpl = Roo.factory(this.tmpl);
2608 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2610 title : 'test dialog',
2620 specificTitle: false,
2622 buttonPosition: 'right',
2641 onRender : function(ct, position)
2643 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2646 var cfg = Roo.apply({}, this.getAutoCreate());
2649 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2651 //if (!cfg.name.length) {
2655 cfg.cls += ' ' + this.cls;
2658 cfg.style = this.style;
2660 this.el = Roo.get(document.body).createChild(cfg, position);
2662 //var type = this.el.dom.type;
2665 if(this.tabIndex !== undefined){
2666 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2669 this.dialogEl = this.el.select('.modal-dialog',true).first();
2670 this.bodyEl = this.el.select('.modal-body',true).first();
2671 this.closeEl = this.el.select('.modal-header .close', true).first();
2672 this.headerEl = this.el.select('.modal-header',true).first();
2673 this.titleEl = this.el.select('.modal-title',true).first();
2674 this.footerEl = this.el.select('.modal-footer',true).first();
2676 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2677 this.maskEl.enableDisplayMode("block");
2679 //this.el.addClass("x-dlg-modal");
2681 if (this.buttons.length) {
2682 Roo.each(this.buttons, function(bb) {
2683 var b = Roo.apply({}, bb);
2684 b.xns = b.xns || Roo.bootstrap;
2685 b.xtype = b.xtype || 'Button';
2686 if (typeof(b.listeners) == 'undefined') {
2687 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2690 var btn = Roo.factory(b);
2692 btn.render(this.el.select('.modal-footer div').first());
2696 // render the children.
2699 if(typeof(this.items) != 'undefined'){
2700 var items = this.items;
2703 for(var i =0;i < items.length;i++) {
2704 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2708 this.items = nitems;
2710 // where are these used - they used to be body/close/footer
2714 //this.el.addClass([this.fieldClass, this.cls]);
2718 getAutoCreate : function(){
2723 html : this.html || ''
2728 cls : 'modal-title',
2732 if(this.specificTitle){
2738 if (this.allow_close) {
2750 if(this.size.length){
2751 size = 'modal-' + this.size;
2758 cls: "modal-dialog " + size,
2761 cls : "modal-content",
2764 cls : 'modal-header',
2769 cls : 'modal-footer',
2773 cls: 'btn-' + this.buttonPosition
2790 modal.cls += ' fade';
2796 getChildContainer : function() {
2801 getButtonContainer : function() {
2802 return this.el.select('.modal-footer div',true).first();
2805 initEvents : function()
2807 if (this.allow_close) {
2808 this.closeEl.on('click', this.hide, this);
2810 Roo.EventManager.onWindowResize(this.resize, this, true);
2817 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2818 if (this.fitwindow) {
2819 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2820 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2825 setSize : function(w,h)
2835 if (!this.rendered) {
2839 //this.el.setStyle('display', 'block');
2840 this.el.addClass('show');
2842 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2845 this.el.addClass('in');
2848 this.el.addClass('in');
2852 // not sure how we can show data in here..
2854 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2857 Roo.get(document.body).addClass("x-body-masked");
2859 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2860 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2865 this.fireEvent('show', this);
2867 // set zindex here - otherwise it appears to be ignored...
2868 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2871 this.items.forEach( function(e) {
2872 e.layout ? e.layout() : false;
2880 if(this.fireEvent("beforehide", this) !== false){
2882 Roo.get(document.body).removeClass("x-body-masked");
2883 this.el.removeClass('in');
2884 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2886 if(this.animate){ // why
2887 this.el.addClass('hideing');
2889 if (!this.el.hasClass('hideing')) {
2890 return; // it's been shown again...
2892 this.el.removeClass('show');
2893 this.el.removeClass('hideing');
2897 this.el.removeClass('show');
2899 this.fireEvent('hide', this);
2902 isVisible : function()
2905 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2909 addButton : function(str, cb)
2913 var b = Roo.apply({}, { html : str } );
2914 b.xns = b.xns || Roo.bootstrap;
2915 b.xtype = b.xtype || 'Button';
2916 if (typeof(b.listeners) == 'undefined') {
2917 b.listeners = { click : cb.createDelegate(this) };
2920 var btn = Roo.factory(b);
2922 btn.render(this.el.select('.modal-footer div').first());
2928 setDefaultButton : function(btn)
2930 //this.el.select('.modal-footer').()
2934 resizeTo: function(w,h)
2938 this.dialogEl.setWidth(w);
2939 if (this.diff === false) {
2940 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2943 this.bodyEl.setHeight(h-this.diff);
2945 this.fireEvent('resize', this);
2948 setContentSize : function(w, h)
2952 onButtonClick: function(btn,e)
2955 this.fireEvent('btnclick', btn.name, e);
2958 * Set the title of the Dialog
2959 * @param {String} str new Title
2961 setTitle: function(str) {
2962 this.titleEl.dom.innerHTML = str;
2965 * Set the body of the Dialog
2966 * @param {String} str new Title
2968 setBody: function(str) {
2969 this.bodyEl.dom.innerHTML = str;
2972 * Set the body of the Dialog using the template
2973 * @param {Obj} data - apply this data to the template and replace the body contents.
2975 applyBody: function(obj)
2978 Roo.log("Error - using apply Body without a template");
2981 this.tmpl.overwrite(this.bodyEl, obj);
2987 Roo.apply(Roo.bootstrap.Modal, {
2989 * Button config that displays a single OK button
2998 * Button config that displays Yes and No buttons
3014 * Button config that displays OK and Cancel buttons
3029 * Button config that displays Yes, No and Cancel buttons
3053 * messagebox - can be used as a replace
3057 * @class Roo.MessageBox
3058 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3062 Roo.Msg.alert('Status', 'Changes saved successfully.');
3064 // Prompt for user data:
3065 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3067 // process text value...
3071 // Show a dialog using config options:
3073 title:'Save Changes?',
3074 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3075 buttons: Roo.Msg.YESNOCANCEL,
3082 Roo.bootstrap.MessageBox = function(){
3083 var dlg, opt, mask, waitTimer;
3084 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3085 var buttons, activeTextEl, bwidth;
3089 var handleButton = function(button){
3091 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3095 var handleHide = function(){
3097 dlg.el.removeClass(opt.cls);
3100 // Roo.TaskMgr.stop(waitTimer);
3101 // waitTimer = null;
3106 var updateButtons = function(b){
3109 buttons["ok"].hide();
3110 buttons["cancel"].hide();
3111 buttons["yes"].hide();
3112 buttons["no"].hide();
3113 //dlg.footer.dom.style.display = 'none';
3116 dlg.footerEl.dom.style.display = '';
3117 for(var k in buttons){
3118 if(typeof buttons[k] != "function"){
3121 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3122 width += buttons[k].el.getWidth()+15;
3132 var handleEsc = function(d, k, e){
3133 if(opt && opt.closable !== false){
3143 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3144 * @return {Roo.BasicDialog} The BasicDialog element
3146 getDialog : function(){
3148 dlg = new Roo.bootstrap.Modal( {
3151 //constraintoviewport:false,
3153 //collapsible : false,
3158 //buttonAlign:"center",
3159 closeClick : function(){
3160 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3163 handleButton("cancel");
3168 dlg.on("hide", handleHide);
3170 //dlg.addKeyListener(27, handleEsc);
3172 this.buttons = buttons;
3173 var bt = this.buttonText;
3174 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3175 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3176 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3177 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3179 bodyEl = dlg.bodyEl.createChild({
3181 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3182 '<textarea class="roo-mb-textarea"></textarea>' +
3183 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3185 msgEl = bodyEl.dom.firstChild;
3186 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3187 textboxEl.enableDisplayMode();
3188 textboxEl.addKeyListener([10,13], function(){
3189 if(dlg.isVisible() && opt && opt.buttons){
3192 }else if(opt.buttons.yes){
3193 handleButton("yes");
3197 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3198 textareaEl.enableDisplayMode();
3199 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3200 progressEl.enableDisplayMode();
3202 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3203 var pf = progressEl.dom.firstChild;
3205 pp = Roo.get(pf.firstChild);
3206 pp.setHeight(pf.offsetHeight);
3214 * Updates the message box body text
3215 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3216 * the XHTML-compliant non-breaking space character '&#160;')
3217 * @return {Roo.MessageBox} This message box
3219 updateText : function(text)
3221 if(!dlg.isVisible() && !opt.width){
3222 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3223 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3225 msgEl.innerHTML = text || ' ';
3227 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3228 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3230 Math.min(opt.width || cw , this.maxWidth),
3231 Math.max(opt.minWidth || this.minWidth, bwidth)
3234 activeTextEl.setWidth(w);
3236 if(dlg.isVisible()){
3237 dlg.fixedcenter = false;
3239 // to big, make it scroll. = But as usual stupid IE does not support
3242 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3243 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3244 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3246 bodyEl.dom.style.height = '';
3247 bodyEl.dom.style.overflowY = '';
3250 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3252 bodyEl.dom.style.overflowX = '';
3255 dlg.setContentSize(w, bodyEl.getHeight());
3256 if(dlg.isVisible()){
3257 dlg.fixedcenter = true;
3263 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3264 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3265 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3266 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3267 * @return {Roo.MessageBox} This message box
3269 updateProgress : function(value, text){
3271 this.updateText(text);
3274 if (pp) { // weird bug on my firefox - for some reason this is not defined
3275 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3276 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3282 * Returns true if the message box is currently displayed
3283 * @return {Boolean} True if the message box is visible, else false
3285 isVisible : function(){
3286 return dlg && dlg.isVisible();
3290 * Hides the message box if it is displayed
3293 if(this.isVisible()){
3299 * Displays a new message box, or reinitializes an existing message box, based on the config options
3300 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3301 * The following config object properties are supported:
3303 Property Type Description
3304 ---------- --------------- ------------------------------------------------------------------------------------
3305 animEl String/Element An id or Element from which the message box should animate as it opens and
3306 closes (defaults to undefined)
3307 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3308 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3309 closable Boolean False to hide the top-right close button (defaults to true). Note that
3310 progress and wait dialogs will ignore this property and always hide the
3311 close button as they can only be closed programmatically.
3312 cls String A custom CSS class to apply to the message box element
3313 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3314 displayed (defaults to 75)
3315 fn Function A callback function to execute after closing the dialog. The arguments to the
3316 function will be btn (the name of the button that was clicked, if applicable,
3317 e.g. "ok"), and text (the value of the active text field, if applicable).
3318 Progress and wait dialogs will ignore this option since they do not respond to
3319 user actions and can only be closed programmatically, so any required function
3320 should be called by the same code after it closes the dialog.
3321 icon String A CSS class that provides a background image to be used as an icon for
3322 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3323 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3324 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3325 modal Boolean False to allow user interaction with the page while the message box is
3326 displayed (defaults to true)
3327 msg String A string that will replace the existing message box body text (defaults
3328 to the XHTML-compliant non-breaking space character ' ')
3329 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3330 progress Boolean True to display a progress bar (defaults to false)
3331 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3332 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3333 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3334 title String The title text
3335 value String The string value to set into the active textbox element if displayed
3336 wait Boolean True to display a progress bar (defaults to false)
3337 width Number The width of the dialog in pixels
3344 msg: 'Please enter your address:',
3346 buttons: Roo.MessageBox.OKCANCEL,
3349 animEl: 'addAddressBtn'
3352 * @param {Object} config Configuration options
3353 * @return {Roo.MessageBox} This message box
3355 show : function(options)
3358 // this causes nightmares if you show one dialog after another
3359 // especially on callbacks..
3361 if(this.isVisible()){
3364 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3365 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3366 Roo.log("New Dialog Message:" + options.msg )
3367 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3368 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3371 var d = this.getDialog();
3373 d.setTitle(opt.title || " ");
3374 d.closeEl.setDisplayed(opt.closable !== false);
3375 activeTextEl = textboxEl;
3376 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3381 textareaEl.setHeight(typeof opt.multiline == "number" ?
3382 opt.multiline : this.defaultTextHeight);
3383 activeTextEl = textareaEl;
3392 progressEl.setDisplayed(opt.progress === true);
3393 this.updateProgress(0);
3394 activeTextEl.dom.value = opt.value || "";
3396 dlg.setDefaultButton(activeTextEl);
3398 var bs = opt.buttons;
3402 }else if(bs && bs.yes){
3403 db = buttons["yes"];
3405 dlg.setDefaultButton(db);
3407 bwidth = updateButtons(opt.buttons);
3408 this.updateText(opt.msg);
3410 d.el.addClass(opt.cls);
3412 d.proxyDrag = opt.proxyDrag === true;
3413 d.modal = opt.modal !== false;
3414 d.mask = opt.modal !== false ? mask : false;
3416 // force it to the end of the z-index stack so it gets a cursor in FF
3417 document.body.appendChild(dlg.el.dom);
3418 d.animateTarget = null;
3419 d.show(options.animEl);
3425 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3426 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3427 * and closing the message box when the process is complete.
3428 * @param {String} title The title bar text
3429 * @param {String} msg The message box body text
3430 * @return {Roo.MessageBox} This message box
3432 progress : function(title, msg){
3439 minWidth: this.minProgressWidth,
3446 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3447 * If a callback function is passed it will be called after the user clicks the button, and the
3448 * id of the button that was clicked will be passed as the only parameter to the callback
3449 * (could also be the top-right close button).
3450 * @param {String} title The title bar text
3451 * @param {String} msg The message box body text
3452 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3453 * @param {Object} scope (optional) The scope of the callback function
3454 * @return {Roo.MessageBox} This message box
3456 alert : function(title, msg, fn, scope)
3471 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3472 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3473 * You are responsible for closing the message box when the process is complete.
3474 * @param {String} msg The message box body text
3475 * @param {String} title (optional) The title bar text
3476 * @return {Roo.MessageBox} This message box
3478 wait : function(msg, title){
3489 waitTimer = Roo.TaskMgr.start({
3491 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3499 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3500 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3501 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3502 * @param {String} title The title bar text
3503 * @param {String} msg The message box body text
3504 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3505 * @param {Object} scope (optional) The scope of the callback function
3506 * @return {Roo.MessageBox} This message box
3508 confirm : function(title, msg, fn, scope){
3512 buttons: this.YESNO,
3521 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3522 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3523 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3524 * (could also be the top-right close button) and the text that was entered will be passed as the two
3525 * parameters to the callback.
3526 * @param {String} title The title bar text
3527 * @param {String} msg The message box body text
3528 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3529 * @param {Object} scope (optional) The scope of the callback function
3530 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3531 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3532 * @return {Roo.MessageBox} This message box
3534 prompt : function(title, msg, fn, scope, multiline){
3538 buttons: this.OKCANCEL,
3543 multiline: multiline,
3550 * Button config that displays a single OK button
3555 * Button config that displays Yes and No buttons
3558 YESNO : {yes:true, no:true},
3560 * Button config that displays OK and Cancel buttons
3563 OKCANCEL : {ok:true, cancel:true},
3565 * Button config that displays Yes, No and Cancel buttons
3568 YESNOCANCEL : {yes:true, no:true, cancel:true},
3571 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3574 defaultTextHeight : 75,
3576 * The maximum width in pixels of the message box (defaults to 600)
3581 * The minimum width in pixels of the message box (defaults to 100)
3586 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3587 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3590 minProgressWidth : 250,
3592 * An object containing the default button text strings that can be overriden for localized language support.
3593 * Supported properties are: ok, cancel, yes and no.
3594 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3607 * Shorthand for {@link Roo.MessageBox}
3609 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3610 Roo.Msg = Roo.Msg || Roo.MessageBox;
3619 * @class Roo.bootstrap.Navbar
3620 * @extends Roo.bootstrap.Component
3621 * Bootstrap Navbar class
3624 * Create a new Navbar
3625 * @param {Object} config The config object
3629 Roo.bootstrap.Navbar = function(config){
3630 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3634 * @event beforetoggle
3635 * Fire before toggle the menu
3636 * @param {Roo.EventObject} e
3638 "beforetoggle" : true
3642 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3651 getAutoCreate : function(){
3654 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3658 initEvents :function ()
3660 //Roo.log(this.el.select('.navbar-toggle',true));
3661 this.el.select('.navbar-toggle',true).on('click', function() {
3662 if(this.fireEvent('beforetoggle', this) !== false){
3663 this.el.select('.navbar-collapse',true).toggleClass('in');
3673 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3675 var size = this.el.getSize();
3676 this.maskEl.setSize(size.width, size.height);
3677 this.maskEl.enableDisplayMode("block");
3686 getChildContainer : function()
3688 if (this.el.select('.collapse').getCount()) {
3689 return this.el.select('.collapse',true).first();
3722 * @class Roo.bootstrap.NavSimplebar
3723 * @extends Roo.bootstrap.Navbar
3724 * Bootstrap Sidebar class
3726 * @cfg {Boolean} inverse is inverted color
3728 * @cfg {String} type (nav | pills | tabs)
3729 * @cfg {Boolean} arrangement stacked | justified
3730 * @cfg {String} align (left | right) alignment
3732 * @cfg {Boolean} main (true|false) main nav bar? default false
3733 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3735 * @cfg {String} tag (header|footer|nav|div) default is nav
3741 * Create a new Sidebar
3742 * @param {Object} config The config object
3746 Roo.bootstrap.NavSimplebar = function(config){
3747 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3750 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3766 getAutoCreate : function(){
3770 tag : this.tag || 'div',
3783 this.type = this.type || 'nav';
3784 if (['tabs','pills'].indexOf(this.type)!==-1) {
3785 cfg.cn[0].cls += ' nav-' + this.type
3789 if (this.type!=='nav') {
3790 Roo.log('nav type must be nav/tabs/pills')
3792 cfg.cn[0].cls += ' navbar-nav'
3798 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3799 cfg.cn[0].cls += ' nav-' + this.arrangement;
3803 if (this.align === 'right') {
3804 cfg.cn[0].cls += ' navbar-right';
3808 cfg.cls += ' navbar-inverse';
3835 * @class Roo.bootstrap.NavHeaderbar
3836 * @extends Roo.bootstrap.NavSimplebar
3837 * Bootstrap Sidebar class
3839 * @cfg {String} brand what is brand
3840 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3841 * @cfg {String} brand_href href of the brand
3842 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3843 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3844 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3845 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3848 * Create a new Sidebar
3849 * @param {Object} config The config object
3853 Roo.bootstrap.NavHeaderbar = function(config){
3854 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3858 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3865 desktopCenter : false,
3868 getAutoCreate : function(){
3871 tag: this.nav || 'nav',
3878 if (this.desktopCenter) {
3879 cn.push({cls : 'container', cn : []});
3886 cls: 'navbar-header',
3891 cls: 'navbar-toggle',
3892 'data-toggle': 'collapse',
3897 html: 'Toggle navigation'
3919 cls: 'collapse navbar-collapse',
3923 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3925 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3926 cfg.cls += ' navbar-' + this.position;
3928 // tag can override this..
3930 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3933 if (this.brand !== '') {
3936 href: this.brand_href ? this.brand_href : '#',
3937 cls: 'navbar-brand',
3945 cfg.cls += ' main-nav';
3953 getHeaderChildContainer : function()
3955 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3956 return this.el.select('.navbar-header',true).first();
3959 return this.getChildContainer();
3963 initEvents : function()
3965 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3967 if (this.autohide) {
3972 Roo.get(document).on('scroll',function(e) {
3973 var ns = Roo.get(document).getScroll().top;
3974 var os = prevScroll;
3978 ft.removeClass('slideDown');
3979 ft.addClass('slideUp');
3982 ft.removeClass('slideUp');
3983 ft.addClass('slideDown');
4004 * @class Roo.bootstrap.NavSidebar
4005 * @extends Roo.bootstrap.Navbar
4006 * Bootstrap Sidebar class
4009 * Create a new Sidebar
4010 * @param {Object} config The config object
4014 Roo.bootstrap.NavSidebar = function(config){
4015 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4018 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4020 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4022 getAutoCreate : function(){
4027 cls: 'sidebar sidebar-nav'
4049 * @class Roo.bootstrap.NavGroup
4050 * @extends Roo.bootstrap.Component
4051 * Bootstrap NavGroup class
4052 * @cfg {String} align (left|right)
4053 * @cfg {Boolean} inverse
4054 * @cfg {String} type (nav|pills|tab) default nav
4055 * @cfg {String} navId - reference Id for navbar.
4059 * Create a new nav group
4060 * @param {Object} config The config object
4063 Roo.bootstrap.NavGroup = function(config){
4064 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4067 Roo.bootstrap.NavGroup.register(this);
4071 * Fires when the active item changes
4072 * @param {Roo.bootstrap.NavGroup} this
4073 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4074 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4081 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4092 getAutoCreate : function()
4094 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4101 if (['tabs','pills'].indexOf(this.type)!==-1) {
4102 cfg.cls += ' nav-' + this.type
4104 if (this.type!=='nav') {
4105 Roo.log('nav type must be nav/tabs/pills')
4107 cfg.cls += ' navbar-nav'
4110 if (this.parent() && this.parent().sidebar) {
4113 cls: 'dashboard-menu sidebar-menu'
4119 if (this.form === true) {
4125 if (this.align === 'right') {
4126 cfg.cls += ' navbar-right';
4128 cfg.cls += ' navbar-left';
4132 if (this.align === 'right') {
4133 cfg.cls += ' navbar-right';
4137 cfg.cls += ' navbar-inverse';
4145 * sets the active Navigation item
4146 * @param {Roo.bootstrap.NavItem} the new current navitem
4148 setActiveItem : function(item)
4151 Roo.each(this.navItems, function(v){
4156 v.setActive(false, true);
4163 item.setActive(true, true);
4164 this.fireEvent('changed', this, item, prev);
4169 * gets the active Navigation item
4170 * @return {Roo.bootstrap.NavItem} the current navitem
4172 getActive : function()
4176 Roo.each(this.navItems, function(v){
4187 indexOfNav : function()
4191 Roo.each(this.navItems, function(v,i){
4202 * adds a Navigation item
4203 * @param {Roo.bootstrap.NavItem} the navitem to add
4205 addItem : function(cfg)
4207 var cn = new Roo.bootstrap.NavItem(cfg);
4209 cn.parentId = this.id;
4210 cn.onRender(this.el, null);
4214 * register a Navigation item
4215 * @param {Roo.bootstrap.NavItem} the navitem to add
4217 register : function(item)
4219 this.navItems.push( item);
4220 item.navId = this.navId;
4225 * clear all the Navigation item
4228 clearAll : function()
4231 this.el.dom.innerHTML = '';
4234 getNavItem: function(tabId)
4237 Roo.each(this.navItems, function(e) {
4238 if (e.tabId == tabId) {
4248 setActiveNext : function()
4250 var i = this.indexOfNav(this.getActive());
4251 if (i > this.navItems.length) {
4254 this.setActiveItem(this.navItems[i+1]);
4256 setActivePrev : function()
4258 var i = this.indexOfNav(this.getActive());
4262 this.setActiveItem(this.navItems[i-1]);
4264 clearWasActive : function(except) {
4265 Roo.each(this.navItems, function(e) {
4266 if (e.tabId != except.tabId && e.was_active) {
4267 e.was_active = false;
4274 getWasActive : function ()
4277 Roo.each(this.navItems, function(e) {
4292 Roo.apply(Roo.bootstrap.NavGroup, {
4296 * register a Navigation Group
4297 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4299 register : function(navgrp)
4301 this.groups[navgrp.navId] = navgrp;
4305 * fetch a Navigation Group based on the navigation ID
4306 * @param {string} the navgroup to add
4307 * @returns {Roo.bootstrap.NavGroup} the navgroup
4309 get: function(navId) {
4310 if (typeof(this.groups[navId]) == 'undefined') {
4312 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4314 return this.groups[navId] ;
4329 * @class Roo.bootstrap.NavItem
4330 * @extends Roo.bootstrap.Component
4331 * Bootstrap Navbar.NavItem class
4332 * @cfg {String} href link to
4333 * @cfg {String} html content of button
4334 * @cfg {String} badge text inside badge
4335 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4336 * @cfg {String} glyphicon name of glyphicon
4337 * @cfg {String} icon name of font awesome icon
4338 * @cfg {Boolean} active Is item active
4339 * @cfg {Boolean} disabled Is item disabled
4341 * @cfg {Boolean} preventDefault (true | false) default false
4342 * @cfg {String} tabId the tab that this item activates.
4343 * @cfg {String} tagtype (a|span) render as a href or span?
4344 * @cfg {Boolean} animateRef (true|false) link to element default false
4347 * Create a new Navbar Item
4348 * @param {Object} config The config object
4350 Roo.bootstrap.NavItem = function(config){
4351 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4356 * The raw click event for the entire grid.
4357 * @param {Roo.EventObject} e
4362 * Fires when the active item active state changes
4363 * @param {Roo.bootstrap.NavItem} this
4364 * @param {boolean} state the new state
4370 * Fires when scroll to element
4371 * @param {Roo.bootstrap.NavItem} this
4372 * @param {Object} options
4373 * @param {Roo.EventObject} e
4381 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4389 preventDefault : false,
4396 getAutoCreate : function(){
4405 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4407 if (this.disabled) {
4408 cfg.cls += ' disabled';
4411 if (this.href || this.html || this.glyphicon || this.icon) {
4415 href : this.href || "#",
4416 html: this.html || ''
4421 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4424 if(this.glyphicon) {
4425 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4430 cfg.cn[0].html += " <span class='caret'></span>";
4434 if (this.badge !== '') {
4436 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4444 initEvents: function()
4446 if (typeof (this.menu) != 'undefined') {
4447 this.menu.parentType = this.xtype;
4448 this.menu.triggerEl = this.el;
4449 this.menu = this.addxtype(Roo.apply({}, this.menu));
4452 this.el.select('a',true).on('click', this.onClick, this);
4454 if(this.tagtype == 'span'){
4455 this.el.select('span',true).on('click', this.onClick, this);
4458 // at this point parent should be available..
4459 this.parent().register(this);
4462 onClick : function(e)
4464 if (e.getTarget('.dropdown-menu-item')) {
4465 // did you click on a menu itemm.... - then don't trigger onclick..
4470 this.preventDefault ||
4473 Roo.log("NavItem - prevent Default?");
4477 if (this.disabled) {
4481 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4482 if (tg && tg.transition) {
4483 Roo.log("waiting for the transitionend");
4489 //Roo.log("fire event clicked");
4490 if(this.fireEvent('click', this, e) === false){
4494 if(this.tagtype == 'span'){
4498 //Roo.log(this.href);
4499 var ael = this.el.select('a',true).first();
4502 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4503 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4504 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4505 return; // ignore... - it's a 'hash' to another page.
4507 Roo.log("NavItem - prevent Default?");
4509 this.scrollToElement(e);
4513 var p = this.parent();
4515 if (['tabs','pills'].indexOf(p.type)!==-1) {
4516 if (typeof(p.setActiveItem) !== 'undefined') {
4517 p.setActiveItem(this);
4521 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4522 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4523 // remove the collapsed menu expand...
4524 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4528 isActive: function () {
4531 setActive : function(state, fire, is_was_active)
4533 if (this.active && !state && this.navId) {
4534 this.was_active = true;
4535 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4537 nv.clearWasActive(this);
4541 this.active = state;
4544 this.el.removeClass('active');
4545 } else if (!this.el.hasClass('active')) {
4546 this.el.addClass('active');
4549 this.fireEvent('changed', this, state);
4552 // show a panel if it's registered and related..
4554 if (!this.navId || !this.tabId || !state || is_was_active) {
4558 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4562 var pan = tg.getPanelByName(this.tabId);
4566 // if we can not flip to new panel - go back to old nav highlight..
4567 if (false == tg.showPanel(pan)) {
4568 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4570 var onav = nv.getWasActive();
4572 onav.setActive(true, false, true);
4581 // this should not be here...
4582 setDisabled : function(state)
4584 this.disabled = state;
4586 this.el.removeClass('disabled');
4587 } else if (!this.el.hasClass('disabled')) {
4588 this.el.addClass('disabled');
4594 * Fetch the element to display the tooltip on.
4595 * @return {Roo.Element} defaults to this.el
4597 tooltipEl : function()
4599 return this.el.select('' + this.tagtype + '', true).first();
4602 scrollToElement : function(e)
4604 var c = document.body;
4607 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4609 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4610 c = document.documentElement;
4613 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4619 var o = target.calcOffsetsTo(c);
4626 this.fireEvent('scrollto', this, options, e);
4628 Roo.get(c).scrollTo('top', options.value, true);
4641 * <span> icon </span>
4642 * <span> text </span>
4643 * <span>badge </span>
4647 * @class Roo.bootstrap.NavSidebarItem
4648 * @extends Roo.bootstrap.NavItem
4649 * Bootstrap Navbar.NavSidebarItem class
4650 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4651 * {Boolean} open is the menu open
4652 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4653 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4654 * {String} buttonSize (sm|md|lg)the extra classes for the button
4655 * {Boolean} showArrow show arrow next to the text (default true)
4657 * Create a new Navbar Button
4658 * @param {Object} config The config object
4660 Roo.bootstrap.NavSidebarItem = function(config){
4661 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4666 * The raw click event for the entire grid.
4667 * @param {Roo.EventObject} e
4672 * Fires when the active item active state changes
4673 * @param {Roo.bootstrap.NavSidebarItem} this
4674 * @param {boolean} state the new state
4682 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4684 badgeWeight : 'default',
4690 buttonWeight : 'default',
4696 getAutoCreate : function(){
4701 href : this.href || '#',
4707 if(this.buttonView){
4710 href : this.href || '#',
4711 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4724 cfg.cls += ' active';
4727 if (this.disabled) {
4728 cfg.cls += ' disabled';
4731 cfg.cls += ' open x-open';
4734 if (this.glyphicon || this.icon) {
4735 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4736 a.cn.push({ tag : 'i', cls : c }) ;
4739 if(!this.buttonView){
4742 html : this.html || ''
4749 if (this.badge !== '') {
4750 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4756 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4759 a.cls += ' dropdown-toggle treeview' ;
4765 initEvents : function()
4767 if (typeof (this.menu) != 'undefined') {
4768 this.menu.parentType = this.xtype;
4769 this.menu.triggerEl = this.el;
4770 this.menu = this.addxtype(Roo.apply({}, this.menu));
4773 this.el.on('click', this.onClick, this);
4775 if(this.badge !== ''){
4776 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4781 onClick : function(e)
4788 if(this.preventDefault){
4792 this.fireEvent('click', this);
4795 disable : function()
4797 this.setDisabled(true);
4802 this.setDisabled(false);
4805 setDisabled : function(state)
4807 if(this.disabled == state){
4811 this.disabled = state;
4814 this.el.addClass('disabled');
4818 this.el.removeClass('disabled');
4823 setActive : function(state)
4825 if(this.active == state){
4829 this.active = state;
4832 this.el.addClass('active');
4836 this.el.removeClass('active');
4841 isActive: function ()
4846 setBadge : function(str)
4852 this.badgeEl.dom.innerHTML = str;
4869 * @class Roo.bootstrap.Row
4870 * @extends Roo.bootstrap.Component
4871 * Bootstrap Row class (contains columns...)
4875 * @param {Object} config The config object
4878 Roo.bootstrap.Row = function(config){
4879 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4882 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4884 getAutoCreate : function(){
4903 * @class Roo.bootstrap.Element
4904 * @extends Roo.bootstrap.Component
4905 * Bootstrap Element class
4906 * @cfg {String} html contents of the element
4907 * @cfg {String} tag tag of the element
4908 * @cfg {String} cls class of the element
4909 * @cfg {Boolean} preventDefault (true|false) default false
4910 * @cfg {Boolean} clickable (true|false) default false
4913 * Create a new Element
4914 * @param {Object} config The config object
4917 Roo.bootstrap.Element = function(config){
4918 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4924 * When a element is chick
4925 * @param {Roo.bootstrap.Element} this
4926 * @param {Roo.EventObject} e
4932 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4937 preventDefault: false,
4940 getAutoCreate : function(){
4944 // cls: this.cls, double assign in parent class Component.js :: onRender
4951 initEvents: function()
4953 Roo.bootstrap.Element.superclass.initEvents.call(this);
4956 this.el.on('click', this.onClick, this);
4961 onClick : function(e)
4963 if(this.preventDefault){
4967 this.fireEvent('click', this, e);
4970 getValue : function()
4972 return this.el.dom.innerHTML;
4975 setValue : function(value)
4977 this.el.dom.innerHTML = value;
4992 * @class Roo.bootstrap.Pagination
4993 * @extends Roo.bootstrap.Component
4994 * Bootstrap Pagination class
4995 * @cfg {String} size xs | sm | md | lg
4996 * @cfg {Boolean} inverse false | true
4999 * Create a new Pagination
5000 * @param {Object} config The config object
5003 Roo.bootstrap.Pagination = function(config){
5004 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5007 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5013 getAutoCreate : function(){
5019 cfg.cls += ' inverse';
5025 cfg.cls += " " + this.cls;
5043 * @class Roo.bootstrap.PaginationItem
5044 * @extends Roo.bootstrap.Component
5045 * Bootstrap PaginationItem class
5046 * @cfg {String} html text
5047 * @cfg {String} href the link
5048 * @cfg {Boolean} preventDefault (true | false) default true
5049 * @cfg {Boolean} active (true | false) default false
5050 * @cfg {Boolean} disabled default false
5054 * Create a new PaginationItem
5055 * @param {Object} config The config object
5059 Roo.bootstrap.PaginationItem = function(config){
5060 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5065 * The raw click event for the entire grid.
5066 * @param {Roo.EventObject} e
5072 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5076 preventDefault: true,
5081 getAutoCreate : function(){
5087 href : this.href ? this.href : '#',
5088 html : this.html ? this.html : ''
5098 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5102 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5108 initEvents: function() {
5110 this.el.on('click', this.onClick, this);
5113 onClick : function(e)
5115 Roo.log('PaginationItem on click ');
5116 if(this.preventDefault){
5124 this.fireEvent('click', this, e);
5140 * @class Roo.bootstrap.Slider
5141 * @extends Roo.bootstrap.Component
5142 * Bootstrap Slider class
5145 * Create a new Slider
5146 * @param {Object} config The config object
5149 Roo.bootstrap.Slider = function(config){
5150 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5153 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5155 getAutoCreate : function(){
5159 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5163 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5175 * Ext JS Library 1.1.1
5176 * Copyright(c) 2006-2007, Ext JS, LLC.
5178 * Originally Released Under LGPL - original licence link has changed is not relivant.
5181 * <script type="text/javascript">
5186 * @class Roo.grid.ColumnModel
5187 * @extends Roo.util.Observable
5188 * This is the default implementation of a ColumnModel used by the Grid. It defines
5189 * the columns in the grid.
5192 var colModel = new Roo.grid.ColumnModel([
5193 {header: "Ticker", width: 60, sortable: true, locked: true},
5194 {header: "Company Name", width: 150, sortable: true},
5195 {header: "Market Cap.", width: 100, sortable: true},
5196 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5197 {header: "Employees", width: 100, sortable: true, resizable: false}
5202 * The config options listed for this class are options which may appear in each
5203 * individual column definition.
5204 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5206 * @param {Object} config An Array of column config objects. See this class's
5207 * config objects for details.
5209 Roo.grid.ColumnModel = function(config){
5211 * The config passed into the constructor
5213 this.config = config;
5216 // if no id, create one
5217 // if the column does not have a dataIndex mapping,
5218 // map it to the order it is in the config
5219 for(var i = 0, len = config.length; i < len; i++){
5221 if(typeof c.dataIndex == "undefined"){
5224 if(typeof c.renderer == "string"){
5225 c.renderer = Roo.util.Format[c.renderer];
5227 if(typeof c.id == "undefined"){
5230 if(c.editor && c.editor.xtype){
5231 c.editor = Roo.factory(c.editor, Roo.grid);
5233 if(c.editor && c.editor.isFormField){
5234 c.editor = new Roo.grid.GridEditor(c.editor);
5236 this.lookup[c.id] = c;
5240 * The width of columns which have no width specified (defaults to 100)
5243 this.defaultWidth = 100;
5246 * Default sortable of columns which have no sortable specified (defaults to false)
5249 this.defaultSortable = false;
5253 * @event widthchange
5254 * Fires when the width of a column changes.
5255 * @param {ColumnModel} this
5256 * @param {Number} columnIndex The column index
5257 * @param {Number} newWidth The new width
5259 "widthchange": true,
5261 * @event headerchange
5262 * Fires when the text of a header changes.
5263 * @param {ColumnModel} this
5264 * @param {Number} columnIndex The column index
5265 * @param {Number} newText The new header text
5267 "headerchange": true,
5269 * @event hiddenchange
5270 * Fires when a column is hidden or "unhidden".
5271 * @param {ColumnModel} this
5272 * @param {Number} columnIndex The column index
5273 * @param {Boolean} hidden true if hidden, false otherwise
5275 "hiddenchange": true,
5277 * @event columnmoved
5278 * Fires when a column is moved.
5279 * @param {ColumnModel} this
5280 * @param {Number} oldIndex
5281 * @param {Number} newIndex
5283 "columnmoved" : true,
5285 * @event columlockchange
5286 * Fires when a column's locked state is changed
5287 * @param {ColumnModel} this
5288 * @param {Number} colIndex
5289 * @param {Boolean} locked true if locked
5291 "columnlockchange" : true
5293 Roo.grid.ColumnModel.superclass.constructor.call(this);
5295 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5297 * @cfg {String} header The header text to display in the Grid view.
5300 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5301 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5302 * specified, the column's index is used as an index into the Record's data Array.
5305 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5306 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5309 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5310 * Defaults to the value of the {@link #defaultSortable} property.
5311 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5314 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5317 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5320 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5323 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5326 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5327 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5328 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5329 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5332 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5335 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5338 * @cfg {String} cursor (Optional)
5341 * @cfg {String} tooltip (Optional)
5344 * @cfg {Number} xs (Optional)
5347 * @cfg {Number} sm (Optional)
5350 * @cfg {Number} md (Optional)
5353 * @cfg {Number} lg (Optional)
5356 * Returns the id of the column at the specified index.
5357 * @param {Number} index The column index
5358 * @return {String} the id
5360 getColumnId : function(index){
5361 return this.config[index].id;
5365 * Returns the column for a specified id.
5366 * @param {String} id The column id
5367 * @return {Object} the column
5369 getColumnById : function(id){
5370 return this.lookup[id];
5375 * Returns the column for a specified dataIndex.
5376 * @param {String} dataIndex The column dataIndex
5377 * @return {Object|Boolean} the column or false if not found
5379 getColumnByDataIndex: function(dataIndex){
5380 var index = this.findColumnIndex(dataIndex);
5381 return index > -1 ? this.config[index] : false;
5385 * Returns the index for a specified column id.
5386 * @param {String} id The column id
5387 * @return {Number} the index, or -1 if not found
5389 getIndexById : function(id){
5390 for(var i = 0, len = this.config.length; i < len; i++){
5391 if(this.config[i].id == id){
5399 * Returns the index for a specified column dataIndex.
5400 * @param {String} dataIndex The column dataIndex
5401 * @return {Number} the index, or -1 if not found
5404 findColumnIndex : function(dataIndex){
5405 for(var i = 0, len = this.config.length; i < len; i++){
5406 if(this.config[i].dataIndex == dataIndex){
5414 moveColumn : function(oldIndex, newIndex){
5415 var c = this.config[oldIndex];
5416 this.config.splice(oldIndex, 1);
5417 this.config.splice(newIndex, 0, c);
5418 this.dataMap = null;
5419 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5422 isLocked : function(colIndex){
5423 return this.config[colIndex].locked === true;
5426 setLocked : function(colIndex, value, suppressEvent){
5427 if(this.isLocked(colIndex) == value){
5430 this.config[colIndex].locked = value;
5432 this.fireEvent("columnlockchange", this, colIndex, value);
5436 getTotalLockedWidth : function(){
5438 for(var i = 0; i < this.config.length; i++){
5439 if(this.isLocked(i) && !this.isHidden(i)){
5440 this.totalWidth += this.getColumnWidth(i);
5446 getLockedCount : function(){
5447 for(var i = 0, len = this.config.length; i < len; i++){
5448 if(!this.isLocked(i)){
5453 return this.config.length;
5457 * Returns the number of columns.
5460 getColumnCount : function(visibleOnly){
5461 if(visibleOnly === true){
5463 for(var i = 0, len = this.config.length; i < len; i++){
5464 if(!this.isHidden(i)){
5470 return this.config.length;
5474 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5475 * @param {Function} fn
5476 * @param {Object} scope (optional)
5477 * @return {Array} result
5479 getColumnsBy : function(fn, scope){
5481 for(var i = 0, len = this.config.length; i < len; i++){
5482 var c = this.config[i];
5483 if(fn.call(scope||this, c, i) === true){
5491 * Returns true if the specified column is sortable.
5492 * @param {Number} col The column index
5495 isSortable : function(col){
5496 if(typeof this.config[col].sortable == "undefined"){
5497 return this.defaultSortable;
5499 return this.config[col].sortable;
5503 * Returns the rendering (formatting) function defined for the column.
5504 * @param {Number} col The column index.
5505 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5507 getRenderer : function(col){
5508 if(!this.config[col].renderer){
5509 return Roo.grid.ColumnModel.defaultRenderer;
5511 return this.config[col].renderer;
5515 * Sets the rendering (formatting) function for a column.
5516 * @param {Number} col The column index
5517 * @param {Function} fn The function to use to process the cell's raw data
5518 * to return HTML markup for the grid view. The render function is called with
5519 * the following parameters:<ul>
5520 * <li>Data value.</li>
5521 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5522 * <li>css A CSS style string to apply to the table cell.</li>
5523 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5524 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5525 * <li>Row index</li>
5526 * <li>Column index</li>
5527 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5529 setRenderer : function(col, fn){
5530 this.config[col].renderer = fn;
5534 * Returns the width for the specified column.
5535 * @param {Number} col The column index
5538 getColumnWidth : function(col){
5539 return this.config[col].width * 1 || this.defaultWidth;
5543 * Sets the width for a column.
5544 * @param {Number} col The column index
5545 * @param {Number} width The new width
5547 setColumnWidth : function(col, width, suppressEvent){
5548 this.config[col].width = width;
5549 this.totalWidth = null;
5551 this.fireEvent("widthchange", this, col, width);
5556 * Returns the total width of all columns.
5557 * @param {Boolean} includeHidden True to include hidden column widths
5560 getTotalWidth : function(includeHidden){
5561 if(!this.totalWidth){
5562 this.totalWidth = 0;
5563 for(var i = 0, len = this.config.length; i < len; i++){
5564 if(includeHidden || !this.isHidden(i)){
5565 this.totalWidth += this.getColumnWidth(i);
5569 return this.totalWidth;
5573 * Returns the header for the specified column.
5574 * @param {Number} col The column index
5577 getColumnHeader : function(col){
5578 return this.config[col].header;
5582 * Sets the header for a column.
5583 * @param {Number} col The column index
5584 * @param {String} header The new header
5586 setColumnHeader : function(col, header){
5587 this.config[col].header = header;
5588 this.fireEvent("headerchange", this, col, header);
5592 * Returns the tooltip for the specified column.
5593 * @param {Number} col The column index
5596 getColumnTooltip : function(col){
5597 return this.config[col].tooltip;
5600 * Sets the tooltip for a column.
5601 * @param {Number} col The column index
5602 * @param {String} tooltip The new tooltip
5604 setColumnTooltip : function(col, tooltip){
5605 this.config[col].tooltip = tooltip;
5609 * Returns the dataIndex for the specified column.
5610 * @param {Number} col The column index
5613 getDataIndex : function(col){
5614 return this.config[col].dataIndex;
5618 * Sets the dataIndex for a column.
5619 * @param {Number} col The column index
5620 * @param {Number} dataIndex The new dataIndex
5622 setDataIndex : function(col, dataIndex){
5623 this.config[col].dataIndex = dataIndex;
5629 * Returns true if the cell is editable.
5630 * @param {Number} colIndex The column index
5631 * @param {Number} rowIndex The row index - this is nto actually used..?
5634 isCellEditable : function(colIndex, rowIndex){
5635 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5639 * Returns the editor defined for the cell/column.
5640 * return false or null to disable editing.
5641 * @param {Number} colIndex The column index
5642 * @param {Number} rowIndex The row index
5645 getCellEditor : function(colIndex, rowIndex){
5646 return this.config[colIndex].editor;
5650 * Sets if a column is editable.
5651 * @param {Number} col The column index
5652 * @param {Boolean} editable True if the column is editable
5654 setEditable : function(col, editable){
5655 this.config[col].editable = editable;
5660 * Returns true if the column is hidden.
5661 * @param {Number} colIndex The column index
5664 isHidden : function(colIndex){
5665 return this.config[colIndex].hidden;
5670 * Returns true if the column width cannot be changed
5672 isFixed : function(colIndex){
5673 return this.config[colIndex].fixed;
5677 * Returns true if the column can be resized
5680 isResizable : function(colIndex){
5681 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5684 * Sets if a column is hidden.
5685 * @param {Number} colIndex The column index
5686 * @param {Boolean} hidden True if the column is hidden
5688 setHidden : function(colIndex, hidden){
5689 this.config[colIndex].hidden = hidden;
5690 this.totalWidth = null;
5691 this.fireEvent("hiddenchange", this, colIndex, hidden);
5695 * Sets the editor for a column.
5696 * @param {Number} col The column index
5697 * @param {Object} editor The editor object
5699 setEditor : function(col, editor){
5700 this.config[col].editor = editor;
5704 Roo.grid.ColumnModel.defaultRenderer = function(value)
5706 if(typeof value == "object") {
5709 if(typeof value == "string" && value.length < 1){
5713 return String.format("{0}", value);
5716 // Alias for backwards compatibility
5717 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5720 * Ext JS Library 1.1.1
5721 * Copyright(c) 2006-2007, Ext JS, LLC.
5723 * Originally Released Under LGPL - original licence link has changed is not relivant.
5726 * <script type="text/javascript">
5730 * @class Roo.LoadMask
5731 * A simple utility class for generically masking elements while loading data. If the element being masked has
5732 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5733 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5734 * element's UpdateManager load indicator and will be destroyed after the initial load.
5736 * Create a new LoadMask
5737 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5738 * @param {Object} config The config object
5740 Roo.LoadMask = function(el, config){
5741 this.el = Roo.get(el);
5742 Roo.apply(this, config);
5744 this.store.on('beforeload', this.onBeforeLoad, this);
5745 this.store.on('load', this.onLoad, this);
5746 this.store.on('loadexception', this.onLoadException, this);
5747 this.removeMask = false;
5749 var um = this.el.getUpdateManager();
5750 um.showLoadIndicator = false; // disable the default indicator
5751 um.on('beforeupdate', this.onBeforeLoad, this);
5752 um.on('update', this.onLoad, this);
5753 um.on('failure', this.onLoad, this);
5754 this.removeMask = true;
5758 Roo.LoadMask.prototype = {
5760 * @cfg {Boolean} removeMask
5761 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5762 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5766 * The text to display in a centered loading message box (defaults to 'Loading...')
5770 * @cfg {String} msgCls
5771 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5773 msgCls : 'x-mask-loading',
5776 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5782 * Disables the mask to prevent it from being displayed
5784 disable : function(){
5785 this.disabled = true;
5789 * Enables the mask so that it can be displayed
5791 enable : function(){
5792 this.disabled = false;
5795 onLoadException : function()
5799 if (typeof(arguments[3]) != 'undefined') {
5800 Roo.MessageBox.alert("Error loading",arguments[3]);
5804 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5805 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5812 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5817 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5821 onBeforeLoad : function(){
5823 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5828 destroy : function(){
5830 this.store.un('beforeload', this.onBeforeLoad, this);
5831 this.store.un('load', this.onLoad, this);
5832 this.store.un('loadexception', this.onLoadException, this);
5834 var um = this.el.getUpdateManager();
5835 um.un('beforeupdate', this.onBeforeLoad, this);
5836 um.un('update', this.onLoad, this);
5837 um.un('failure', this.onLoad, this);
5848 * @class Roo.bootstrap.Table
5849 * @extends Roo.bootstrap.Component
5850 * Bootstrap Table class
5851 * @cfg {String} cls table class
5852 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5853 * @cfg {String} bgcolor Specifies the background color for a table
5854 * @cfg {Number} border Specifies whether the table cells should have borders or not
5855 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5856 * @cfg {Number} cellspacing Specifies the space between cells
5857 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5858 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5859 * @cfg {String} sortable Specifies that the table should be sortable
5860 * @cfg {String} summary Specifies a summary of the content of a table
5861 * @cfg {Number} width Specifies the width of a table
5862 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5864 * @cfg {boolean} striped Should the rows be alternative striped
5865 * @cfg {boolean} bordered Add borders to the table
5866 * @cfg {boolean} hover Add hover highlighting
5867 * @cfg {boolean} condensed Format condensed
5868 * @cfg {boolean} responsive Format condensed
5869 * @cfg {Boolean} loadMask (true|false) default false
5870 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5871 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5872 * @cfg {Boolean} rowSelection (true|false) default false
5873 * @cfg {Boolean} cellSelection (true|false) default false
5874 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5875 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5876 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5880 * Create a new Table
5881 * @param {Object} config The config object
5884 Roo.bootstrap.Table = function(config){
5885 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5890 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5891 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5892 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5893 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5895 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5897 this.sm.grid = this;
5898 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5899 this.sm = this.selModel;
5900 this.sm.xmodule = this.xmodule || false;
5903 if (this.cm && typeof(this.cm.config) == 'undefined') {
5904 this.colModel = new Roo.grid.ColumnModel(this.cm);
5905 this.cm = this.colModel;
5906 this.cm.xmodule = this.xmodule || false;
5909 this.store= Roo.factory(this.store, Roo.data);
5910 this.ds = this.store;
5911 this.ds.xmodule = this.xmodule || false;
5914 if (this.footer && this.store) {
5915 this.footer.dataSource = this.ds;
5916 this.footer = Roo.factory(this.footer);
5923 * Fires when a cell is clicked
5924 * @param {Roo.bootstrap.Table} this
5925 * @param {Roo.Element} el
5926 * @param {Number} rowIndex
5927 * @param {Number} columnIndex
5928 * @param {Roo.EventObject} e
5932 * @event celldblclick
5933 * Fires when a cell is double clicked
5934 * @param {Roo.bootstrap.Table} this
5935 * @param {Roo.Element} el
5936 * @param {Number} rowIndex
5937 * @param {Number} columnIndex
5938 * @param {Roo.EventObject} e
5940 "celldblclick" : true,
5943 * Fires when a row is clicked
5944 * @param {Roo.bootstrap.Table} this
5945 * @param {Roo.Element} el
5946 * @param {Number} rowIndex
5947 * @param {Roo.EventObject} e
5951 * @event rowdblclick
5952 * Fires when a row is double clicked
5953 * @param {Roo.bootstrap.Table} this
5954 * @param {Roo.Element} el
5955 * @param {Number} rowIndex
5956 * @param {Roo.EventObject} e
5958 "rowdblclick" : true,
5961 * Fires when a mouseover occur
5962 * @param {Roo.bootstrap.Table} this
5963 * @param {Roo.Element} el
5964 * @param {Number} rowIndex
5965 * @param {Number} columnIndex
5966 * @param {Roo.EventObject} e
5971 * Fires when a mouseout occur
5972 * @param {Roo.bootstrap.Table} this
5973 * @param {Roo.Element} el
5974 * @param {Number} rowIndex
5975 * @param {Number} columnIndex
5976 * @param {Roo.EventObject} e
5981 * Fires when a row is rendered, so you can change add a style to it.
5982 * @param {Roo.bootstrap.Table} this
5983 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5987 * @event rowsrendered
5988 * Fires when all the rows have been rendered
5989 * @param {Roo.bootstrap.Table} this
5991 'rowsrendered' : true,
5993 * @event contextmenu
5994 * The raw contextmenu event for the entire grid.
5995 * @param {Roo.EventObject} e
5997 "contextmenu" : true,
5999 * @event rowcontextmenu
6000 * Fires when a row is right clicked
6001 * @param {Roo.bootstrap.Table} this
6002 * @param {Number} rowIndex
6003 * @param {Roo.EventObject} e
6005 "rowcontextmenu" : true,
6007 * @event cellcontextmenu
6008 * Fires when a cell is right clicked
6009 * @param {Roo.bootstrap.Table} this
6010 * @param {Number} rowIndex
6011 * @param {Number} cellIndex
6012 * @param {Roo.EventObject} e
6014 "cellcontextmenu" : true,
6016 * @event headercontextmenu
6017 * Fires when a header is right clicked
6018 * @param {Roo.bootstrap.Table} this
6019 * @param {Number} columnIndex
6020 * @param {Roo.EventObject} e
6022 "headercontextmenu" : true
6026 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6052 rowSelection : false,
6053 cellSelection : false,
6056 // Roo.Element - the tbody
6058 // Roo.Element - thead element
6061 container: false, // used by gridpanel...
6067 getAutoCreate : function()
6069 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6076 if (this.scrollBody) {
6077 cfg.cls += ' table-body-fixed';
6080 cfg.cls += ' table-striped';
6084 cfg.cls += ' table-hover';
6086 if (this.bordered) {
6087 cfg.cls += ' table-bordered';
6089 if (this.condensed) {
6090 cfg.cls += ' table-condensed';
6092 if (this.responsive) {
6093 cfg.cls += ' table-responsive';
6097 cfg.cls+= ' ' +this.cls;
6100 // this lot should be simplifed...
6103 cfg.align=this.align;
6106 cfg.bgcolor=this.bgcolor;
6109 cfg.border=this.border;
6111 if (this.cellpadding) {
6112 cfg.cellpadding=this.cellpadding;
6114 if (this.cellspacing) {
6115 cfg.cellspacing=this.cellspacing;
6118 cfg.frame=this.frame;
6121 cfg.rules=this.rules;
6123 if (this.sortable) {
6124 cfg.sortable=this.sortable;
6127 cfg.summary=this.summary;
6130 cfg.width=this.width;
6133 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6136 if(this.store || this.cm){
6137 if(this.headerShow){
6138 cfg.cn.push(this.renderHeader());
6141 cfg.cn.push(this.renderBody());
6143 if(this.footerShow){
6144 cfg.cn.push(this.renderFooter());
6146 // where does this come from?
6147 //cfg.cls+= ' TableGrid';
6150 return { cn : [ cfg ] };
6153 initEvents : function()
6155 if(!this.store || !this.cm){
6158 if (this.selModel) {
6159 this.selModel.initEvents();
6163 //Roo.log('initEvents with ds!!!!');
6165 this.mainBody = this.el.select('tbody', true).first();
6166 this.mainHead = this.el.select('thead', true).first();
6173 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6174 e.on('click', _this.sort, _this);
6177 this.mainBody.on("click", this.onClick, this);
6178 this.mainBody.on("dblclick", this.onDblClick, this);
6180 // why is this done????? = it breaks dialogs??
6181 //this.parent().el.setStyle('position', 'relative');
6185 this.footer.parentId = this.id;
6186 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6189 this.el.select('tfoot tr td').first().addClass('hide');
6193 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6195 this.store.on('load', this.onLoad, this);
6196 this.store.on('beforeload', this.onBeforeLoad, this);
6197 this.store.on('update', this.onUpdate, this);
6198 this.store.on('add', this.onAdd, this);
6199 this.store.on("clear", this.clear, this);
6201 this.el.on("contextmenu", this.onContextMenu, this);
6203 this.mainBody.on('scroll', this.onBodyScroll, this);
6205 this.cm.on("headerchange", this.onHeaderChange, this);
6207 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6211 onContextMenu : function(e, t)
6213 this.processEvent("contextmenu", e);
6216 processEvent : function(name, e)
6218 if (name != 'touchstart' ) {
6219 this.fireEvent(name, e);
6222 var t = e.getTarget();
6224 var cell = Roo.get(t);
6230 if(cell.findParent('tfoot', false, true)){
6234 if(cell.findParent('thead', false, true)){
6236 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6237 cell = Roo.get(t).findParent('th', false, true);
6239 Roo.log("failed to find th in thead?");
6240 Roo.log(e.getTarget());
6245 var cellIndex = cell.dom.cellIndex;
6247 var ename = name == 'touchstart' ? 'click' : name;
6248 this.fireEvent("header" + ename, this, cellIndex, e);
6253 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6254 cell = Roo.get(t).findParent('td', false, true);
6256 Roo.log("failed to find th in tbody?");
6257 Roo.log(e.getTarget());
6262 var row = cell.findParent('tr', false, true);
6263 var cellIndex = cell.dom.cellIndex;
6264 var rowIndex = row.dom.rowIndex - 1;
6268 this.fireEvent("row" + name, this, rowIndex, e);
6272 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6278 onMouseover : function(e, el)
6280 var cell = Roo.get(el);
6286 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6287 cell = cell.findParent('td', false, true);
6290 var row = cell.findParent('tr', false, true);
6291 var cellIndex = cell.dom.cellIndex;
6292 var rowIndex = row.dom.rowIndex - 1; // start from 0
6294 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6298 onMouseout : function(e, el)
6300 var cell = Roo.get(el);
6306 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6307 cell = cell.findParent('td', false, true);
6310 var row = cell.findParent('tr', false, true);
6311 var cellIndex = cell.dom.cellIndex;
6312 var rowIndex = row.dom.rowIndex - 1; // start from 0
6314 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6318 onClick : function(e, el)
6320 var cell = Roo.get(el);
6322 if(!cell || (!this.cellSelection && !this.rowSelection)){
6326 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6327 cell = cell.findParent('td', false, true);
6330 if(!cell || typeof(cell) == 'undefined'){
6334 var row = cell.findParent('tr', false, true);
6336 if(!row || typeof(row) == 'undefined'){
6340 var cellIndex = cell.dom.cellIndex;
6341 var rowIndex = this.getRowIndex(row);
6343 // why??? - should these not be based on SelectionModel?
6344 if(this.cellSelection){
6345 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6348 if(this.rowSelection){
6349 this.fireEvent('rowclick', this, row, rowIndex, e);
6355 onDblClick : function(e,el)
6357 var cell = Roo.get(el);
6359 if(!cell || (!this.cellSelection && !this.rowSelection)){
6363 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6364 cell = cell.findParent('td', false, true);
6367 if(!cell || typeof(cell) == 'undefined'){
6371 var row = cell.findParent('tr', false, true);
6373 if(!row || typeof(row) == 'undefined'){
6377 var cellIndex = cell.dom.cellIndex;
6378 var rowIndex = this.getRowIndex(row);
6380 if(this.cellSelection){
6381 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6384 if(this.rowSelection){
6385 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6389 sort : function(e,el)
6391 var col = Roo.get(el);
6393 if(!col.hasClass('sortable')){
6397 var sort = col.attr('sort');
6400 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6404 this.store.sortInfo = {field : sort, direction : dir};
6407 Roo.log("calling footer first");
6408 this.footer.onClick('first');
6411 this.store.load({ params : { start : 0 } });
6415 renderHeader : function()
6423 this.totalWidth = 0;
6425 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6427 var config = cm.config[i];
6431 cls : 'x-hcol-' + i,
6433 html: cm.getColumnHeader(i)
6438 if(typeof(config.sortable) != 'undefined' && config.sortable){
6440 c.html = '<i class="glyphicon"></i>' + c.html;
6443 if(typeof(config.lgHeader) != 'undefined'){
6444 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6447 if(typeof(config.mdHeader) != 'undefined'){
6448 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6451 if(typeof(config.smHeader) != 'undefined'){
6452 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6455 if(typeof(config.xsHeader) != 'undefined'){
6456 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6463 if(typeof(config.tooltip) != 'undefined'){
6464 c.tooltip = config.tooltip;
6467 if(typeof(config.colspan) != 'undefined'){
6468 c.colspan = config.colspan;
6471 if(typeof(config.hidden) != 'undefined' && config.hidden){
6472 c.style += ' display:none;';
6475 if(typeof(config.dataIndex) != 'undefined'){
6476 c.sort = config.dataIndex;
6481 if(typeof(config.align) != 'undefined' && config.align.length){
6482 c.style += ' text-align:' + config.align + ';';
6485 if(typeof(config.width) != 'undefined'){
6486 c.style += ' width:' + config.width + 'px;';
6487 this.totalWidth += config.width;
6489 this.totalWidth += 100; // assume minimum of 100 per column?
6492 if(typeof(config.cls) != 'undefined'){
6493 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6496 ['xs','sm','md','lg'].map(function(size){
6498 if(typeof(config[size]) == 'undefined'){
6502 if (!config[size]) { // 0 = hidden
6503 c.cls += ' hidden-' + size;
6507 c.cls += ' col-' + size + '-' + config[size];
6517 renderBody : function()
6527 colspan : this.cm.getColumnCount()
6537 renderFooter : function()
6547 colspan : this.cm.getColumnCount()
6561 // Roo.log('ds onload');
6566 var ds = this.store;
6568 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6569 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6570 if (_this.store.sortInfo) {
6572 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6573 e.select('i', true).addClass(['glyphicon-arrow-up']);
6576 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6577 e.select('i', true).addClass(['glyphicon-arrow-down']);
6582 var tbody = this.mainBody;
6584 if(ds.getCount() > 0){
6585 ds.data.each(function(d,rowIndex){
6586 var row = this.renderRow(cm, ds, rowIndex);
6588 tbody.createChild(row);
6592 if(row.cellObjects.length){
6593 Roo.each(row.cellObjects, function(r){
6594 _this.renderCellObject(r);
6601 Roo.each(this.el.select('tbody td', true).elements, function(e){
6602 e.on('mouseover', _this.onMouseover, _this);
6605 Roo.each(this.el.select('tbody td', true).elements, function(e){
6606 e.on('mouseout', _this.onMouseout, _this);
6608 this.fireEvent('rowsrendered', this);
6609 //if(this.loadMask){
6610 // this.maskEl.hide();
6617 onUpdate : function(ds,record)
6619 this.refreshRow(record);
6623 onRemove : function(ds, record, index, isUpdate){
6624 if(isUpdate !== true){
6625 this.fireEvent("beforerowremoved", this, index, record);
6627 var bt = this.mainBody.dom;
6629 var rows = this.el.select('tbody > tr', true).elements;
6631 if(typeof(rows[index]) != 'undefined'){
6632 bt.removeChild(rows[index].dom);
6635 // if(bt.rows[index]){
6636 // bt.removeChild(bt.rows[index]);
6639 if(isUpdate !== true){
6640 //this.stripeRows(index);
6641 //this.syncRowHeights(index, index);
6643 this.fireEvent("rowremoved", this, index, record);
6647 onAdd : function(ds, records, rowIndex)
6649 //Roo.log('on Add called');
6650 // - note this does not handle multiple adding very well..
6651 var bt = this.mainBody.dom;
6652 for (var i =0 ; i < records.length;i++) {
6653 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6654 //Roo.log(records[i]);
6655 //Roo.log(this.store.getAt(rowIndex+i));
6656 this.insertRow(this.store, rowIndex + i, false);
6663 refreshRow : function(record){
6664 var ds = this.store, index;
6665 if(typeof record == 'number'){
6667 record = ds.getAt(index);
6669 index = ds.indexOf(record);
6671 this.insertRow(ds, index, true);
6673 this.onRemove(ds, record, index+1, true);
6675 //this.syncRowHeights(index, index);
6677 this.fireEvent("rowupdated", this, index, record);
6680 insertRow : function(dm, rowIndex, isUpdate){
6683 this.fireEvent("beforerowsinserted", this, rowIndex);
6685 //var s = this.getScrollState();
6686 var row = this.renderRow(this.cm, this.store, rowIndex);
6687 // insert before rowIndex..
6688 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6692 if(row.cellObjects.length){
6693 Roo.each(row.cellObjects, function(r){
6694 _this.renderCellObject(r);
6699 this.fireEvent("rowsinserted", this, rowIndex);
6700 //this.syncRowHeights(firstRow, lastRow);
6701 //this.stripeRows(firstRow);
6708 getRowDom : function(rowIndex)
6710 var rows = this.el.select('tbody > tr', true).elements;
6712 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6715 // returns the object tree for a tr..
6718 renderRow : function(cm, ds, rowIndex)
6720 var d = ds.getAt(rowIndex);
6724 cls : 'x-row-' + rowIndex,
6728 var cellObjects = [];
6730 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6731 var config = cm.config[i];
6733 var renderer = cm.getRenderer(i);
6737 if(typeof(renderer) !== 'undefined'){
6738 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6740 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6741 // and are rendered into the cells after the row is rendered - using the id for the element.
6743 if(typeof(value) === 'object'){
6753 rowIndex : rowIndex,
6758 this.fireEvent('rowclass', this, rowcfg);
6762 cls : rowcfg.rowClass + ' x-col-' + i,
6764 html: (typeof(value) === 'object') ? '' : value
6771 if(typeof(config.colspan) != 'undefined'){
6772 td.colspan = config.colspan;
6775 if(typeof(config.hidden) != 'undefined' && config.hidden){
6776 td.style += ' display:none;';
6779 if(typeof(config.align) != 'undefined' && config.align.length){
6780 td.style += ' text-align:' + config.align + ';';
6783 if(typeof(config.width) != 'undefined'){
6784 td.style += ' width:' + config.width + 'px;';
6787 if(typeof(config.cursor) != 'undefined'){
6788 td.style += ' cursor:' + config.cursor + ';';
6791 if(typeof(config.cls) != 'undefined'){
6792 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6795 ['xs','sm','md','lg'].map(function(size){
6797 if(typeof(config[size]) == 'undefined'){
6801 if (!config[size]) { // 0 = hidden
6802 td.cls += ' hidden-' + size;
6806 td.cls += ' col-' + size + '-' + config[size];
6814 row.cellObjects = cellObjects;
6822 onBeforeLoad : function()
6824 //Roo.log('ds onBeforeLoad');
6828 //if(this.loadMask){
6829 // this.maskEl.show();
6837 this.el.select('tbody', true).first().dom.innerHTML = '';
6840 * Show or hide a row.
6841 * @param {Number} rowIndex to show or hide
6842 * @param {Boolean} state hide
6844 setRowVisibility : function(rowIndex, state)
6846 var bt = this.mainBody.dom;
6848 var rows = this.el.select('tbody > tr', true).elements;
6850 if(typeof(rows[rowIndex]) == 'undefined'){
6853 rows[rowIndex].dom.style.display = state ? '' : 'none';
6857 getSelectionModel : function(){
6859 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6861 return this.selModel;
6864 * Render the Roo.bootstrap object from renderder
6866 renderCellObject : function(r)
6870 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6872 var t = r.cfg.render(r.container);
6875 Roo.each(r.cfg.cn, function(c){
6877 container: t.getChildContainer(),
6880 _this.renderCellObject(child);
6885 getRowIndex : function(row)
6889 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6900 * Returns the grid's underlying element = used by panel.Grid
6901 * @return {Element} The element
6903 getGridEl : function(){
6907 * Forces a resize - used by panel.Grid
6908 * @return {Element} The element
6910 autoSize : function()
6912 //var ctr = Roo.get(this.container.dom.parentElement);
6913 var ctr = Roo.get(this.el.dom);
6915 var thd = this.getGridEl().select('thead',true).first();
6916 var tbd = this.getGridEl().select('tbody', true).first();
6917 var tfd = this.getGridEl().select('tfoot', true).first();
6919 var cw = ctr.getWidth();
6923 tbd.setSize(ctr.getWidth(),
6924 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6926 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6929 cw = Math.max(cw, this.totalWidth);
6930 this.getGridEl().select('tr',true).setWidth(cw);
6931 // resize 'expandable coloumn?
6933 return; // we doe not have a view in this design..
6936 onBodyScroll: function()
6938 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6940 this.mainHead.setStyle({
6941 'position' : 'relative',
6942 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6948 var scrollHeight = this.mainBody.dom.scrollHeight;
6950 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6952 var height = this.mainBody.getHeight();
6954 if(scrollHeight - height == scrollTop) {
6956 var total = this.ds.getTotalCount();
6958 if(this.footer.cursor + this.footer.pageSize < total){
6960 this.footer.ds.load({
6962 start : this.footer.cursor + this.footer.pageSize,
6963 limit : this.footer.pageSize
6973 onHeaderChange : function()
6975 var header = this.renderHeader();
6976 var table = this.el.select('table', true).first();
6978 this.mainHead.remove();
6979 this.mainHead = table.createChild(header, this.mainBody, false);
6982 onHiddenChange : function(colModel, colIndex, hidden)
6984 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
6985 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
6987 this.CSS.updateRule(thSelector, "display", "");
6988 this.CSS.updateRule(tdSelector, "display", "");
6991 this.CSS.updateRule(thSelector, "display", "none");
6992 this.CSS.updateRule(tdSelector, "display", "none");
6995 this.onHeaderChange();
7012 * @class Roo.bootstrap.TableCell
7013 * @extends Roo.bootstrap.Component
7014 * Bootstrap TableCell class
7015 * @cfg {String} html cell contain text
7016 * @cfg {String} cls cell class
7017 * @cfg {String} tag cell tag (td|th) default td
7018 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7019 * @cfg {String} align Aligns the content in a cell
7020 * @cfg {String} axis Categorizes cells
7021 * @cfg {String} bgcolor Specifies the background color of a cell
7022 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7023 * @cfg {Number} colspan Specifies the number of columns a cell should span
7024 * @cfg {String} headers Specifies one or more header cells a cell is related to
7025 * @cfg {Number} height Sets the height of a cell
7026 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7027 * @cfg {Number} rowspan Sets the number of rows a cell should span
7028 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7029 * @cfg {String} valign Vertical aligns the content in a cell
7030 * @cfg {Number} width Specifies the width of a cell
7033 * Create a new TableCell
7034 * @param {Object} config The config object
7037 Roo.bootstrap.TableCell = function(config){
7038 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7041 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7061 getAutoCreate : function(){
7062 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7082 cfg.align=this.align
7088 cfg.bgcolor=this.bgcolor
7091 cfg.charoff=this.charoff
7094 cfg.colspan=this.colspan
7097 cfg.headers=this.headers
7100 cfg.height=this.height
7103 cfg.nowrap=this.nowrap
7106 cfg.rowspan=this.rowspan
7109 cfg.scope=this.scope
7112 cfg.valign=this.valign
7115 cfg.width=this.width
7134 * @class Roo.bootstrap.TableRow
7135 * @extends Roo.bootstrap.Component
7136 * Bootstrap TableRow class
7137 * @cfg {String} cls row class
7138 * @cfg {String} align Aligns the content in a table row
7139 * @cfg {String} bgcolor Specifies a background color for a table row
7140 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7141 * @cfg {String} valign Vertical aligns the content in a table row
7144 * Create a new TableRow
7145 * @param {Object} config The config object
7148 Roo.bootstrap.TableRow = function(config){
7149 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7152 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7160 getAutoCreate : function(){
7161 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7171 cfg.align = this.align;
7174 cfg.bgcolor = this.bgcolor;
7177 cfg.charoff = this.charoff;
7180 cfg.valign = this.valign;
7198 * @class Roo.bootstrap.TableBody
7199 * @extends Roo.bootstrap.Component
7200 * Bootstrap TableBody class
7201 * @cfg {String} cls element class
7202 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7203 * @cfg {String} align Aligns the content inside the element
7204 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7205 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7208 * Create a new TableBody
7209 * @param {Object} config The config object
7212 Roo.bootstrap.TableBody = function(config){
7213 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7216 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7224 getAutoCreate : function(){
7225 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7239 cfg.align = this.align;
7242 cfg.charoff = this.charoff;
7245 cfg.valign = this.valign;
7252 // initEvents : function()
7259 // this.store = Roo.factory(this.store, Roo.data);
7260 // this.store.on('load', this.onLoad, this);
7262 // this.store.load();
7266 // onLoad: function ()
7268 // this.fireEvent('load', this);
7278 * Ext JS Library 1.1.1
7279 * Copyright(c) 2006-2007, Ext JS, LLC.
7281 * Originally Released Under LGPL - original licence link has changed is not relivant.
7284 * <script type="text/javascript">
7287 // as we use this in bootstrap.
7288 Roo.namespace('Roo.form');
7290 * @class Roo.form.Action
7291 * Internal Class used to handle form actions
7293 * @param {Roo.form.BasicForm} el The form element or its id
7294 * @param {Object} config Configuration options
7299 // define the action interface
7300 Roo.form.Action = function(form, options){
7302 this.options = options || {};
7305 * Client Validation Failed
7308 Roo.form.Action.CLIENT_INVALID = 'client';
7310 * Server Validation Failed
7313 Roo.form.Action.SERVER_INVALID = 'server';
7315 * Connect to Server Failed
7318 Roo.form.Action.CONNECT_FAILURE = 'connect';
7320 * Reading Data from Server Failed
7323 Roo.form.Action.LOAD_FAILURE = 'load';
7325 Roo.form.Action.prototype = {
7327 failureType : undefined,
7328 response : undefined,
7332 run : function(options){
7337 success : function(response){
7342 handleResponse : function(response){
7346 // default connection failure
7347 failure : function(response){
7349 this.response = response;
7350 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7351 this.form.afterAction(this, false);
7354 processResponse : function(response){
7355 this.response = response;
7356 if(!response.responseText){
7359 this.result = this.handleResponse(response);
7363 // utility functions used internally
7364 getUrl : function(appendParams){
7365 var url = this.options.url || this.form.url || this.form.el.dom.action;
7367 var p = this.getParams();
7369 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7375 getMethod : function(){
7376 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7379 getParams : function(){
7380 var bp = this.form.baseParams;
7381 var p = this.options.params;
7383 if(typeof p == "object"){
7384 p = Roo.urlEncode(Roo.applyIf(p, bp));
7385 }else if(typeof p == 'string' && bp){
7386 p += '&' + Roo.urlEncode(bp);
7389 p = Roo.urlEncode(bp);
7394 createCallback : function(){
7396 success: this.success,
7397 failure: this.failure,
7399 timeout: (this.form.timeout*1000),
7400 upload: this.form.fileUpload ? this.success : undefined
7405 Roo.form.Action.Submit = function(form, options){
7406 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7409 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7412 haveProgress : false,
7413 uploadComplete : false,
7415 // uploadProgress indicator.
7416 uploadProgress : function()
7418 if (!this.form.progressUrl) {
7422 if (!this.haveProgress) {
7423 Roo.MessageBox.progress("Uploading", "Uploading");
7425 if (this.uploadComplete) {
7426 Roo.MessageBox.hide();
7430 this.haveProgress = true;
7432 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7434 var c = new Roo.data.Connection();
7436 url : this.form.progressUrl,
7441 success : function(req){
7442 //console.log(data);
7446 rdata = Roo.decode(req.responseText)
7448 Roo.log("Invalid data from server..");
7452 if (!rdata || !rdata.success) {
7454 Roo.MessageBox.alert(Roo.encode(rdata));
7457 var data = rdata.data;
7459 if (this.uploadComplete) {
7460 Roo.MessageBox.hide();
7465 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7466 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7469 this.uploadProgress.defer(2000,this);
7472 failure: function(data) {
7473 Roo.log('progress url failed ');
7484 // run get Values on the form, so it syncs any secondary forms.
7485 this.form.getValues();
7487 var o = this.options;
7488 var method = this.getMethod();
7489 var isPost = method == 'POST';
7490 if(o.clientValidation === false || this.form.isValid()){
7492 if (this.form.progressUrl) {
7493 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7494 (new Date() * 1) + '' + Math.random());
7499 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7500 form:this.form.el.dom,
7501 url:this.getUrl(!isPost),
7503 params:isPost ? this.getParams() : null,
7504 isUpload: this.form.fileUpload
7507 this.uploadProgress();
7509 }else if (o.clientValidation !== false){ // client validation failed
7510 this.failureType = Roo.form.Action.CLIENT_INVALID;
7511 this.form.afterAction(this, false);
7515 success : function(response)
7517 this.uploadComplete= true;
7518 if (this.haveProgress) {
7519 Roo.MessageBox.hide();
7523 var result = this.processResponse(response);
7524 if(result === true || result.success){
7525 this.form.afterAction(this, true);
7529 this.form.markInvalid(result.errors);
7530 this.failureType = Roo.form.Action.SERVER_INVALID;
7532 this.form.afterAction(this, false);
7534 failure : function(response)
7536 this.uploadComplete= true;
7537 if (this.haveProgress) {
7538 Roo.MessageBox.hide();
7541 this.response = response;
7542 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7543 this.form.afterAction(this, false);
7546 handleResponse : function(response){
7547 if(this.form.errorReader){
7548 var rs = this.form.errorReader.read(response);
7551 for(var i = 0, len = rs.records.length; i < len; i++) {
7552 var r = rs.records[i];
7556 if(errors.length < 1){
7560 success : rs.success,
7566 ret = Roo.decode(response.responseText);
7570 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7580 Roo.form.Action.Load = function(form, options){
7581 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7582 this.reader = this.form.reader;
7585 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7590 Roo.Ajax.request(Roo.apply(
7591 this.createCallback(), {
7592 method:this.getMethod(),
7593 url:this.getUrl(false),
7594 params:this.getParams()
7598 success : function(response){
7600 var result = this.processResponse(response);
7601 if(result === true || !result.success || !result.data){
7602 this.failureType = Roo.form.Action.LOAD_FAILURE;
7603 this.form.afterAction(this, false);
7606 this.form.clearInvalid();
7607 this.form.setValues(result.data);
7608 this.form.afterAction(this, true);
7611 handleResponse : function(response){
7612 if(this.form.reader){
7613 var rs = this.form.reader.read(response);
7614 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7616 success : rs.success,
7620 return Roo.decode(response.responseText);
7624 Roo.form.Action.ACTION_TYPES = {
7625 'load' : Roo.form.Action.Load,
7626 'submit' : Roo.form.Action.Submit
7635 * @class Roo.bootstrap.Form
7636 * @extends Roo.bootstrap.Component
7637 * Bootstrap Form class
7638 * @cfg {String} method GET | POST (default POST)
7639 * @cfg {String} labelAlign top | left (default top)
7640 * @cfg {String} align left | right - for navbars
7641 * @cfg {Boolean} loadMask load mask when submit (default true)
7646 * @param {Object} config The config object
7650 Roo.bootstrap.Form = function(config){
7652 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7654 Roo.bootstrap.Form.popover.apply();
7658 * @event clientvalidation
7659 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7660 * @param {Form} this
7661 * @param {Boolean} valid true if the form has passed client-side validation
7663 clientvalidation: true,
7665 * @event beforeaction
7666 * Fires before any action is performed. Return false to cancel the action.
7667 * @param {Form} this
7668 * @param {Action} action The action to be performed
7672 * @event actionfailed
7673 * Fires when an action fails.
7674 * @param {Form} this
7675 * @param {Action} action The action that failed
7677 actionfailed : true,
7679 * @event actioncomplete
7680 * Fires when an action is completed.
7681 * @param {Form} this
7682 * @param {Action} action The action that completed
7684 actioncomplete : true
7688 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7691 * @cfg {String} method
7692 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7697 * The URL to use for form actions if one isn't supplied in the action options.
7700 * @cfg {Boolean} fileUpload
7701 * Set to true if this form is a file upload.
7705 * @cfg {Object} baseParams
7706 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7710 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7714 * @cfg {Sting} align (left|right) for navbar forms
7719 activeAction : null,
7722 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7723 * element by passing it or its id or mask the form itself by passing in true.
7726 waitMsgTarget : false,
7731 * @cfg {Boolean} errorMask (true|false) default false
7736 * @cfg {Number} maskOffset Default 100
7741 * @cfg {Boolean} maskBody
7745 getAutoCreate : function(){
7749 method : this.method || 'POST',
7750 id : this.id || Roo.id(),
7753 if (this.parent().xtype.match(/^Nav/)) {
7754 cfg.cls = 'navbar-form navbar-' + this.align;
7758 if (this.labelAlign == 'left' ) {
7759 cfg.cls += ' form-horizontal';
7765 initEvents : function()
7767 this.el.on('submit', this.onSubmit, this);
7768 // this was added as random key presses on the form where triggering form submit.
7769 this.el.on('keypress', function(e) {
7770 if (e.getCharCode() != 13) {
7773 // we might need to allow it for textareas.. and some other items.
7774 // check e.getTarget().
7776 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7780 Roo.log("keypress blocked");
7788 onSubmit : function(e){
7793 * Returns true if client-side validation on the form is successful.
7796 isValid : function(){
7797 var items = this.getItems();
7801 items.each(function(f){
7808 if(!target && f.el.isVisible(true)){
7814 if(this.errorMask && !valid){
7815 Roo.bootstrap.Form.popover.mask(this, target);
7822 * Returns true if any fields in this form have changed since their original load.
7825 isDirty : function(){
7827 var items = this.getItems();
7828 items.each(function(f){
7838 * Performs a predefined action (submit or load) or custom actions you define on this form.
7839 * @param {String} actionName The name of the action type
7840 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7841 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7842 * accept other config options):
7844 Property Type Description
7845 ---------------- --------------- ----------------------------------------------------------------------------------
7846 url String The url for the action (defaults to the form's url)
7847 method String The form method to use (defaults to the form's method, or POST if not defined)
7848 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7849 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7850 validate the form on the client (defaults to false)
7852 * @return {BasicForm} this
7854 doAction : function(action, options){
7855 if(typeof action == 'string'){
7856 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7858 if(this.fireEvent('beforeaction', this, action) !== false){
7859 this.beforeAction(action);
7860 action.run.defer(100, action);
7866 beforeAction : function(action){
7867 var o = action.options;
7872 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7874 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7877 // not really supported yet.. ??
7879 //if(this.waitMsgTarget === true){
7880 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7881 //}else if(this.waitMsgTarget){
7882 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7883 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7885 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7891 afterAction : function(action, success){
7892 this.activeAction = null;
7893 var o = action.options;
7898 Roo.get(document.body).unmask();
7904 //if(this.waitMsgTarget === true){
7905 // this.el.unmask();
7906 //}else if(this.waitMsgTarget){
7907 // this.waitMsgTarget.unmask();
7909 // Roo.MessageBox.updateProgress(1);
7910 // Roo.MessageBox.hide();
7917 Roo.callback(o.success, o.scope, [this, action]);
7918 this.fireEvent('actioncomplete', this, action);
7922 // failure condition..
7923 // we have a scenario where updates need confirming.
7924 // eg. if a locking scenario exists..
7925 // we look for { errors : { needs_confirm : true }} in the response.
7927 (typeof(action.result) != 'undefined') &&
7928 (typeof(action.result.errors) != 'undefined') &&
7929 (typeof(action.result.errors.needs_confirm) != 'undefined')
7932 Roo.log("not supported yet");
7935 Roo.MessageBox.confirm(
7936 "Change requires confirmation",
7937 action.result.errorMsg,
7942 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7952 Roo.callback(o.failure, o.scope, [this, action]);
7953 // show an error message if no failed handler is set..
7954 if (!this.hasListener('actionfailed')) {
7955 Roo.log("need to add dialog support");
7957 Roo.MessageBox.alert("Error",
7958 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7959 action.result.errorMsg :
7960 "Saving Failed, please check your entries or try again"
7965 this.fireEvent('actionfailed', this, action);
7970 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7971 * @param {String} id The value to search for
7974 findField : function(id){
7975 var items = this.getItems();
7976 var field = items.get(id);
7978 items.each(function(f){
7979 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7986 return field || null;
7989 * Mark fields in this form invalid in bulk.
7990 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7991 * @return {BasicForm} this
7993 markInvalid : function(errors){
7994 if(errors instanceof Array){
7995 for(var i = 0, len = errors.length; i < len; i++){
7996 var fieldError = errors[i];
7997 var f = this.findField(fieldError.id);
7999 f.markInvalid(fieldError.msg);
8005 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8006 field.markInvalid(errors[id]);
8010 //Roo.each(this.childForms || [], function (f) {
8011 // f.markInvalid(errors);
8018 * Set values for fields in this form in bulk.
8019 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8020 * @return {BasicForm} this
8022 setValues : function(values){
8023 if(values instanceof Array){ // array of objects
8024 for(var i = 0, len = values.length; i < len; i++){
8026 var f = this.findField(v.id);
8028 f.setValue(v.value);
8029 if(this.trackResetOnLoad){
8030 f.originalValue = f.getValue();
8034 }else{ // object hash
8037 if(typeof values[id] != 'function' && (field = this.findField(id))){
8039 if (field.setFromData &&
8041 field.displayField &&
8042 // combos' with local stores can
8043 // be queried via setValue()
8044 // to set their value..
8045 (field.store && !field.store.isLocal)
8049 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8050 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8051 field.setFromData(sd);
8053 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8055 field.setFromData(values);
8058 field.setValue(values[id]);
8062 if(this.trackResetOnLoad){
8063 field.originalValue = field.getValue();
8069 //Roo.each(this.childForms || [], function (f) {
8070 // f.setValues(values);
8077 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8078 * they are returned as an array.
8079 * @param {Boolean} asString
8082 getValues : function(asString){
8083 //if (this.childForms) {
8084 // copy values from the child forms
8085 // Roo.each(this.childForms, function (f) {
8086 // this.setValues(f.getValues());
8092 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8093 if(asString === true){
8096 return Roo.urlDecode(fs);
8100 * Returns the fields in this form as an object with key/value pairs.
8101 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8104 getFieldValues : function(with_hidden)
8106 var items = this.getItems();
8108 items.each(function(f){
8114 var v = f.getValue();
8116 if (f.inputType =='radio') {
8117 if (typeof(ret[f.getName()]) == 'undefined') {
8118 ret[f.getName()] = ''; // empty..
8121 if (!f.el.dom.checked) {
8129 if(f.xtype == 'MoneyField'){
8130 ret[f.currencyName] = f.getCurrency();
8133 // not sure if this supported any more..
8134 if ((typeof(v) == 'object') && f.getRawValue) {
8135 v = f.getRawValue() ; // dates..
8137 // combo boxes where name != hiddenName...
8138 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8139 ret[f.name] = f.getRawValue();
8141 ret[f.getName()] = v;
8148 * Clears all invalid messages in this form.
8149 * @return {BasicForm} this
8151 clearInvalid : function(){
8152 var items = this.getItems();
8154 items.each(function(f){
8163 * @return {BasicForm} this
8166 var items = this.getItems();
8167 items.each(function(f){
8171 Roo.each(this.childForms || [], function (f) {
8179 getItems : function()
8181 var r=new Roo.util.MixedCollection(false, function(o){
8182 return o.id || (o.id = Roo.id());
8184 var iter = function(el) {
8191 Roo.each(el.items,function(e) {
8200 hideFields : function(items)
8202 Roo.each(items, function(i){
8204 var f = this.findField(i);
8210 if(f.xtype == 'DateField'){
8211 f.setVisible(false);
8220 showFields : function(items)
8222 Roo.each(items, function(i){
8224 var f = this.findField(i);
8230 if(f.xtype == 'DateField'){
8242 Roo.apply(Roo.bootstrap.Form, {
8269 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8270 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8271 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8272 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8275 this.maskEl.top.enableDisplayMode("block");
8276 this.maskEl.left.enableDisplayMode("block");
8277 this.maskEl.bottom.enableDisplayMode("block");
8278 this.maskEl.right.enableDisplayMode("block");
8280 this.toolTip = new Roo.bootstrap.Tooltip({
8281 cls : 'roo-form-error-popover',
8283 'left' : ['r-l', [-2,0], 'right'],
8284 'right' : ['l-r', [2,0], 'left'],
8285 'bottom' : ['tl-bl', [0,2], 'top'],
8286 'top' : [ 'bl-tl', [0,-2], 'bottom']
8290 this.toolTip.render(Roo.get(document.body));
8292 this.toolTip.el.enableDisplayMode("block");
8294 Roo.get(document.body).on('click', function(){
8298 Roo.get(document.body).on('touchstart', function(){
8302 this.isApplied = true
8305 mask : function(form, target)
8309 this.target = target;
8311 if(!this.form.errorMask || !target.el){
8315 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8317 Roo.log(scrollable);
8319 var ot = this.target.el.calcOffsetsTo(scrollable);
8321 var scrollTo = ot[1] - this.form.maskOffset;
8323 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8325 scrollable.scrollTo('top', scrollTo);
8327 var box = this.target.el.getBox();
8329 var zIndex = Roo.bootstrap.Modal.zIndex++;
8332 this.maskEl.top.setStyle('position', 'absolute');
8333 this.maskEl.top.setStyle('z-index', zIndex);
8334 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8335 this.maskEl.top.setLeft(0);
8336 this.maskEl.top.setTop(0);
8337 this.maskEl.top.show();
8339 this.maskEl.left.setStyle('position', 'absolute');
8340 this.maskEl.left.setStyle('z-index', zIndex);
8341 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8342 this.maskEl.left.setLeft(0);
8343 this.maskEl.left.setTop(box.y - this.padding);
8344 this.maskEl.left.show();
8346 this.maskEl.bottom.setStyle('position', 'absolute');
8347 this.maskEl.bottom.setStyle('z-index', zIndex);
8348 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8349 this.maskEl.bottom.setLeft(0);
8350 this.maskEl.bottom.setTop(box.bottom + this.padding);
8351 this.maskEl.bottom.show();
8353 this.maskEl.right.setStyle('position', 'absolute');
8354 this.maskEl.right.setStyle('z-index', zIndex);
8355 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8356 this.maskEl.right.setLeft(box.right + this.padding);
8357 this.maskEl.right.setTop(box.y - this.padding);
8358 this.maskEl.right.show();
8360 this.toolTip.bindEl = this.target.el;
8362 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8364 var tip = this.target.blankText;
8366 if(this.target.getValue() !== '' ) {
8368 if (this.target.invalidText.length) {
8369 tip = this.target.invalidText;
8370 } else if (this.target.regexText.length){
8371 tip = this.target.regexText;
8375 this.toolTip.show(tip);
8377 this.intervalID = window.setInterval(function() {
8378 Roo.bootstrap.Form.popover.unmask();
8381 window.onwheel = function(){ return false;};
8383 (function(){ this.isMasked = true; }).defer(500, this);
8389 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8393 this.maskEl.top.setStyle('position', 'absolute');
8394 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8395 this.maskEl.top.hide();
8397 this.maskEl.left.setStyle('position', 'absolute');
8398 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8399 this.maskEl.left.hide();
8401 this.maskEl.bottom.setStyle('position', 'absolute');
8402 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8403 this.maskEl.bottom.hide();
8405 this.maskEl.right.setStyle('position', 'absolute');
8406 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8407 this.maskEl.right.hide();
8409 this.toolTip.hide();
8411 this.toolTip.el.hide();
8413 window.onwheel = function(){ return true;};
8415 if(this.intervalID){
8416 window.clearInterval(this.intervalID);
8417 this.intervalID = false;
8420 this.isMasked = false;
8430 * Ext JS Library 1.1.1
8431 * Copyright(c) 2006-2007, Ext JS, LLC.
8433 * Originally Released Under LGPL - original licence link has changed is not relivant.
8436 * <script type="text/javascript">
8439 * @class Roo.form.VTypes
8440 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8443 Roo.form.VTypes = function(){
8444 // closure these in so they are only created once.
8445 var alpha = /^[a-zA-Z_]+$/;
8446 var alphanum = /^[a-zA-Z0-9_]+$/;
8447 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8448 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8450 // All these messages and functions are configurable
8453 * The function used to validate email addresses
8454 * @param {String} value The email address
8456 'email' : function(v){
8457 return email.test(v);
8460 * The error text to display when the email validation function returns false
8463 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8465 * The keystroke filter mask to be applied on email input
8468 'emailMask' : /[a-z0-9_\.\-@]/i,
8471 * The function used to validate URLs
8472 * @param {String} value The URL
8474 'url' : function(v){
8478 * The error text to display when the url validation function returns false
8481 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8484 * The function used to validate alpha values
8485 * @param {String} value The value
8487 'alpha' : function(v){
8488 return alpha.test(v);
8491 * The error text to display when the alpha validation function returns false
8494 'alphaText' : 'This field should only contain letters and _',
8496 * The keystroke filter mask to be applied on alpha input
8499 'alphaMask' : /[a-z_]/i,
8502 * The function used to validate alphanumeric values
8503 * @param {String} value The value
8505 'alphanum' : function(v){
8506 return alphanum.test(v);
8509 * The error text to display when the alphanumeric validation function returns false
8512 'alphanumText' : 'This field should only contain letters, numbers and _',
8514 * The keystroke filter mask to be applied on alphanumeric input
8517 'alphanumMask' : /[a-z0-9_]/i
8527 * @class Roo.bootstrap.Input
8528 * @extends Roo.bootstrap.Component
8529 * Bootstrap Input class
8530 * @cfg {Boolean} disabled is it disabled
8531 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8532 * @cfg {String} name name of the input
8533 * @cfg {string} fieldLabel - the label associated
8534 * @cfg {string} placeholder - placeholder to put in text.
8535 * @cfg {string} before - input group add on before
8536 * @cfg {string} after - input group add on after
8537 * @cfg {string} size - (lg|sm) or leave empty..
8538 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8539 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8540 * @cfg {Number} md colspan out of 12 for computer-sized screens
8541 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8542 * @cfg {string} value default value of the input
8543 * @cfg {Number} labelWidth set the width of label
8544 * @cfg {Number} labellg set the width of label (1-12)
8545 * @cfg {Number} labelmd set the width of label (1-12)
8546 * @cfg {Number} labelsm set the width of label (1-12)
8547 * @cfg {Number} labelxs set the width of label (1-12)
8548 * @cfg {String} labelAlign (top|left)
8549 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8550 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8551 * @cfg {String} indicatorpos (left|right) default left
8552 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8554 * @cfg {String} align (left|center|right) Default left
8555 * @cfg {Boolean} forceFeedback (true|false) Default false
8558 * Create a new Input
8559 * @param {Object} config The config object
8562 Roo.bootstrap.Input = function(config){
8564 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8569 * Fires when this field receives input focus.
8570 * @param {Roo.form.Field} this
8575 * Fires when this field loses input focus.
8576 * @param {Roo.form.Field} this
8581 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8582 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8583 * @param {Roo.form.Field} this
8584 * @param {Roo.EventObject} e The event object
8589 * Fires just before the field blurs if the field value has changed.
8590 * @param {Roo.form.Field} this
8591 * @param {Mixed} newValue The new value
8592 * @param {Mixed} oldValue The original value
8597 * Fires after the field has been marked as invalid.
8598 * @param {Roo.form.Field} this
8599 * @param {String} msg The validation message
8604 * Fires after the field has been validated with no errors.
8605 * @param {Roo.form.Field} this
8610 * Fires after the key up
8611 * @param {Roo.form.Field} this
8612 * @param {Roo.EventObject} e The event Object
8618 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8620 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8621 automatic validation (defaults to "keyup").
8623 validationEvent : "keyup",
8625 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8627 validateOnBlur : true,
8629 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8631 validationDelay : 250,
8633 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8635 focusClass : "x-form-focus", // not needed???
8639 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8641 invalidClass : "has-warning",
8644 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8646 validClass : "has-success",
8649 * @cfg {Boolean} hasFeedback (true|false) default true
8654 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8656 invalidFeedbackClass : "glyphicon-warning-sign",
8659 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8661 validFeedbackClass : "glyphicon-ok",
8664 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8666 selectOnFocus : false,
8669 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8673 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8678 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8680 disableKeyFilter : false,
8683 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8687 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8691 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8693 blankText : "Please complete this mandatory field",
8696 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8700 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8702 maxLength : Number.MAX_VALUE,
8704 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8706 minLengthText : "The minimum length for this field is {0}",
8708 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8710 maxLengthText : "The maximum length for this field is {0}",
8714 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8715 * If available, this function will be called only after the basic validators all return true, and will be passed the
8716 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8720 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8721 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8722 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8726 * @cfg {String} regexText -- Depricated - use Invalid Text
8731 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8737 autocomplete: false,
8756 formatedValue : false,
8757 forceFeedback : false,
8759 indicatorpos : 'left',
8768 parentLabelAlign : function()
8771 while (parent.parent()) {
8772 parent = parent.parent();
8773 if (typeof(parent.labelAlign) !='undefined') {
8774 return parent.labelAlign;
8781 getAutoCreate : function()
8783 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8789 if(this.inputType != 'hidden'){
8790 cfg.cls = 'form-group' //input-group
8796 type : this.inputType,
8798 cls : 'form-control',
8799 placeholder : this.placeholder || '',
8800 autocomplete : this.autocomplete || 'new-password'
8803 if(this.capture.length){
8804 input.capture = this.capture;
8808 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8811 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8812 input.maxLength = this.maxLength;
8815 if (this.disabled) {
8816 input.disabled=true;
8819 if (this.readOnly) {
8820 input.readonly=true;
8824 input.name = this.name;
8828 input.cls += ' input-' + this.size;
8832 ['xs','sm','md','lg'].map(function(size){
8833 if (settings[size]) {
8834 cfg.cls += ' col-' + size + '-' + settings[size];
8838 var inputblock = input;
8842 cls: 'glyphicon form-control-feedback'
8845 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8848 cls : 'has-feedback',
8856 if (this.before || this.after) {
8859 cls : 'input-group',
8863 if (this.before && typeof(this.before) == 'string') {
8865 inputblock.cn.push({
8867 cls : 'roo-input-before input-group-addon',
8871 if (this.before && typeof(this.before) == 'object') {
8872 this.before = Roo.factory(this.before);
8874 inputblock.cn.push({
8876 cls : 'roo-input-before input-group-' +
8877 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8881 inputblock.cn.push(input);
8883 if (this.after && typeof(this.after) == 'string') {
8884 inputblock.cn.push({
8886 cls : 'roo-input-after input-group-addon',
8890 if (this.after && typeof(this.after) == 'object') {
8891 this.after = Roo.factory(this.after);
8893 inputblock.cn.push({
8895 cls : 'roo-input-after input-group-' +
8896 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8900 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8901 inputblock.cls += ' has-feedback';
8902 inputblock.cn.push(feedback);
8906 if (align ==='left' && this.fieldLabel.length) {
8908 cfg.cls += ' roo-form-group-label-left';
8913 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8914 tooltip : 'This field is required'
8919 cls : 'control-label',
8920 html : this.fieldLabel
8931 var labelCfg = cfg.cn[1];
8932 var contentCfg = cfg.cn[2];
8934 if(this.indicatorpos == 'right'){
8939 cls : 'control-label',
8943 html : this.fieldLabel
8947 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8948 tooltip : 'This field is required'
8961 labelCfg = cfg.cn[0];
8962 contentCfg = cfg.cn[1];
8966 if(this.labelWidth > 12){
8967 labelCfg.style = "width: " + this.labelWidth + 'px';
8970 if(this.labelWidth < 13 && this.labelmd == 0){
8971 this.labelmd = this.labelWidth;
8974 if(this.labellg > 0){
8975 labelCfg.cls += ' col-lg-' + this.labellg;
8976 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8979 if(this.labelmd > 0){
8980 labelCfg.cls += ' col-md-' + this.labelmd;
8981 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8984 if(this.labelsm > 0){
8985 labelCfg.cls += ' col-sm-' + this.labelsm;
8986 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8989 if(this.labelxs > 0){
8990 labelCfg.cls += ' col-xs-' + this.labelxs;
8991 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8995 } else if ( this.fieldLabel.length) {
9000 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9001 tooltip : 'This field is required'
9005 //cls : 'input-group-addon',
9006 html : this.fieldLabel
9014 if(this.indicatorpos == 'right'){
9019 //cls : 'input-group-addon',
9020 html : this.fieldLabel
9025 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9026 tooltip : 'This field is required'
9046 if (this.parentType === 'Navbar' && this.parent().bar) {
9047 cfg.cls += ' navbar-form';
9050 if (this.parentType === 'NavGroup') {
9051 cfg.cls += ' navbar-form';
9059 * return the real input element.
9061 inputEl: function ()
9063 return this.el.select('input.form-control',true).first();
9066 tooltipEl : function()
9068 return this.inputEl();
9071 indicatorEl : function()
9073 var indicator = this.el.select('i.roo-required-indicator',true).first();
9083 setDisabled : function(v)
9085 var i = this.inputEl().dom;
9087 i.removeAttribute('disabled');
9091 i.setAttribute('disabled','true');
9093 initEvents : function()
9096 this.inputEl().on("keydown" , this.fireKey, this);
9097 this.inputEl().on("focus", this.onFocus, this);
9098 this.inputEl().on("blur", this.onBlur, this);
9100 this.inputEl().relayEvent('keyup', this);
9102 this.indicator = this.indicatorEl();
9105 this.indicator.addClass('invisible');
9108 // reference to original value for reset
9109 this.originalValue = this.getValue();
9110 //Roo.form.TextField.superclass.initEvents.call(this);
9111 if(this.validationEvent == 'keyup'){
9112 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9113 this.inputEl().on('keyup', this.filterValidation, this);
9115 else if(this.validationEvent !== false){
9116 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9119 if(this.selectOnFocus){
9120 this.on("focus", this.preFocus, this);
9123 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9124 this.inputEl().on("keypress", this.filterKeys, this);
9126 this.inputEl().relayEvent('keypress', this);
9129 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9130 this.el.on("click", this.autoSize, this);
9133 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9134 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9137 if (typeof(this.before) == 'object') {
9138 this.before.render(this.el.select('.roo-input-before',true).first());
9140 if (typeof(this.after) == 'object') {
9141 this.after.render(this.el.select('.roo-input-after',true).first());
9144 this.inputEl().on('change', this.onChange, this);
9147 filterValidation : function(e){
9148 if(!e.isNavKeyPress()){
9149 this.validationTask.delay(this.validationDelay);
9153 * Validates the field value
9154 * @return {Boolean} True if the value is valid, else false
9156 validate : function(){
9157 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9158 if(this.disabled || this.validateValue(this.getRawValue())){
9169 * Validates a value according to the field's validation rules and marks the field as invalid
9170 * if the validation fails
9171 * @param {Mixed} value The value to validate
9172 * @return {Boolean} True if the value is valid, else false
9174 validateValue : function(value)
9176 if(this.getVisibilityEl().hasClass('hidden')){
9180 if(value.length < 1) { // if it's blank
9181 if(this.allowBlank){
9187 if(value.length < this.minLength){
9190 if(value.length > this.maxLength){
9194 var vt = Roo.form.VTypes;
9195 if(!vt[this.vtype](value, this)){
9199 if(typeof this.validator == "function"){
9200 var msg = this.validator(value);
9204 if (typeof(msg) == 'string') {
9205 this.invalidText = msg;
9209 if(this.regex && !this.regex.test(value)){
9217 fireKey : function(e){
9218 //Roo.log('field ' + e.getKey());
9219 if(e.isNavKeyPress()){
9220 this.fireEvent("specialkey", this, e);
9223 focus : function (selectText){
9225 this.inputEl().focus();
9226 if(selectText === true){
9227 this.inputEl().dom.select();
9233 onFocus : function(){
9234 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9235 // this.el.addClass(this.focusClass);
9238 this.hasFocus = true;
9239 this.startValue = this.getValue();
9240 this.fireEvent("focus", this);
9244 beforeBlur : Roo.emptyFn,
9248 onBlur : function(){
9250 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9251 //this.el.removeClass(this.focusClass);
9253 this.hasFocus = false;
9254 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9257 var v = this.getValue();
9258 if(String(v) !== String(this.startValue)){
9259 this.fireEvent('change', this, v, this.startValue);
9261 this.fireEvent("blur", this);
9264 onChange : function(e)
9266 var v = this.getValue();
9267 if(String(v) !== String(this.startValue)){
9268 this.fireEvent('change', this, v, this.startValue);
9274 * Resets the current field value to the originally loaded value and clears any validation messages
9277 this.setValue(this.originalValue);
9281 * Returns the name of the field
9282 * @return {Mixed} name The name field
9284 getName: function(){
9288 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9289 * @return {Mixed} value The field value
9291 getValue : function(){
9293 var v = this.inputEl().getValue();
9298 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9299 * @return {Mixed} value The field value
9301 getRawValue : function(){
9302 var v = this.inputEl().getValue();
9308 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9309 * @param {Mixed} value The value to set
9311 setRawValue : function(v){
9312 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9315 selectText : function(start, end){
9316 var v = this.getRawValue();
9318 start = start === undefined ? 0 : start;
9319 end = end === undefined ? v.length : end;
9320 var d = this.inputEl().dom;
9321 if(d.setSelectionRange){
9322 d.setSelectionRange(start, end);
9323 }else if(d.createTextRange){
9324 var range = d.createTextRange();
9325 range.moveStart("character", start);
9326 range.moveEnd("character", v.length-end);
9333 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9334 * @param {Mixed} value The value to set
9336 setValue : function(v){
9339 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9345 processValue : function(value){
9346 if(this.stripCharsRe){
9347 var newValue = value.replace(this.stripCharsRe, '');
9348 if(newValue !== value){
9349 this.setRawValue(newValue);
9356 preFocus : function(){
9358 if(this.selectOnFocus){
9359 this.inputEl().dom.select();
9362 filterKeys : function(e){
9364 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9367 var c = e.getCharCode(), cc = String.fromCharCode(c);
9368 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9371 if(!this.maskRe.test(cc)){
9376 * Clear any invalid styles/messages for this field
9378 clearInvalid : function(){
9380 if(!this.el || this.preventMark){ // not rendered
9385 this.el.removeClass(this.invalidClass);
9387 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9389 var feedback = this.el.select('.form-control-feedback', true).first();
9392 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9397 this.fireEvent('valid', this);
9401 * Mark this field as valid
9403 markValid : function()
9405 if(!this.el || this.preventMark){ // not rendered...
9409 this.el.removeClass([this.invalidClass, this.validClass]);
9411 var feedback = this.el.select('.form-control-feedback', true).first();
9414 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9418 this.indicator.removeClass('visible');
9419 this.indicator.addClass('invisible');
9426 if(this.allowBlank && !this.getRawValue().length){
9430 this.el.addClass(this.validClass);
9432 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9434 var feedback = this.el.select('.form-control-feedback', true).first();
9437 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9438 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9443 this.fireEvent('valid', this);
9447 * Mark this field as invalid
9448 * @param {String} msg The validation message
9450 markInvalid : function(msg)
9452 if(!this.el || this.preventMark){ // not rendered
9456 this.el.removeClass([this.invalidClass, this.validClass]);
9458 var feedback = this.el.select('.form-control-feedback', true).first();
9461 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9468 if(this.allowBlank && !this.getRawValue().length){
9473 this.indicator.removeClass('invisible');
9474 this.indicator.addClass('visible');
9477 this.el.addClass(this.invalidClass);
9479 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9481 var feedback = this.el.select('.form-control-feedback', true).first();
9484 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9486 if(this.getValue().length || this.forceFeedback){
9487 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9494 this.fireEvent('invalid', this, msg);
9497 SafariOnKeyDown : function(event)
9499 // this is a workaround for a password hang bug on chrome/ webkit.
9500 if (this.inputEl().dom.type != 'password') {
9504 var isSelectAll = false;
9506 if(this.inputEl().dom.selectionEnd > 0){
9507 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9509 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9510 event.preventDefault();
9515 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9517 event.preventDefault();
9518 // this is very hacky as keydown always get's upper case.
9520 var cc = String.fromCharCode(event.getCharCode());
9521 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9525 adjustWidth : function(tag, w){
9526 tag = tag.toLowerCase();
9527 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9528 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9532 if(tag == 'textarea'){
9535 }else if(Roo.isOpera){
9539 if(tag == 'textarea'){
9547 setFieldLabel : function(v)
9554 var ar = this.el.select('label > span',true);
9556 if (ar.elements.length) {
9557 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9558 this.fieldLabel = v;
9562 var br = this.el.select('label',true);
9564 if(br.elements.length) {
9565 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9566 this.fieldLabel = v;
9570 Roo.log('Cannot Found any of label > span || label in input');
9574 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9575 this.fieldLabel = v;
9590 * @class Roo.bootstrap.TextArea
9591 * @extends Roo.bootstrap.Input
9592 * Bootstrap TextArea class
9593 * @cfg {Number} cols Specifies the visible width of a text area
9594 * @cfg {Number} rows Specifies the visible number of lines in a text area
9595 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9596 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9597 * @cfg {string} html text
9600 * Create a new TextArea
9601 * @param {Object} config The config object
9604 Roo.bootstrap.TextArea = function(config){
9605 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9609 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9619 getAutoCreate : function(){
9621 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9627 if(this.inputType != 'hidden'){
9628 cfg.cls = 'form-group' //input-group
9636 value : this.value || '',
9637 html: this.html || '',
9638 cls : 'form-control',
9639 placeholder : this.placeholder || ''
9643 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9644 input.maxLength = this.maxLength;
9648 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9652 input.cols = this.cols;
9655 if (this.readOnly) {
9656 input.readonly = true;
9660 input.name = this.name;
9664 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9668 ['xs','sm','md','lg'].map(function(size){
9669 if (settings[size]) {
9670 cfg.cls += ' col-' + size + '-' + settings[size];
9674 var inputblock = input;
9676 if(this.hasFeedback && !this.allowBlank){
9680 cls: 'glyphicon form-control-feedback'
9684 cls : 'has-feedback',
9693 if (this.before || this.after) {
9696 cls : 'input-group',
9700 inputblock.cn.push({
9702 cls : 'input-group-addon',
9707 inputblock.cn.push(input);
9709 if(this.hasFeedback && !this.allowBlank){
9710 inputblock.cls += ' has-feedback';
9711 inputblock.cn.push(feedback);
9715 inputblock.cn.push({
9717 cls : 'input-group-addon',
9724 if (align ==='left' && this.fieldLabel.length) {
9729 cls : 'control-label',
9730 html : this.fieldLabel
9741 if(this.labelWidth > 12){
9742 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9745 if(this.labelWidth < 13 && this.labelmd == 0){
9746 this.labelmd = this.labelWidth;
9749 if(this.labellg > 0){
9750 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9751 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9754 if(this.labelmd > 0){
9755 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9756 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9759 if(this.labelsm > 0){
9760 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9761 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9764 if(this.labelxs > 0){
9765 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9766 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9769 } else if ( this.fieldLabel.length) {
9774 //cls : 'input-group-addon',
9775 html : this.fieldLabel
9793 if (this.disabled) {
9794 input.disabled=true;
9801 * return the real textarea element.
9803 inputEl: function ()
9805 return this.el.select('textarea.form-control',true).first();
9809 * Clear any invalid styles/messages for this field
9811 clearInvalid : function()
9814 if(!this.el || this.preventMark){ // not rendered
9818 var label = this.el.select('label', true).first();
9819 var icon = this.el.select('i.fa-star', true).first();
9825 this.el.removeClass(this.invalidClass);
9827 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9829 var feedback = this.el.select('.form-control-feedback', true).first();
9832 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9837 this.fireEvent('valid', this);
9841 * Mark this field as valid
9843 markValid : function()
9845 if(!this.el || this.preventMark){ // not rendered
9849 this.el.removeClass([this.invalidClass, this.validClass]);
9851 var feedback = this.el.select('.form-control-feedback', true).first();
9854 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9857 if(this.disabled || this.allowBlank){
9861 var label = this.el.select('label', true).first();
9862 var icon = this.el.select('i.fa-star', true).first();
9868 this.el.addClass(this.validClass);
9870 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9872 var feedback = this.el.select('.form-control-feedback', true).first();
9875 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9876 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9881 this.fireEvent('valid', this);
9885 * Mark this field as invalid
9886 * @param {String} msg The validation message
9888 markInvalid : function(msg)
9890 if(!this.el || this.preventMark){ // not rendered
9894 this.el.removeClass([this.invalidClass, this.validClass]);
9896 var feedback = this.el.select('.form-control-feedback', true).first();
9899 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9902 if(this.disabled || this.allowBlank){
9906 var label = this.el.select('label', true).first();
9907 var icon = this.el.select('i.fa-star', true).first();
9909 if(!this.getValue().length && label && !icon){
9910 this.el.createChild({
9912 cls : 'text-danger fa fa-lg fa-star',
9913 tooltip : 'This field is required',
9914 style : 'margin-right:5px;'
9918 this.el.addClass(this.invalidClass);
9920 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9922 var feedback = this.el.select('.form-control-feedback', true).first();
9925 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9927 if(this.getValue().length || this.forceFeedback){
9928 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9935 this.fireEvent('invalid', this, msg);
9943 * trigger field - base class for combo..
9948 * @class Roo.bootstrap.TriggerField
9949 * @extends Roo.bootstrap.Input
9950 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9951 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9952 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9953 * for which you can provide a custom implementation. For example:
9955 var trigger = new Roo.bootstrap.TriggerField();
9956 trigger.onTriggerClick = myTriggerFn;
9957 trigger.applyTo('my-field');
9960 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9961 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9962 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9963 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9964 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9967 * Create a new TriggerField.
9968 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9969 * to the base TextField)
9971 Roo.bootstrap.TriggerField = function(config){
9972 this.mimicing = false;
9973 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9976 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9978 * @cfg {String} triggerClass A CSS class to apply to the trigger
9981 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9986 * @cfg {Boolean} removable (true|false) special filter default false
9990 /** @cfg {Boolean} grow @hide */
9991 /** @cfg {Number} growMin @hide */
9992 /** @cfg {Number} growMax @hide */
9998 autoSize: Roo.emptyFn,
10002 deferHeight : true,
10005 actionMode : 'wrap',
10010 getAutoCreate : function(){
10012 var align = this.labelAlign || this.parentLabelAlign();
10017 cls: 'form-group' //input-group
10024 type : this.inputType,
10025 cls : 'form-control',
10026 autocomplete: 'new-password',
10027 placeholder : this.placeholder || ''
10031 input.name = this.name;
10034 input.cls += ' input-' + this.size;
10037 if (this.disabled) {
10038 input.disabled=true;
10041 var inputblock = input;
10043 if(this.hasFeedback && !this.allowBlank){
10047 cls: 'glyphicon form-control-feedback'
10050 if(this.removable && !this.editable && !this.tickable){
10052 cls : 'has-feedback',
10058 cls : 'roo-combo-removable-btn close'
10065 cls : 'has-feedback',
10074 if(this.removable && !this.editable && !this.tickable){
10076 cls : 'roo-removable',
10082 cls : 'roo-combo-removable-btn close'
10089 if (this.before || this.after) {
10092 cls : 'input-group',
10096 inputblock.cn.push({
10098 cls : 'input-group-addon',
10103 inputblock.cn.push(input);
10105 if(this.hasFeedback && !this.allowBlank){
10106 inputblock.cls += ' has-feedback';
10107 inputblock.cn.push(feedback);
10111 inputblock.cn.push({
10113 cls : 'input-group-addon',
10126 cls: 'form-hidden-field'
10140 cls: 'form-hidden-field'
10144 cls: 'roo-select2-choices',
10148 cls: 'roo-select2-search-field',
10161 cls: 'roo-select2-container input-group',
10166 // cls: 'typeahead typeahead-long dropdown-menu',
10167 // style: 'display:none'
10172 if(!this.multiple && this.showToggleBtn){
10178 if (this.caret != false) {
10181 cls: 'fa fa-' + this.caret
10188 cls : 'input-group-addon btn dropdown-toggle',
10193 cls: 'combobox-clear',
10207 combobox.cls += ' roo-select2-container-multi';
10210 if (align ==='left' && this.fieldLabel.length) {
10212 cfg.cls += ' roo-form-group-label-left';
10217 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10218 tooltip : 'This field is required'
10223 cls : 'control-label',
10224 html : this.fieldLabel
10236 var labelCfg = cfg.cn[1];
10237 var contentCfg = cfg.cn[2];
10239 if(this.indicatorpos == 'right'){
10244 cls : 'control-label',
10248 html : this.fieldLabel
10252 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10253 tooltip : 'This field is required'
10266 labelCfg = cfg.cn[0];
10267 contentCfg = cfg.cn[1];
10270 if(this.labelWidth > 12){
10271 labelCfg.style = "width: " + this.labelWidth + 'px';
10274 if(this.labelWidth < 13 && this.labelmd == 0){
10275 this.labelmd = this.labelWidth;
10278 if(this.labellg > 0){
10279 labelCfg.cls += ' col-lg-' + this.labellg;
10280 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10283 if(this.labelmd > 0){
10284 labelCfg.cls += ' col-md-' + this.labelmd;
10285 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10288 if(this.labelsm > 0){
10289 labelCfg.cls += ' col-sm-' + this.labelsm;
10290 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10293 if(this.labelxs > 0){
10294 labelCfg.cls += ' col-xs-' + this.labelxs;
10295 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10298 } else if ( this.fieldLabel.length) {
10299 // Roo.log(" label");
10303 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10304 tooltip : 'This field is required'
10308 //cls : 'input-group-addon',
10309 html : this.fieldLabel
10317 if(this.indicatorpos == 'right'){
10325 html : this.fieldLabel
10329 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10330 tooltip : 'This field is required'
10343 // Roo.log(" no label && no align");
10350 ['xs','sm','md','lg'].map(function(size){
10351 if (settings[size]) {
10352 cfg.cls += ' col-' + size + '-' + settings[size];
10363 onResize : function(w, h){
10364 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10365 // if(typeof w == 'number'){
10366 // var x = w - this.trigger.getWidth();
10367 // this.inputEl().setWidth(this.adjustWidth('input', x));
10368 // this.trigger.setStyle('left', x+'px');
10373 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10376 getResizeEl : function(){
10377 return this.inputEl();
10381 getPositionEl : function(){
10382 return this.inputEl();
10386 alignErrorIcon : function(){
10387 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10391 initEvents : function(){
10395 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10396 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10397 if(!this.multiple && this.showToggleBtn){
10398 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10399 if(this.hideTrigger){
10400 this.trigger.setDisplayed(false);
10402 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10406 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10409 if(this.removable && !this.editable && !this.tickable){
10410 var close = this.closeTriggerEl();
10413 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10414 close.on('click', this.removeBtnClick, this, close);
10418 //this.trigger.addClassOnOver('x-form-trigger-over');
10419 //this.trigger.addClassOnClick('x-form-trigger-click');
10422 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10426 closeTriggerEl : function()
10428 var close = this.el.select('.roo-combo-removable-btn', true).first();
10429 return close ? close : false;
10432 removeBtnClick : function(e, h, el)
10434 e.preventDefault();
10436 if(this.fireEvent("remove", this) !== false){
10438 this.fireEvent("afterremove", this)
10442 createList : function()
10444 this.list = Roo.get(document.body).createChild({
10446 cls: 'typeahead typeahead-long dropdown-menu',
10447 style: 'display:none'
10450 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10455 initTrigger : function(){
10460 onDestroy : function(){
10462 this.trigger.removeAllListeners();
10463 // this.trigger.remove();
10466 // this.wrap.remove();
10468 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10472 onFocus : function(){
10473 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10475 if(!this.mimicing){
10476 this.wrap.addClass('x-trigger-wrap-focus');
10477 this.mimicing = true;
10478 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10479 if(this.monitorTab){
10480 this.el.on("keydown", this.checkTab, this);
10487 checkTab : function(e){
10488 if(e.getKey() == e.TAB){
10489 this.triggerBlur();
10494 onBlur : function(){
10499 mimicBlur : function(e, t){
10501 if(!this.wrap.contains(t) && this.validateBlur()){
10502 this.triggerBlur();
10508 triggerBlur : function(){
10509 this.mimicing = false;
10510 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10511 if(this.monitorTab){
10512 this.el.un("keydown", this.checkTab, this);
10514 //this.wrap.removeClass('x-trigger-wrap-focus');
10515 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10519 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10520 validateBlur : function(e, t){
10525 onDisable : function(){
10526 this.inputEl().dom.disabled = true;
10527 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10529 // this.wrap.addClass('x-item-disabled');
10534 onEnable : function(){
10535 this.inputEl().dom.disabled = false;
10536 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10538 // this.el.removeClass('x-item-disabled');
10543 onShow : function(){
10544 var ae = this.getActionEl();
10547 ae.dom.style.display = '';
10548 ae.dom.style.visibility = 'visible';
10554 onHide : function(){
10555 var ae = this.getActionEl();
10556 ae.dom.style.display = 'none';
10560 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10561 * by an implementing function.
10563 * @param {EventObject} e
10565 onTriggerClick : Roo.emptyFn
10569 * Ext JS Library 1.1.1
10570 * Copyright(c) 2006-2007, Ext JS, LLC.
10572 * Originally Released Under LGPL - original licence link has changed is not relivant.
10575 * <script type="text/javascript">
10580 * @class Roo.data.SortTypes
10582 * Defines the default sorting (casting?) comparison functions used when sorting data.
10584 Roo.data.SortTypes = {
10586 * Default sort that does nothing
10587 * @param {Mixed} s The value being converted
10588 * @return {Mixed} The comparison value
10590 none : function(s){
10595 * The regular expression used to strip tags
10599 stripTagsRE : /<\/?[^>]+>/gi,
10602 * Strips all HTML tags to sort on text only
10603 * @param {Mixed} s The value being converted
10604 * @return {String} The comparison value
10606 asText : function(s){
10607 return String(s).replace(this.stripTagsRE, "");
10611 * Strips all HTML tags to sort on text only - Case insensitive
10612 * @param {Mixed} s The value being converted
10613 * @return {String} The comparison value
10615 asUCText : function(s){
10616 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10620 * Case insensitive string
10621 * @param {Mixed} s The value being converted
10622 * @return {String} The comparison value
10624 asUCString : function(s) {
10625 return String(s).toUpperCase();
10630 * @param {Mixed} s The value being converted
10631 * @return {Number} The comparison value
10633 asDate : function(s) {
10637 if(s instanceof Date){
10638 return s.getTime();
10640 return Date.parse(String(s));
10645 * @param {Mixed} s The value being converted
10646 * @return {Float} The comparison value
10648 asFloat : function(s) {
10649 var val = parseFloat(String(s).replace(/,/g, ""));
10658 * @param {Mixed} s The value being converted
10659 * @return {Number} The comparison value
10661 asInt : function(s) {
10662 var val = parseInt(String(s).replace(/,/g, ""));
10670 * Ext JS Library 1.1.1
10671 * Copyright(c) 2006-2007, Ext JS, LLC.
10673 * Originally Released Under LGPL - original licence link has changed is not relivant.
10676 * <script type="text/javascript">
10680 * @class Roo.data.Record
10681 * Instances of this class encapsulate both record <em>definition</em> information, and record
10682 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10683 * to access Records cached in an {@link Roo.data.Store} object.<br>
10685 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10686 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10689 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10691 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10692 * {@link #create}. The parameters are the same.
10693 * @param {Array} data An associative Array of data values keyed by the field name.
10694 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10695 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10696 * not specified an integer id is generated.
10698 Roo.data.Record = function(data, id){
10699 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10704 * Generate a constructor for a specific record layout.
10705 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10706 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10707 * Each field definition object may contain the following properties: <ul>
10708 * <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,
10709 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10710 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10711 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10712 * is being used, then this is a string containing the javascript expression to reference the data relative to
10713 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10714 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10715 * this may be omitted.</p></li>
10716 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10717 * <ul><li>auto (Default, implies no conversion)</li>
10722 * <li>date</li></ul></p></li>
10723 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10724 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10725 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10726 * by the Reader into an object that will be stored in the Record. It is passed the
10727 * following parameters:<ul>
10728 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10730 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10732 * <br>usage:<br><pre><code>
10733 var TopicRecord = Roo.data.Record.create(
10734 {name: 'title', mapping: 'topic_title'},
10735 {name: 'author', mapping: 'username'},
10736 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10737 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10738 {name: 'lastPoster', mapping: 'user2'},
10739 {name: 'excerpt', mapping: 'post_text'}
10742 var myNewRecord = new TopicRecord({
10743 title: 'Do my job please',
10746 lastPost: new Date(),
10747 lastPoster: 'Animal',
10748 excerpt: 'No way dude!'
10750 myStore.add(myNewRecord);
10755 Roo.data.Record.create = function(o){
10756 var f = function(){
10757 f.superclass.constructor.apply(this, arguments);
10759 Roo.extend(f, Roo.data.Record);
10760 var p = f.prototype;
10761 p.fields = new Roo.util.MixedCollection(false, function(field){
10764 for(var i = 0, len = o.length; i < len; i++){
10765 p.fields.add(new Roo.data.Field(o[i]));
10767 f.getField = function(name){
10768 return p.fields.get(name);
10773 Roo.data.Record.AUTO_ID = 1000;
10774 Roo.data.Record.EDIT = 'edit';
10775 Roo.data.Record.REJECT = 'reject';
10776 Roo.data.Record.COMMIT = 'commit';
10778 Roo.data.Record.prototype = {
10780 * Readonly flag - true if this record has been modified.
10789 join : function(store){
10790 this.store = store;
10794 * Set the named field to the specified value.
10795 * @param {String} name The name of the field to set.
10796 * @param {Object} value The value to set the field to.
10798 set : function(name, value){
10799 if(this.data[name] == value){
10803 if(!this.modified){
10804 this.modified = {};
10806 if(typeof this.modified[name] == 'undefined'){
10807 this.modified[name] = this.data[name];
10809 this.data[name] = value;
10810 if(!this.editing && this.store){
10811 this.store.afterEdit(this);
10816 * Get the value of the named field.
10817 * @param {String} name The name of the field to get the value of.
10818 * @return {Object} The value of the field.
10820 get : function(name){
10821 return this.data[name];
10825 beginEdit : function(){
10826 this.editing = true;
10827 this.modified = {};
10831 cancelEdit : function(){
10832 this.editing = false;
10833 delete this.modified;
10837 endEdit : function(){
10838 this.editing = false;
10839 if(this.dirty && this.store){
10840 this.store.afterEdit(this);
10845 * Usually called by the {@link Roo.data.Store} which owns the Record.
10846 * Rejects all changes made to the Record since either creation, or the last commit operation.
10847 * Modified fields are reverted to their original values.
10849 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10850 * of reject operations.
10852 reject : function(){
10853 var m = this.modified;
10855 if(typeof m[n] != "function"){
10856 this.data[n] = m[n];
10859 this.dirty = false;
10860 delete this.modified;
10861 this.editing = false;
10863 this.store.afterReject(this);
10868 * Usually called by the {@link Roo.data.Store} which owns the Record.
10869 * Commits all changes made to the Record since either creation, or the last commit operation.
10871 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10872 * of commit operations.
10874 commit : function(){
10875 this.dirty = false;
10876 delete this.modified;
10877 this.editing = false;
10879 this.store.afterCommit(this);
10884 hasError : function(){
10885 return this.error != null;
10889 clearError : function(){
10894 * Creates a copy of this record.
10895 * @param {String} id (optional) A new record id if you don't want to use this record's id
10898 copy : function(newId) {
10899 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10903 * Ext JS Library 1.1.1
10904 * Copyright(c) 2006-2007, Ext JS, LLC.
10906 * Originally Released Under LGPL - original licence link has changed is not relivant.
10909 * <script type="text/javascript">
10915 * @class Roo.data.Store
10916 * @extends Roo.util.Observable
10917 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10918 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10920 * 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
10921 * has no knowledge of the format of the data returned by the Proxy.<br>
10923 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10924 * instances from the data object. These records are cached and made available through accessor functions.
10926 * Creates a new Store.
10927 * @param {Object} config A config object containing the objects needed for the Store to access data,
10928 * and read the data into Records.
10930 Roo.data.Store = function(config){
10931 this.data = new Roo.util.MixedCollection(false);
10932 this.data.getKey = function(o){
10935 this.baseParams = {};
10937 this.paramNames = {
10942 "multisort" : "_multisort"
10945 if(config && config.data){
10946 this.inlineData = config.data;
10947 delete config.data;
10950 Roo.apply(this, config);
10952 if(this.reader){ // reader passed
10953 this.reader = Roo.factory(this.reader, Roo.data);
10954 this.reader.xmodule = this.xmodule || false;
10955 if(!this.recordType){
10956 this.recordType = this.reader.recordType;
10958 if(this.reader.onMetaChange){
10959 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10963 if(this.recordType){
10964 this.fields = this.recordType.prototype.fields;
10966 this.modified = [];
10970 * @event datachanged
10971 * Fires when the data cache has changed, and a widget which is using this Store
10972 * as a Record cache should refresh its view.
10973 * @param {Store} this
10975 datachanged : true,
10977 * @event metachange
10978 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10979 * @param {Store} this
10980 * @param {Object} meta The JSON metadata
10985 * Fires when Records have been added to the Store
10986 * @param {Store} this
10987 * @param {Roo.data.Record[]} records The array of Records added
10988 * @param {Number} index The index at which the record(s) were added
10993 * Fires when a Record has been removed from the Store
10994 * @param {Store} this
10995 * @param {Roo.data.Record} record The Record that was removed
10996 * @param {Number} index The index at which the record was removed
11001 * Fires when a Record has been updated
11002 * @param {Store} this
11003 * @param {Roo.data.Record} record The Record that was updated
11004 * @param {String} operation The update operation being performed. Value may be one of:
11006 Roo.data.Record.EDIT
11007 Roo.data.Record.REJECT
11008 Roo.data.Record.COMMIT
11014 * Fires when the data cache has been cleared.
11015 * @param {Store} this
11019 * @event beforeload
11020 * Fires before a request is made for a new data object. If the beforeload handler returns false
11021 * the load action will be canceled.
11022 * @param {Store} this
11023 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11027 * @event beforeloadadd
11028 * Fires after a new set of Records has been loaded.
11029 * @param {Store} this
11030 * @param {Roo.data.Record[]} records The Records that were loaded
11031 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11033 beforeloadadd : true,
11036 * Fires after a new set of Records has been loaded, before they are added to the store.
11037 * @param {Store} this
11038 * @param {Roo.data.Record[]} records The Records that were loaded
11039 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11040 * @params {Object} return from reader
11044 * @event loadexception
11045 * Fires if an exception occurs in the Proxy during loading.
11046 * Called with the signature of the Proxy's "loadexception" event.
11047 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11050 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11051 * @param {Object} load options
11052 * @param {Object} jsonData from your request (normally this contains the Exception)
11054 loadexception : true
11058 this.proxy = Roo.factory(this.proxy, Roo.data);
11059 this.proxy.xmodule = this.xmodule || false;
11060 this.relayEvents(this.proxy, ["loadexception"]);
11062 this.sortToggle = {};
11063 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11065 Roo.data.Store.superclass.constructor.call(this);
11067 if(this.inlineData){
11068 this.loadData(this.inlineData);
11069 delete this.inlineData;
11073 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11075 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11076 * without a remote query - used by combo/forms at present.
11080 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11083 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11086 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11087 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11090 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11091 * on any HTTP request
11094 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11097 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11101 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11102 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11104 remoteSort : false,
11107 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11108 * loaded or when a record is removed. (defaults to false).
11110 pruneModifiedRecords : false,
11113 lastOptions : null,
11116 * Add Records to the Store and fires the add event.
11117 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11119 add : function(records){
11120 records = [].concat(records);
11121 for(var i = 0, len = records.length; i < len; i++){
11122 records[i].join(this);
11124 var index = this.data.length;
11125 this.data.addAll(records);
11126 this.fireEvent("add", this, records, index);
11130 * Remove a Record from the Store and fires the remove event.
11131 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11133 remove : function(record){
11134 var index = this.data.indexOf(record);
11135 this.data.removeAt(index);
11136 if(this.pruneModifiedRecords){
11137 this.modified.remove(record);
11139 this.fireEvent("remove", this, record, index);
11143 * Remove all Records from the Store and fires the clear event.
11145 removeAll : function(){
11147 if(this.pruneModifiedRecords){
11148 this.modified = [];
11150 this.fireEvent("clear", this);
11154 * Inserts Records to the Store at the given index and fires the add event.
11155 * @param {Number} index The start index at which to insert the passed Records.
11156 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11158 insert : function(index, records){
11159 records = [].concat(records);
11160 for(var i = 0, len = records.length; i < len; i++){
11161 this.data.insert(index, records[i]);
11162 records[i].join(this);
11164 this.fireEvent("add", this, records, index);
11168 * Get the index within the cache of the passed Record.
11169 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11170 * @return {Number} The index of the passed Record. Returns -1 if not found.
11172 indexOf : function(record){
11173 return this.data.indexOf(record);
11177 * Get the index within the cache of the Record with the passed id.
11178 * @param {String} id The id of the Record to find.
11179 * @return {Number} The index of the Record. Returns -1 if not found.
11181 indexOfId : function(id){
11182 return this.data.indexOfKey(id);
11186 * Get the Record with the specified id.
11187 * @param {String} id The id of the Record to find.
11188 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11190 getById : function(id){
11191 return this.data.key(id);
11195 * Get the Record at the specified index.
11196 * @param {Number} index The index of the Record to find.
11197 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11199 getAt : function(index){
11200 return this.data.itemAt(index);
11204 * Returns a range of Records between specified indices.
11205 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11206 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11207 * @return {Roo.data.Record[]} An array of Records
11209 getRange : function(start, end){
11210 return this.data.getRange(start, end);
11214 storeOptions : function(o){
11215 o = Roo.apply({}, o);
11218 this.lastOptions = o;
11222 * Loads the Record cache from the configured Proxy using the configured Reader.
11224 * If using remote paging, then the first load call must specify the <em>start</em>
11225 * and <em>limit</em> properties in the options.params property to establish the initial
11226 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11228 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11229 * and this call will return before the new data has been loaded. Perform any post-processing
11230 * in a callback function, or in a "load" event handler.</strong>
11232 * @param {Object} options An object containing properties which control loading options:<ul>
11233 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11234 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11235 * passed the following arguments:<ul>
11236 * <li>r : Roo.data.Record[]</li>
11237 * <li>options: Options object from the load call</li>
11238 * <li>success: Boolean success indicator</li></ul></li>
11239 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11240 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11243 load : function(options){
11244 options = options || {};
11245 if(this.fireEvent("beforeload", this, options) !== false){
11246 this.storeOptions(options);
11247 var p = Roo.apply(options.params || {}, this.baseParams);
11248 // if meta was not loaded from remote source.. try requesting it.
11249 if (!this.reader.metaFromRemote) {
11250 p._requestMeta = 1;
11252 if(this.sortInfo && this.remoteSort){
11253 var pn = this.paramNames;
11254 p[pn["sort"]] = this.sortInfo.field;
11255 p[pn["dir"]] = this.sortInfo.direction;
11257 if (this.multiSort) {
11258 var pn = this.paramNames;
11259 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11262 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11267 * Reloads the Record cache from the configured Proxy using the configured Reader and
11268 * the options from the last load operation performed.
11269 * @param {Object} options (optional) An object containing properties which may override the options
11270 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11271 * the most recently used options are reused).
11273 reload : function(options){
11274 this.load(Roo.applyIf(options||{}, this.lastOptions));
11278 // Called as a callback by the Reader during a load operation.
11279 loadRecords : function(o, options, success){
11280 if(!o || success === false){
11281 if(success !== false){
11282 this.fireEvent("load", this, [], options, o);
11284 if(options.callback){
11285 options.callback.call(options.scope || this, [], options, false);
11289 // if data returned failure - throw an exception.
11290 if (o.success === false) {
11291 // show a message if no listener is registered.
11292 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11293 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11295 // loadmask wil be hooked into this..
11296 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11299 var r = o.records, t = o.totalRecords || r.length;
11301 this.fireEvent("beforeloadadd", this, r, options, o);
11303 if(!options || options.add !== true){
11304 if(this.pruneModifiedRecords){
11305 this.modified = [];
11307 for(var i = 0, len = r.length; i < len; i++){
11311 this.data = this.snapshot;
11312 delete this.snapshot;
11315 this.data.addAll(r);
11316 this.totalLength = t;
11318 this.fireEvent("datachanged", this);
11320 this.totalLength = Math.max(t, this.data.length+r.length);
11324 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11326 var e = new Roo.data.Record({});
11328 e.set(this.parent.displayField, this.parent.emptyTitle);
11329 e.set(this.parent.valueField, '');
11334 this.fireEvent("load", this, r, options, o);
11335 if(options.callback){
11336 options.callback.call(options.scope || this, r, options, true);
11342 * Loads data from a passed data block. A Reader which understands the format of the data
11343 * must have been configured in the constructor.
11344 * @param {Object} data The data block from which to read the Records. The format of the data expected
11345 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11346 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11348 loadData : function(o, append){
11349 var r = this.reader.readRecords(o);
11350 this.loadRecords(r, {add: append}, true);
11354 * Gets the number of cached records.
11356 * <em>If using paging, this may not be the total size of the dataset. If the data object
11357 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11358 * the data set size</em>
11360 getCount : function(){
11361 return this.data.length || 0;
11365 * Gets the total number of records in the dataset as returned by the server.
11367 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11368 * the dataset size</em>
11370 getTotalCount : function(){
11371 return this.totalLength || 0;
11375 * Returns the sort state of the Store as an object with two properties:
11377 field {String} The name of the field by which the Records are sorted
11378 direction {String} The sort order, "ASC" or "DESC"
11381 getSortState : function(){
11382 return this.sortInfo;
11386 applySort : function(){
11387 if(this.sortInfo && !this.remoteSort){
11388 var s = this.sortInfo, f = s.field;
11389 var st = this.fields.get(f).sortType;
11390 var fn = function(r1, r2){
11391 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11392 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11394 this.data.sort(s.direction, fn);
11395 if(this.snapshot && this.snapshot != this.data){
11396 this.snapshot.sort(s.direction, fn);
11402 * Sets the default sort column and order to be used by the next load operation.
11403 * @param {String} fieldName The name of the field to sort by.
11404 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11406 setDefaultSort : function(field, dir){
11407 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11411 * Sort the Records.
11412 * If remote sorting is used, the sort is performed on the server, and the cache is
11413 * reloaded. If local sorting is used, the cache is sorted internally.
11414 * @param {String} fieldName The name of the field to sort by.
11415 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11417 sort : function(fieldName, dir){
11418 var f = this.fields.get(fieldName);
11420 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11422 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11423 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11428 this.sortToggle[f.name] = dir;
11429 this.sortInfo = {field: f.name, direction: dir};
11430 if(!this.remoteSort){
11432 this.fireEvent("datachanged", this);
11434 this.load(this.lastOptions);
11439 * Calls the specified function for each of the Records in the cache.
11440 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11441 * Returning <em>false</em> aborts and exits the iteration.
11442 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11444 each : function(fn, scope){
11445 this.data.each(fn, scope);
11449 * Gets all records modified since the last commit. Modified records are persisted across load operations
11450 * (e.g., during paging).
11451 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11453 getModifiedRecords : function(){
11454 return this.modified;
11458 createFilterFn : function(property, value, anyMatch){
11459 if(!value.exec){ // not a regex
11460 value = String(value);
11461 if(value.length == 0){
11464 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11466 return function(r){
11467 return value.test(r.data[property]);
11472 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11473 * @param {String} property A field on your records
11474 * @param {Number} start The record index to start at (defaults to 0)
11475 * @param {Number} end The last record index to include (defaults to length - 1)
11476 * @return {Number} The sum
11478 sum : function(property, start, end){
11479 var rs = this.data.items, v = 0;
11480 start = start || 0;
11481 end = (end || end === 0) ? end : rs.length-1;
11483 for(var i = start; i <= end; i++){
11484 v += (rs[i].data[property] || 0);
11490 * Filter the records by a specified property.
11491 * @param {String} field A field on your records
11492 * @param {String/RegExp} value Either a string that the field
11493 * should start with or a RegExp to test against the field
11494 * @param {Boolean} anyMatch True to match any part not just the beginning
11496 filter : function(property, value, anyMatch){
11497 var fn = this.createFilterFn(property, value, anyMatch);
11498 return fn ? this.filterBy(fn) : this.clearFilter();
11502 * Filter by a function. The specified function will be called with each
11503 * record in this data source. If the function returns true the record is included,
11504 * otherwise it is filtered.
11505 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11506 * @param {Object} scope (optional) The scope of the function (defaults to this)
11508 filterBy : function(fn, scope){
11509 this.snapshot = this.snapshot || this.data;
11510 this.data = this.queryBy(fn, scope||this);
11511 this.fireEvent("datachanged", this);
11515 * Query the records by a specified property.
11516 * @param {String} field A field on your records
11517 * @param {String/RegExp} value Either a string that the field
11518 * should start with or a RegExp to test against the field
11519 * @param {Boolean} anyMatch True to match any part not just the beginning
11520 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11522 query : function(property, value, anyMatch){
11523 var fn = this.createFilterFn(property, value, anyMatch);
11524 return fn ? this.queryBy(fn) : this.data.clone();
11528 * Query by a function. The specified function will be called with each
11529 * record in this data source. If the function returns true the record is included
11531 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11532 * @param {Object} scope (optional) The scope of the function (defaults to this)
11533 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11535 queryBy : function(fn, scope){
11536 var data = this.snapshot || this.data;
11537 return data.filterBy(fn, scope||this);
11541 * Collects unique values for a particular dataIndex from this store.
11542 * @param {String} dataIndex The property to collect
11543 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11544 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11545 * @return {Array} An array of the unique values
11547 collect : function(dataIndex, allowNull, bypassFilter){
11548 var d = (bypassFilter === true && this.snapshot) ?
11549 this.snapshot.items : this.data.items;
11550 var v, sv, r = [], l = {};
11551 for(var i = 0, len = d.length; i < len; i++){
11552 v = d[i].data[dataIndex];
11554 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11563 * Revert to a view of the Record cache with no filtering applied.
11564 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11566 clearFilter : function(suppressEvent){
11567 if(this.snapshot && this.snapshot != this.data){
11568 this.data = this.snapshot;
11569 delete this.snapshot;
11570 if(suppressEvent !== true){
11571 this.fireEvent("datachanged", this);
11577 afterEdit : function(record){
11578 if(this.modified.indexOf(record) == -1){
11579 this.modified.push(record);
11581 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11585 afterReject : function(record){
11586 this.modified.remove(record);
11587 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11591 afterCommit : function(record){
11592 this.modified.remove(record);
11593 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11597 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11598 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11600 commitChanges : function(){
11601 var m = this.modified.slice(0);
11602 this.modified = [];
11603 for(var i = 0, len = m.length; i < len; i++){
11609 * Cancel outstanding changes on all changed records.
11611 rejectChanges : function(){
11612 var m = this.modified.slice(0);
11613 this.modified = [];
11614 for(var i = 0, len = m.length; i < len; i++){
11619 onMetaChange : function(meta, rtype, o){
11620 this.recordType = rtype;
11621 this.fields = rtype.prototype.fields;
11622 delete this.snapshot;
11623 this.sortInfo = meta.sortInfo || this.sortInfo;
11624 this.modified = [];
11625 this.fireEvent('metachange', this, this.reader.meta);
11628 moveIndex : function(data, type)
11630 var index = this.indexOf(data);
11632 var newIndex = index + type;
11636 this.insert(newIndex, data);
11641 * Ext JS Library 1.1.1
11642 * Copyright(c) 2006-2007, Ext JS, LLC.
11644 * Originally Released Under LGPL - original licence link has changed is not relivant.
11647 * <script type="text/javascript">
11651 * @class Roo.data.SimpleStore
11652 * @extends Roo.data.Store
11653 * Small helper class to make creating Stores from Array data easier.
11654 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11655 * @cfg {Array} fields An array of field definition objects, or field name strings.
11656 * @cfg {Array} data The multi-dimensional array of data
11658 * @param {Object} config
11660 Roo.data.SimpleStore = function(config){
11661 Roo.data.SimpleStore.superclass.constructor.call(this, {
11663 reader: new Roo.data.ArrayReader({
11666 Roo.data.Record.create(config.fields)
11668 proxy : new Roo.data.MemoryProxy(config.data)
11672 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11674 * Ext JS Library 1.1.1
11675 * Copyright(c) 2006-2007, Ext JS, LLC.
11677 * Originally Released Under LGPL - original licence link has changed is not relivant.
11680 * <script type="text/javascript">
11685 * @extends Roo.data.Store
11686 * @class Roo.data.JsonStore
11687 * Small helper class to make creating Stores for JSON data easier. <br/>
11689 var store = new Roo.data.JsonStore({
11690 url: 'get-images.php',
11692 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11695 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11696 * JsonReader and HttpProxy (unless inline data is provided).</b>
11697 * @cfg {Array} fields An array of field definition objects, or field name strings.
11699 * @param {Object} config
11701 Roo.data.JsonStore = function(c){
11702 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11703 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11704 reader: new Roo.data.JsonReader(c, c.fields)
11707 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11709 * Ext JS Library 1.1.1
11710 * Copyright(c) 2006-2007, Ext JS, LLC.
11712 * Originally Released Under LGPL - original licence link has changed is not relivant.
11715 * <script type="text/javascript">
11719 Roo.data.Field = function(config){
11720 if(typeof config == "string"){
11721 config = {name: config};
11723 Roo.apply(this, config);
11726 this.type = "auto";
11729 var st = Roo.data.SortTypes;
11730 // named sortTypes are supported, here we look them up
11731 if(typeof this.sortType == "string"){
11732 this.sortType = st[this.sortType];
11735 // set default sortType for strings and dates
11736 if(!this.sortType){
11739 this.sortType = st.asUCString;
11742 this.sortType = st.asDate;
11745 this.sortType = st.none;
11750 var stripRe = /[\$,%]/g;
11752 // prebuilt conversion function for this field, instead of
11753 // switching every time we're reading a value
11755 var cv, dateFormat = this.dateFormat;
11760 cv = function(v){ return v; };
11763 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11767 return v !== undefined && v !== null && v !== '' ?
11768 parseInt(String(v).replace(stripRe, ""), 10) : '';
11773 return v !== undefined && v !== null && v !== '' ?
11774 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11779 cv = function(v){ return v === true || v === "true" || v == 1; };
11786 if(v instanceof Date){
11790 if(dateFormat == "timestamp"){
11791 return new Date(v*1000);
11793 return Date.parseDate(v, dateFormat);
11795 var parsed = Date.parse(v);
11796 return parsed ? new Date(parsed) : null;
11805 Roo.data.Field.prototype = {
11813 * Ext JS Library 1.1.1
11814 * Copyright(c) 2006-2007, Ext JS, LLC.
11816 * Originally Released Under LGPL - original licence link has changed is not relivant.
11819 * <script type="text/javascript">
11822 // Base class for reading structured data from a data source. This class is intended to be
11823 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11826 * @class Roo.data.DataReader
11827 * Base class for reading structured data from a data source. This class is intended to be
11828 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11831 Roo.data.DataReader = function(meta, recordType){
11835 this.recordType = recordType instanceof Array ?
11836 Roo.data.Record.create(recordType) : recordType;
11839 Roo.data.DataReader.prototype = {
11841 * Create an empty record
11842 * @param {Object} data (optional) - overlay some values
11843 * @return {Roo.data.Record} record created.
11845 newRow : function(d) {
11847 this.recordType.prototype.fields.each(function(c) {
11849 case 'int' : da[c.name] = 0; break;
11850 case 'date' : da[c.name] = new Date(); break;
11851 case 'float' : da[c.name] = 0.0; break;
11852 case 'boolean' : da[c.name] = false; break;
11853 default : da[c.name] = ""; break;
11857 return new this.recordType(Roo.apply(da, d));
11862 * Ext JS Library 1.1.1
11863 * Copyright(c) 2006-2007, Ext JS, LLC.
11865 * Originally Released Under LGPL - original licence link has changed is not relivant.
11868 * <script type="text/javascript">
11872 * @class Roo.data.DataProxy
11873 * @extends Roo.data.Observable
11874 * This class is an abstract base class for implementations which provide retrieval of
11875 * unformatted data objects.<br>
11877 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11878 * (of the appropriate type which knows how to parse the data object) to provide a block of
11879 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11881 * Custom implementations must implement the load method as described in
11882 * {@link Roo.data.HttpProxy#load}.
11884 Roo.data.DataProxy = function(){
11887 * @event beforeload
11888 * Fires before a network request is made to retrieve a data object.
11889 * @param {Object} This DataProxy object.
11890 * @param {Object} params The params parameter to the load function.
11895 * Fires before the load method's callback is called.
11896 * @param {Object} This DataProxy object.
11897 * @param {Object} o The data object.
11898 * @param {Object} arg The callback argument object passed to the load function.
11902 * @event loadexception
11903 * Fires if an Exception occurs during data retrieval.
11904 * @param {Object} This DataProxy object.
11905 * @param {Object} o The data object.
11906 * @param {Object} arg The callback argument object passed to the load function.
11907 * @param {Object} e The Exception.
11909 loadexception : true
11911 Roo.data.DataProxy.superclass.constructor.call(this);
11914 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11917 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11921 * Ext JS Library 1.1.1
11922 * Copyright(c) 2006-2007, Ext JS, LLC.
11924 * Originally Released Under LGPL - original licence link has changed is not relivant.
11927 * <script type="text/javascript">
11930 * @class Roo.data.MemoryProxy
11931 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11932 * to the Reader when its load method is called.
11934 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11936 Roo.data.MemoryProxy = function(data){
11940 Roo.data.MemoryProxy.superclass.constructor.call(this);
11944 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11947 * Load data from the requested source (in this case an in-memory
11948 * data object passed to the constructor), read the data object into
11949 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11950 * process that block using the passed callback.
11951 * @param {Object} params This parameter is not used by the MemoryProxy class.
11952 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11953 * object into a block of Roo.data.Records.
11954 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11955 * The function must be passed <ul>
11956 * <li>The Record block object</li>
11957 * <li>The "arg" argument from the load function</li>
11958 * <li>A boolean success indicator</li>
11960 * @param {Object} scope The scope in which to call the callback
11961 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11963 load : function(params, reader, callback, scope, arg){
11964 params = params || {};
11967 result = reader.readRecords(this.data);
11969 this.fireEvent("loadexception", this, arg, null, e);
11970 callback.call(scope, null, arg, false);
11973 callback.call(scope, result, arg, true);
11977 update : function(params, records){
11982 * Ext JS Library 1.1.1
11983 * Copyright(c) 2006-2007, Ext JS, LLC.
11985 * Originally Released Under LGPL - original licence link has changed is not relivant.
11988 * <script type="text/javascript">
11991 * @class Roo.data.HttpProxy
11992 * @extends Roo.data.DataProxy
11993 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11994 * configured to reference a certain URL.<br><br>
11996 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11997 * from which the running page was served.<br><br>
11999 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12001 * Be aware that to enable the browser to parse an XML document, the server must set
12002 * the Content-Type header in the HTTP response to "text/xml".
12004 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12005 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12006 * will be used to make the request.
12008 Roo.data.HttpProxy = function(conn){
12009 Roo.data.HttpProxy.superclass.constructor.call(this);
12010 // is conn a conn config or a real conn?
12012 this.useAjax = !conn || !conn.events;
12016 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12017 // thse are take from connection...
12020 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12023 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12024 * extra parameters to each request made by this object. (defaults to undefined)
12027 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12028 * to each request made by this object. (defaults to undefined)
12031 * @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)
12034 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12037 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12043 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12047 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12048 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12049 * a finer-grained basis than the DataProxy events.
12051 getConnection : function(){
12052 return this.useAjax ? Roo.Ajax : this.conn;
12056 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12057 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12058 * process that block using the passed callback.
12059 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12060 * for the request to the remote server.
12061 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12062 * object into a block of Roo.data.Records.
12063 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12064 * The function must be passed <ul>
12065 * <li>The Record block object</li>
12066 * <li>The "arg" argument from the load function</li>
12067 * <li>A boolean success indicator</li>
12069 * @param {Object} scope The scope in which to call the callback
12070 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12072 load : function(params, reader, callback, scope, arg){
12073 if(this.fireEvent("beforeload", this, params) !== false){
12075 params : params || {},
12077 callback : callback,
12082 callback : this.loadResponse,
12086 Roo.applyIf(o, this.conn);
12087 if(this.activeRequest){
12088 Roo.Ajax.abort(this.activeRequest);
12090 this.activeRequest = Roo.Ajax.request(o);
12092 this.conn.request(o);
12095 callback.call(scope||this, null, arg, false);
12100 loadResponse : function(o, success, response){
12101 delete this.activeRequest;
12103 this.fireEvent("loadexception", this, o, response);
12104 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12109 result = o.reader.read(response);
12111 this.fireEvent("loadexception", this, o, response, e);
12112 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12116 this.fireEvent("load", this, o, o.request.arg);
12117 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12121 update : function(dataSet){
12126 updateResponse : function(dataSet){
12131 * Ext JS Library 1.1.1
12132 * Copyright(c) 2006-2007, Ext JS, LLC.
12134 * Originally Released Under LGPL - original licence link has changed is not relivant.
12137 * <script type="text/javascript">
12141 * @class Roo.data.ScriptTagProxy
12142 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12143 * other than the originating domain of the running page.<br><br>
12145 * <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
12146 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12148 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12149 * source code that is used as the source inside a <script> tag.<br><br>
12151 * In order for the browser to process the returned data, the server must wrap the data object
12152 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12153 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12154 * depending on whether the callback name was passed:
12157 boolean scriptTag = false;
12158 String cb = request.getParameter("callback");
12161 response.setContentType("text/javascript");
12163 response.setContentType("application/x-json");
12165 Writer out = response.getWriter();
12167 out.write(cb + "(");
12169 out.print(dataBlock.toJsonString());
12176 * @param {Object} config A configuration object.
12178 Roo.data.ScriptTagProxy = function(config){
12179 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12180 Roo.apply(this, config);
12181 this.head = document.getElementsByTagName("head")[0];
12184 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12186 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12188 * @cfg {String} url The URL from which to request the data object.
12191 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12195 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12196 * the server the name of the callback function set up by the load call to process the returned data object.
12197 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12198 * javascript output which calls this named function passing the data object as its only parameter.
12200 callbackParam : "callback",
12202 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12203 * name to the request.
12208 * Load data from the configured URL, read the data object into
12209 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12210 * process that block using the passed callback.
12211 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12212 * for the request to the remote server.
12213 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12214 * object into a block of Roo.data.Records.
12215 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12216 * The function must be passed <ul>
12217 * <li>The Record block object</li>
12218 * <li>The "arg" argument from the load function</li>
12219 * <li>A boolean success indicator</li>
12221 * @param {Object} scope The scope in which to call the callback
12222 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12224 load : function(params, reader, callback, scope, arg){
12225 if(this.fireEvent("beforeload", this, params) !== false){
12227 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12229 var url = this.url;
12230 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12232 url += "&_dc=" + (new Date().getTime());
12234 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12237 cb : "stcCallback"+transId,
12238 scriptId : "stcScript"+transId,
12242 callback : callback,
12248 window[trans.cb] = function(o){
12249 conn.handleResponse(o, trans);
12252 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12254 if(this.autoAbort !== false){
12258 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12260 var script = document.createElement("script");
12261 script.setAttribute("src", url);
12262 script.setAttribute("type", "text/javascript");
12263 script.setAttribute("id", trans.scriptId);
12264 this.head.appendChild(script);
12266 this.trans = trans;
12268 callback.call(scope||this, null, arg, false);
12273 isLoading : function(){
12274 return this.trans ? true : false;
12278 * Abort the current server request.
12280 abort : function(){
12281 if(this.isLoading()){
12282 this.destroyTrans(this.trans);
12287 destroyTrans : function(trans, isLoaded){
12288 this.head.removeChild(document.getElementById(trans.scriptId));
12289 clearTimeout(trans.timeoutId);
12291 window[trans.cb] = undefined;
12293 delete window[trans.cb];
12296 // if hasn't been loaded, wait for load to remove it to prevent script error
12297 window[trans.cb] = function(){
12298 window[trans.cb] = undefined;
12300 delete window[trans.cb];
12307 handleResponse : function(o, trans){
12308 this.trans = false;
12309 this.destroyTrans(trans, true);
12312 result = trans.reader.readRecords(o);
12314 this.fireEvent("loadexception", this, o, trans.arg, e);
12315 trans.callback.call(trans.scope||window, null, trans.arg, false);
12318 this.fireEvent("load", this, o, trans.arg);
12319 trans.callback.call(trans.scope||window, result, trans.arg, true);
12323 handleFailure : function(trans){
12324 this.trans = false;
12325 this.destroyTrans(trans, false);
12326 this.fireEvent("loadexception", this, null, trans.arg);
12327 trans.callback.call(trans.scope||window, null, trans.arg, false);
12331 * Ext JS Library 1.1.1
12332 * Copyright(c) 2006-2007, Ext JS, LLC.
12334 * Originally Released Under LGPL - original licence link has changed is not relivant.
12337 * <script type="text/javascript">
12341 * @class Roo.data.JsonReader
12342 * @extends Roo.data.DataReader
12343 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12344 * based on mappings in a provided Roo.data.Record constructor.
12346 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12347 * in the reply previously.
12352 var RecordDef = Roo.data.Record.create([
12353 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12354 {name: 'occupation'} // This field will use "occupation" as the mapping.
12356 var myReader = new Roo.data.JsonReader({
12357 totalProperty: "results", // The property which contains the total dataset size (optional)
12358 root: "rows", // The property which contains an Array of row objects
12359 id: "id" // The property within each row object that provides an ID for the record (optional)
12363 * This would consume a JSON file like this:
12365 { 'results': 2, 'rows': [
12366 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12367 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12370 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12371 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12372 * paged from the remote server.
12373 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12374 * @cfg {String} root name of the property which contains the Array of row objects.
12375 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12376 * @cfg {Array} fields Array of field definition objects
12378 * Create a new JsonReader
12379 * @param {Object} meta Metadata configuration options
12380 * @param {Object} recordType Either an Array of field definition objects,
12381 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12383 Roo.data.JsonReader = function(meta, recordType){
12386 // set some defaults:
12387 Roo.applyIf(meta, {
12388 totalProperty: 'total',
12389 successProperty : 'success',
12394 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12396 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12399 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12400 * Used by Store query builder to append _requestMeta to params.
12403 metaFromRemote : false,
12405 * This method is only used by a DataProxy which has retrieved data from a remote server.
12406 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12407 * @return {Object} data A data block which is used by an Roo.data.Store object as
12408 * a cache of Roo.data.Records.
12410 read : function(response){
12411 var json = response.responseText;
12413 var o = /* eval:var:o */ eval("("+json+")");
12415 throw {message: "JsonReader.read: Json object not found"};
12421 this.metaFromRemote = true;
12422 this.meta = o.metaData;
12423 this.recordType = Roo.data.Record.create(o.metaData.fields);
12424 this.onMetaChange(this.meta, this.recordType, o);
12426 return this.readRecords(o);
12429 // private function a store will implement
12430 onMetaChange : function(meta, recordType, o){
12437 simpleAccess: function(obj, subsc) {
12444 getJsonAccessor: function(){
12446 return function(expr) {
12448 return(re.test(expr))
12449 ? new Function("obj", "return obj." + expr)
12454 return Roo.emptyFn;
12459 * Create a data block containing Roo.data.Records from an XML document.
12460 * @param {Object} o An object which contains an Array of row objects in the property specified
12461 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12462 * which contains the total size of the dataset.
12463 * @return {Object} data A data block which is used by an Roo.data.Store object as
12464 * a cache of Roo.data.Records.
12466 readRecords : function(o){
12468 * After any data loads, the raw JSON data is available for further custom processing.
12472 var s = this.meta, Record = this.recordType,
12473 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12475 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12477 if(s.totalProperty) {
12478 this.getTotal = this.getJsonAccessor(s.totalProperty);
12480 if(s.successProperty) {
12481 this.getSuccess = this.getJsonAccessor(s.successProperty);
12483 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12485 var g = this.getJsonAccessor(s.id);
12486 this.getId = function(rec) {
12488 return (r === undefined || r === "") ? null : r;
12491 this.getId = function(){return null;};
12494 for(var jj = 0; jj < fl; jj++){
12496 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12497 this.ef[jj] = this.getJsonAccessor(map);
12501 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12502 if(s.totalProperty){
12503 var vt = parseInt(this.getTotal(o), 10);
12508 if(s.successProperty){
12509 var vs = this.getSuccess(o);
12510 if(vs === false || vs === 'false'){
12515 for(var i = 0; i < c; i++){
12518 var id = this.getId(n);
12519 for(var j = 0; j < fl; j++){
12521 var v = this.ef[j](n);
12523 Roo.log('missing convert for ' + f.name);
12527 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12529 var record = new Record(values, id);
12531 records[i] = record;
12537 totalRecords : totalRecords
12542 * Ext JS Library 1.1.1
12543 * Copyright(c) 2006-2007, Ext JS, LLC.
12545 * Originally Released Under LGPL - original licence link has changed is not relivant.
12548 * <script type="text/javascript">
12552 * @class Roo.data.ArrayReader
12553 * @extends Roo.data.DataReader
12554 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12555 * Each element of that Array represents a row of data fields. The
12556 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12557 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12561 var RecordDef = Roo.data.Record.create([
12562 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12563 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12565 var myReader = new Roo.data.ArrayReader({
12566 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12570 * This would consume an Array like this:
12572 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12574 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12576 * Create a new JsonReader
12577 * @param {Object} meta Metadata configuration options.
12578 * @param {Object} recordType Either an Array of field definition objects
12579 * as specified to {@link Roo.data.Record#create},
12580 * or an {@link Roo.data.Record} object
12581 * created using {@link Roo.data.Record#create}.
12583 Roo.data.ArrayReader = function(meta, recordType){
12584 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12587 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12589 * Create a data block containing Roo.data.Records from an XML document.
12590 * @param {Object} o An Array of row objects which represents the dataset.
12591 * @return {Object} data A data block which is used by an Roo.data.Store object as
12592 * a cache of Roo.data.Records.
12594 readRecords : function(o){
12595 var sid = this.meta ? this.meta.id : null;
12596 var recordType = this.recordType, fields = recordType.prototype.fields;
12599 for(var i = 0; i < root.length; i++){
12602 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12603 for(var j = 0, jlen = fields.length; j < jlen; j++){
12604 var f = fields.items[j];
12605 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12606 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12608 values[f.name] = v;
12610 var record = new recordType(values, id);
12612 records[records.length] = record;
12616 totalRecords : records.length
12625 * @class Roo.bootstrap.ComboBox
12626 * @extends Roo.bootstrap.TriggerField
12627 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12628 * @cfg {Boolean} append (true|false) default false
12629 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12630 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12631 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12632 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12633 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12634 * @cfg {Boolean} animate default true
12635 * @cfg {Boolean} emptyResultText only for touch device
12636 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12637 * @cfg {String} emptyTitle default ''
12639 * Create a new ComboBox.
12640 * @param {Object} config Configuration options
12642 Roo.bootstrap.ComboBox = function(config){
12643 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12647 * Fires when the dropdown list is expanded
12648 * @param {Roo.bootstrap.ComboBox} combo This combo box
12653 * Fires when the dropdown list is collapsed
12654 * @param {Roo.bootstrap.ComboBox} combo This combo box
12658 * @event beforeselect
12659 * Fires before a list item is selected. Return false to cancel the selection.
12660 * @param {Roo.bootstrap.ComboBox} combo This combo box
12661 * @param {Roo.data.Record} record The data record returned from the underlying store
12662 * @param {Number} index The index of the selected item in the dropdown list
12664 'beforeselect' : true,
12667 * Fires when a list item is selected
12668 * @param {Roo.bootstrap.ComboBox} combo This combo box
12669 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12670 * @param {Number} index The index of the selected item in the dropdown list
12674 * @event beforequery
12675 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12676 * The event object passed has these properties:
12677 * @param {Roo.bootstrap.ComboBox} combo This combo box
12678 * @param {String} query The query
12679 * @param {Boolean} forceAll true to force "all" query
12680 * @param {Boolean} cancel true to cancel the query
12681 * @param {Object} e The query event object
12683 'beforequery': true,
12686 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12687 * @param {Roo.bootstrap.ComboBox} combo This combo box
12692 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12693 * @param {Roo.bootstrap.ComboBox} combo This combo box
12694 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12699 * Fires when the remove value from the combobox array
12700 * @param {Roo.bootstrap.ComboBox} combo This combo box
12704 * @event afterremove
12705 * Fires when the remove value from the combobox array
12706 * @param {Roo.bootstrap.ComboBox} combo This combo box
12708 'afterremove' : true,
12710 * @event specialfilter
12711 * Fires when specialfilter
12712 * @param {Roo.bootstrap.ComboBox} combo This combo box
12714 'specialfilter' : true,
12717 * Fires when tick the element
12718 * @param {Roo.bootstrap.ComboBox} combo This combo box
12722 * @event touchviewdisplay
12723 * Fires when touch view require special display (default is using displayField)
12724 * @param {Roo.bootstrap.ComboBox} combo This combo box
12725 * @param {Object} cfg set html .
12727 'touchviewdisplay' : true
12732 this.tickItems = [];
12734 this.selectedIndex = -1;
12735 if(this.mode == 'local'){
12736 if(config.queryDelay === undefined){
12737 this.queryDelay = 10;
12739 if(config.minChars === undefined){
12745 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12748 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12749 * rendering into an Roo.Editor, defaults to false)
12752 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12753 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12756 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12759 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12760 * the dropdown list (defaults to undefined, with no header element)
12764 * @cfg {String/Roo.Template} tpl The template to use to render the output
12768 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12770 listWidth: undefined,
12772 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12773 * mode = 'remote' or 'text' if mode = 'local')
12775 displayField: undefined,
12778 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12779 * mode = 'remote' or 'value' if mode = 'local').
12780 * Note: use of a valueField requires the user make a selection
12781 * in order for a value to be mapped.
12783 valueField: undefined,
12785 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12790 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12791 * field's data value (defaults to the underlying DOM element's name)
12793 hiddenName: undefined,
12795 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12799 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12801 selectedClass: 'active',
12804 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12808 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12809 * anchor positions (defaults to 'tl-bl')
12811 listAlign: 'tl-bl?',
12813 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12817 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12818 * query specified by the allQuery config option (defaults to 'query')
12820 triggerAction: 'query',
12822 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12823 * (defaults to 4, does not apply if editable = false)
12827 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12828 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12832 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12833 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12837 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12838 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12842 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12843 * when editable = true (defaults to false)
12845 selectOnFocus:false,
12847 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12849 queryParam: 'query',
12851 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12852 * when mode = 'remote' (defaults to 'Loading...')
12854 loadingText: 'Loading...',
12856 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12860 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12864 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12865 * traditional select (defaults to true)
12869 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12873 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12877 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12878 * listWidth has a higher value)
12882 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12883 * allow the user to set arbitrary text into the field (defaults to false)
12885 forceSelection:false,
12887 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12888 * if typeAhead = true (defaults to 250)
12890 typeAheadDelay : 250,
12892 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12893 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12895 valueNotFoundText : undefined,
12897 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12899 blockFocus : false,
12902 * @cfg {Boolean} disableClear Disable showing of clear button.
12904 disableClear : false,
12906 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12908 alwaysQuery : false,
12911 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12916 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12918 invalidClass : "has-warning",
12921 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12923 validClass : "has-success",
12926 * @cfg {Boolean} specialFilter (true|false) special filter default false
12928 specialFilter : false,
12931 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12933 mobileTouchView : true,
12936 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12938 useNativeIOS : false,
12940 ios_options : false,
12952 btnPosition : 'right',
12953 triggerList : true,
12954 showToggleBtn : true,
12956 emptyResultText: 'Empty',
12957 triggerText : 'Select',
12960 // element that contains real text value.. (when hidden is used..)
12962 getAutoCreate : function()
12967 * Render classic select for iso
12970 if(Roo.isIOS && this.useNativeIOS){
12971 cfg = this.getAutoCreateNativeIOS();
12979 if(Roo.isTouch && this.mobileTouchView){
12980 cfg = this.getAutoCreateTouchView();
12987 if(!this.tickable){
12988 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12993 * ComboBox with tickable selections
12996 var align = this.labelAlign || this.parentLabelAlign();
12999 cls : 'form-group roo-combobox-tickable' //input-group
13002 var btn_text_select = '';
13003 var btn_text_done = '';
13004 var btn_text_cancel = '';
13006 if (this.btn_text_show) {
13007 btn_text_select = 'Select';
13008 btn_text_done = 'Done';
13009 btn_text_cancel = 'Cancel';
13014 cls : 'tickable-buttons',
13019 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13020 //html : this.triggerText
13021 html: btn_text_select
13027 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13029 html: btn_text_done
13035 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13037 html: btn_text_cancel
13043 buttons.cn.unshift({
13045 cls: 'roo-select2-search-field-input'
13051 Roo.each(buttons.cn, function(c){
13053 c.cls += ' btn-' + _this.size;
13056 if (_this.disabled) {
13067 cls: 'form-hidden-field'
13071 cls: 'roo-select2-choices',
13075 cls: 'roo-select2-search-field',
13086 cls: 'roo-select2-container input-group roo-select2-container-multi',
13091 // cls: 'typeahead typeahead-long dropdown-menu',
13092 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13097 if(this.hasFeedback && !this.allowBlank){
13101 cls: 'glyphicon form-control-feedback'
13104 combobox.cn.push(feedback);
13108 if (align ==='left' && this.fieldLabel.length) {
13110 cfg.cls += ' roo-form-group-label-left';
13115 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13116 tooltip : 'This field is required'
13121 cls : 'control-label',
13122 html : this.fieldLabel
13134 var labelCfg = cfg.cn[1];
13135 var contentCfg = cfg.cn[2];
13138 if(this.indicatorpos == 'right'){
13144 cls : 'control-label',
13148 html : this.fieldLabel
13152 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13153 tooltip : 'This field is required'
13168 labelCfg = cfg.cn[0];
13169 contentCfg = cfg.cn[1];
13173 if(this.labelWidth > 12){
13174 labelCfg.style = "width: " + this.labelWidth + 'px';
13177 if(this.labelWidth < 13 && this.labelmd == 0){
13178 this.labelmd = this.labelWidth;
13181 if(this.labellg > 0){
13182 labelCfg.cls += ' col-lg-' + this.labellg;
13183 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13186 if(this.labelmd > 0){
13187 labelCfg.cls += ' col-md-' + this.labelmd;
13188 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13191 if(this.labelsm > 0){
13192 labelCfg.cls += ' col-sm-' + this.labelsm;
13193 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13196 if(this.labelxs > 0){
13197 labelCfg.cls += ' col-xs-' + this.labelxs;
13198 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13202 } else if ( this.fieldLabel.length) {
13203 // Roo.log(" label");
13207 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13208 tooltip : 'This field is required'
13212 //cls : 'input-group-addon',
13213 html : this.fieldLabel
13218 if(this.indicatorpos == 'right'){
13222 //cls : 'input-group-addon',
13223 html : this.fieldLabel
13227 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13228 tooltip : 'This field is required'
13237 // Roo.log(" no label && no align");
13244 ['xs','sm','md','lg'].map(function(size){
13245 if (settings[size]) {
13246 cfg.cls += ' col-' + size + '-' + settings[size];
13254 _initEventsCalled : false,
13257 initEvents: function()
13259 if (this._initEventsCalled) { // as we call render... prevent looping...
13262 this._initEventsCalled = true;
13265 throw "can not find store for combo";
13268 this.indicator = this.indicatorEl();
13270 this.store = Roo.factory(this.store, Roo.data);
13271 this.store.parent = this;
13273 // if we are building from html. then this element is so complex, that we can not really
13274 // use the rendered HTML.
13275 // so we have to trash and replace the previous code.
13276 if (Roo.XComponent.build_from_html) {
13277 // remove this element....
13278 var e = this.el.dom, k=0;
13279 while (e ) { e = e.previousSibling; ++k;}
13284 this.rendered = false;
13286 this.render(this.parent().getChildContainer(true), k);
13289 if(Roo.isIOS && this.useNativeIOS){
13290 this.initIOSView();
13298 if(Roo.isTouch && this.mobileTouchView){
13299 this.initTouchView();
13304 this.initTickableEvents();
13308 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13310 if(this.hiddenName){
13312 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13314 this.hiddenField.dom.value =
13315 this.hiddenValue !== undefined ? this.hiddenValue :
13316 this.value !== undefined ? this.value : '';
13318 // prevent input submission
13319 this.el.dom.removeAttribute('name');
13320 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13325 // this.el.dom.setAttribute('autocomplete', 'off');
13328 var cls = 'x-combo-list';
13330 //this.list = new Roo.Layer({
13331 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13337 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13338 _this.list.setWidth(lw);
13341 this.list.on('mouseover', this.onViewOver, this);
13342 this.list.on('mousemove', this.onViewMove, this);
13343 this.list.on('scroll', this.onViewScroll, this);
13346 this.list.swallowEvent('mousewheel');
13347 this.assetHeight = 0;
13350 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13351 this.assetHeight += this.header.getHeight();
13354 this.innerList = this.list.createChild({cls:cls+'-inner'});
13355 this.innerList.on('mouseover', this.onViewOver, this);
13356 this.innerList.on('mousemove', this.onViewMove, this);
13357 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13359 if(this.allowBlank && !this.pageSize && !this.disableClear){
13360 this.footer = this.list.createChild({cls:cls+'-ft'});
13361 this.pageTb = new Roo.Toolbar(this.footer);
13365 this.footer = this.list.createChild({cls:cls+'-ft'});
13366 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13367 {pageSize: this.pageSize});
13371 if (this.pageTb && this.allowBlank && !this.disableClear) {
13373 this.pageTb.add(new Roo.Toolbar.Fill(), {
13374 cls: 'x-btn-icon x-btn-clear',
13376 handler: function()
13379 _this.clearValue();
13380 _this.onSelect(false, -1);
13385 this.assetHeight += this.footer.getHeight();
13390 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13393 this.view = new Roo.View(this.list, this.tpl, {
13394 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13396 //this.view.wrapEl.setDisplayed(false);
13397 this.view.on('click', this.onViewClick, this);
13400 this.store.on('beforeload', this.onBeforeLoad, this);
13401 this.store.on('load', this.onLoad, this);
13402 this.store.on('loadexception', this.onLoadException, this);
13404 if(this.resizable){
13405 this.resizer = new Roo.Resizable(this.list, {
13406 pinned:true, handles:'se'
13408 this.resizer.on('resize', function(r, w, h){
13409 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13410 this.listWidth = w;
13411 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13412 this.restrictHeight();
13414 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13417 if(!this.editable){
13418 this.editable = true;
13419 this.setEditable(false);
13424 if (typeof(this.events.add.listeners) != 'undefined') {
13426 this.addicon = this.wrap.createChild(
13427 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13429 this.addicon.on('click', function(e) {
13430 this.fireEvent('add', this);
13433 if (typeof(this.events.edit.listeners) != 'undefined') {
13435 this.editicon = this.wrap.createChild(
13436 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13437 if (this.addicon) {
13438 this.editicon.setStyle('margin-left', '40px');
13440 this.editicon.on('click', function(e) {
13442 // we fire even if inothing is selected..
13443 this.fireEvent('edit', this, this.lastData );
13449 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13450 "up" : function(e){
13451 this.inKeyMode = true;
13455 "down" : function(e){
13456 if(!this.isExpanded()){
13457 this.onTriggerClick();
13459 this.inKeyMode = true;
13464 "enter" : function(e){
13465 // this.onViewClick();
13469 if(this.fireEvent("specialkey", this, e)){
13470 this.onViewClick(false);
13476 "esc" : function(e){
13480 "tab" : function(e){
13483 if(this.fireEvent("specialkey", this, e)){
13484 this.onViewClick(false);
13492 doRelay : function(foo, bar, hname){
13493 if(hname == 'down' || this.scope.isExpanded()){
13494 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13503 this.queryDelay = Math.max(this.queryDelay || 10,
13504 this.mode == 'local' ? 10 : 250);
13507 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13509 if(this.typeAhead){
13510 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13512 if(this.editable !== false){
13513 this.inputEl().on("keyup", this.onKeyUp, this);
13515 if(this.forceSelection){
13516 this.inputEl().on('blur', this.doForce, this);
13520 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13521 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13525 initTickableEvents: function()
13529 if(this.hiddenName){
13531 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13533 this.hiddenField.dom.value =
13534 this.hiddenValue !== undefined ? this.hiddenValue :
13535 this.value !== undefined ? this.value : '';
13537 // prevent input submission
13538 this.el.dom.removeAttribute('name');
13539 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13544 // this.list = this.el.select('ul.dropdown-menu',true).first();
13546 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13547 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13548 if(this.triggerList){
13549 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13552 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13553 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13555 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13556 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13558 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13559 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13561 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13562 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13563 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13566 this.cancelBtn.hide();
13571 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13572 _this.list.setWidth(lw);
13575 this.list.on('mouseover', this.onViewOver, this);
13576 this.list.on('mousemove', this.onViewMove, this);
13578 this.list.on('scroll', this.onViewScroll, this);
13581 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13584 this.view = new Roo.View(this.list, this.tpl, {
13585 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13588 //this.view.wrapEl.setDisplayed(false);
13589 this.view.on('click', this.onViewClick, this);
13593 this.store.on('beforeload', this.onBeforeLoad, this);
13594 this.store.on('load', this.onLoad, this);
13595 this.store.on('loadexception', this.onLoadException, this);
13598 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13599 "up" : function(e){
13600 this.inKeyMode = true;
13604 "down" : function(e){
13605 this.inKeyMode = true;
13609 "enter" : function(e){
13610 if(this.fireEvent("specialkey", this, e)){
13611 this.onViewClick(false);
13617 "esc" : function(e){
13618 this.onTickableFooterButtonClick(e, false, false);
13621 "tab" : function(e){
13622 this.fireEvent("specialkey", this, e);
13624 this.onTickableFooterButtonClick(e, false, false);
13631 doRelay : function(e, fn, key){
13632 if(this.scope.isExpanded()){
13633 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13642 this.queryDelay = Math.max(this.queryDelay || 10,
13643 this.mode == 'local' ? 10 : 250);
13646 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13648 if(this.typeAhead){
13649 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13652 if(this.editable !== false){
13653 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13656 this.indicator = this.indicatorEl();
13658 if(this.indicator){
13659 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13660 this.indicator.hide();
13665 onDestroy : function(){
13667 this.view.setStore(null);
13668 this.view.el.removeAllListeners();
13669 this.view.el.remove();
13670 this.view.purgeListeners();
13673 this.list.dom.innerHTML = '';
13677 this.store.un('beforeload', this.onBeforeLoad, this);
13678 this.store.un('load', this.onLoad, this);
13679 this.store.un('loadexception', this.onLoadException, this);
13681 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13685 fireKey : function(e){
13686 if(e.isNavKeyPress() && !this.list.isVisible()){
13687 this.fireEvent("specialkey", this, e);
13692 onResize: function(w, h){
13693 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13695 // if(typeof w != 'number'){
13696 // // we do not handle it!?!?
13699 // var tw = this.trigger.getWidth();
13700 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13701 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13703 // this.inputEl().setWidth( this.adjustWidth('input', x));
13705 // //this.trigger.setStyle('left', x+'px');
13707 // if(this.list && this.listWidth === undefined){
13708 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13709 // this.list.setWidth(lw);
13710 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13718 * Allow or prevent the user from directly editing the field text. If false is passed,
13719 * the user will only be able to select from the items defined in the dropdown list. This method
13720 * is the runtime equivalent of setting the 'editable' config option at config time.
13721 * @param {Boolean} value True to allow the user to directly edit the field text
13723 setEditable : function(value){
13724 if(value == this.editable){
13727 this.editable = value;
13729 this.inputEl().dom.setAttribute('readOnly', true);
13730 this.inputEl().on('mousedown', this.onTriggerClick, this);
13731 this.inputEl().addClass('x-combo-noedit');
13733 this.inputEl().dom.setAttribute('readOnly', false);
13734 this.inputEl().un('mousedown', this.onTriggerClick, this);
13735 this.inputEl().removeClass('x-combo-noedit');
13741 onBeforeLoad : function(combo,opts){
13742 if(!this.hasFocus){
13746 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13748 this.restrictHeight();
13749 this.selectedIndex = -1;
13753 onLoad : function(){
13755 this.hasQuery = false;
13757 if(!this.hasFocus){
13761 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13762 this.loading.hide();
13765 if(this.store.getCount() > 0){
13768 this.restrictHeight();
13769 if(this.lastQuery == this.allQuery){
13770 if(this.editable && !this.tickable){
13771 this.inputEl().dom.select();
13775 !this.selectByValue(this.value, true) &&
13778 !this.store.lastOptions ||
13779 typeof(this.store.lastOptions.add) == 'undefined' ||
13780 this.store.lastOptions.add != true
13783 this.select(0, true);
13786 if(this.autoFocus){
13789 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13790 this.taTask.delay(this.typeAheadDelay);
13794 this.onEmptyResults();
13800 onLoadException : function()
13802 this.hasQuery = false;
13804 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13805 this.loading.hide();
13808 if(this.tickable && this.editable){
13813 // only causes errors at present
13814 //Roo.log(this.store.reader.jsonData);
13815 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13817 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13823 onTypeAhead : function(){
13824 if(this.store.getCount() > 0){
13825 var r = this.store.getAt(0);
13826 var newValue = r.data[this.displayField];
13827 var len = newValue.length;
13828 var selStart = this.getRawValue().length;
13830 if(selStart != len){
13831 this.setRawValue(newValue);
13832 this.selectText(selStart, newValue.length);
13838 onSelect : function(record, index){
13840 if(this.fireEvent('beforeselect', this, record, index) !== false){
13842 this.setFromData(index > -1 ? record.data : false);
13845 this.fireEvent('select', this, record, index);
13850 * Returns the currently selected field value or empty string if no value is set.
13851 * @return {String} value The selected value
13853 getValue : function()
13855 if(Roo.isIOS && this.useNativeIOS){
13856 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13860 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13863 if(this.valueField){
13864 return typeof this.value != 'undefined' ? this.value : '';
13866 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13870 getRawValue : function()
13872 if(Roo.isIOS && this.useNativeIOS){
13873 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13876 var v = this.inputEl().getValue();
13882 * Clears any text/value currently set in the field
13884 clearValue : function(){
13886 if(this.hiddenField){
13887 this.hiddenField.dom.value = '';
13890 this.setRawValue('');
13891 this.lastSelectionText = '';
13892 this.lastData = false;
13894 var close = this.closeTriggerEl();
13905 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13906 * will be displayed in the field. If the value does not match the data value of an existing item,
13907 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13908 * Otherwise the field will be blank (although the value will still be set).
13909 * @param {String} value The value to match
13911 setValue : function(v)
13913 if(Roo.isIOS && this.useNativeIOS){
13914 this.setIOSValue(v);
13924 if(this.valueField){
13925 var r = this.findRecord(this.valueField, v);
13927 text = r.data[this.displayField];
13928 }else if(this.valueNotFoundText !== undefined){
13929 text = this.valueNotFoundText;
13932 this.lastSelectionText = text;
13933 if(this.hiddenField){
13934 this.hiddenField.dom.value = v;
13936 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13939 var close = this.closeTriggerEl();
13942 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13948 * @property {Object} the last set data for the element
13953 * Sets the value of the field based on a object which is related to the record format for the store.
13954 * @param {Object} value the value to set as. or false on reset?
13956 setFromData : function(o){
13963 var dv = ''; // display value
13964 var vv = ''; // value value..
13966 if (this.displayField) {
13967 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13969 // this is an error condition!!!
13970 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13973 if(this.valueField){
13974 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13977 var close = this.closeTriggerEl();
13980 if(dv.length || vv * 1 > 0){
13982 this.blockFocus=true;
13988 if(this.hiddenField){
13989 this.hiddenField.dom.value = vv;
13991 this.lastSelectionText = dv;
13992 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13996 // no hidden field.. - we store the value in 'value', but still display
13997 // display field!!!!
13998 this.lastSelectionText = dv;
13999 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14006 reset : function(){
14007 // overridden so that last data is reset..
14014 this.setValue(this.originalValue);
14015 //this.clearInvalid();
14016 this.lastData = false;
14018 this.view.clearSelections();
14024 findRecord : function(prop, value){
14026 if(this.store.getCount() > 0){
14027 this.store.each(function(r){
14028 if(r.data[prop] == value){
14038 getName: function()
14040 // returns hidden if it's set..
14041 if (!this.rendered) {return ''};
14042 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14046 onViewMove : function(e, t){
14047 this.inKeyMode = false;
14051 onViewOver : function(e, t){
14052 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14055 var item = this.view.findItemFromChild(t);
14058 var index = this.view.indexOf(item);
14059 this.select(index, false);
14064 onViewClick : function(view, doFocus, el, e)
14066 var index = this.view.getSelectedIndexes()[0];
14068 var r = this.store.getAt(index);
14072 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14079 Roo.each(this.tickItems, function(v,k){
14081 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14083 _this.tickItems.splice(k, 1);
14085 if(typeof(e) == 'undefined' && view == false){
14086 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14098 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14099 this.tickItems.push(r.data);
14102 if(typeof(e) == 'undefined' && view == false){
14103 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14110 this.onSelect(r, index);
14112 if(doFocus !== false && !this.blockFocus){
14113 this.inputEl().focus();
14118 restrictHeight : function(){
14119 //this.innerList.dom.style.height = '';
14120 //var inner = this.innerList.dom;
14121 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14122 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14123 //this.list.beginUpdate();
14124 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14125 this.list.alignTo(this.inputEl(), this.listAlign);
14126 this.list.alignTo(this.inputEl(), this.listAlign);
14127 //this.list.endUpdate();
14131 onEmptyResults : function(){
14133 if(this.tickable && this.editable){
14134 this.hasFocus = false;
14135 this.restrictHeight();
14143 * Returns true if the dropdown list is expanded, else false.
14145 isExpanded : function(){
14146 return this.list.isVisible();
14150 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14151 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14152 * @param {String} value The data value of the item to select
14153 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14154 * selected item if it is not currently in view (defaults to true)
14155 * @return {Boolean} True if the value matched an item in the list, else false
14157 selectByValue : function(v, scrollIntoView){
14158 if(v !== undefined && v !== null){
14159 var r = this.findRecord(this.valueField || this.displayField, v);
14161 this.select(this.store.indexOf(r), scrollIntoView);
14169 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14170 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14171 * @param {Number} index The zero-based index of the list item to select
14172 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14173 * selected item if it is not currently in view (defaults to true)
14175 select : function(index, scrollIntoView){
14176 this.selectedIndex = index;
14177 this.view.select(index);
14178 if(scrollIntoView !== false){
14179 var el = this.view.getNode(index);
14181 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14184 this.list.scrollChildIntoView(el, false);
14190 selectNext : function(){
14191 var ct = this.store.getCount();
14193 if(this.selectedIndex == -1){
14195 }else if(this.selectedIndex < ct-1){
14196 this.select(this.selectedIndex+1);
14202 selectPrev : function(){
14203 var ct = this.store.getCount();
14205 if(this.selectedIndex == -1){
14207 }else if(this.selectedIndex != 0){
14208 this.select(this.selectedIndex-1);
14214 onKeyUp : function(e){
14215 if(this.editable !== false && !e.isSpecialKey()){
14216 this.lastKey = e.getKey();
14217 this.dqTask.delay(this.queryDelay);
14222 validateBlur : function(){
14223 return !this.list || !this.list.isVisible();
14227 initQuery : function(){
14229 var v = this.getRawValue();
14231 if(this.tickable && this.editable){
14232 v = this.tickableInputEl().getValue();
14239 doForce : function(){
14240 if(this.inputEl().dom.value.length > 0){
14241 this.inputEl().dom.value =
14242 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14248 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14249 * query allowing the query action to be canceled if needed.
14250 * @param {String} query The SQL query to execute
14251 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14252 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14253 * saved in the current store (defaults to false)
14255 doQuery : function(q, forceAll){
14257 if(q === undefined || q === null){
14262 forceAll: forceAll,
14266 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14271 forceAll = qe.forceAll;
14272 if(forceAll === true || (q.length >= this.minChars)){
14274 this.hasQuery = true;
14276 if(this.lastQuery != q || this.alwaysQuery){
14277 this.lastQuery = q;
14278 if(this.mode == 'local'){
14279 this.selectedIndex = -1;
14281 this.store.clearFilter();
14284 if(this.specialFilter){
14285 this.fireEvent('specialfilter', this);
14290 this.store.filter(this.displayField, q);
14293 this.store.fireEvent("datachanged", this.store);
14300 this.store.baseParams[this.queryParam] = q;
14302 var options = {params : this.getParams(q)};
14305 options.add = true;
14306 options.params.start = this.page * this.pageSize;
14309 this.store.load(options);
14312 * this code will make the page width larger, at the beginning, the list not align correctly,
14313 * we should expand the list on onLoad
14314 * so command out it
14319 this.selectedIndex = -1;
14324 this.loadNext = false;
14328 getParams : function(q){
14330 //p[this.queryParam] = q;
14334 p.limit = this.pageSize;
14340 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14342 collapse : function(){
14343 if(!this.isExpanded()){
14349 this.hasFocus = false;
14353 this.cancelBtn.hide();
14354 this.trigger.show();
14357 this.tickableInputEl().dom.value = '';
14358 this.tickableInputEl().blur();
14363 Roo.get(document).un('mousedown', this.collapseIf, this);
14364 Roo.get(document).un('mousewheel', this.collapseIf, this);
14365 if (!this.editable) {
14366 Roo.get(document).un('keydown', this.listKeyPress, this);
14368 this.fireEvent('collapse', this);
14374 collapseIf : function(e){
14375 var in_combo = e.within(this.el);
14376 var in_list = e.within(this.list);
14377 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14379 if (in_combo || in_list || is_list) {
14380 //e.stopPropagation();
14385 this.onTickableFooterButtonClick(e, false, false);
14393 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14395 expand : function(){
14397 if(this.isExpanded() || !this.hasFocus){
14401 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14402 this.list.setWidth(lw);
14408 this.restrictHeight();
14412 this.tickItems = Roo.apply([], this.item);
14415 this.cancelBtn.show();
14416 this.trigger.hide();
14419 this.tickableInputEl().focus();
14424 Roo.get(document).on('mousedown', this.collapseIf, this);
14425 Roo.get(document).on('mousewheel', this.collapseIf, this);
14426 if (!this.editable) {
14427 Roo.get(document).on('keydown', this.listKeyPress, this);
14430 this.fireEvent('expand', this);
14434 // Implements the default empty TriggerField.onTriggerClick function
14435 onTriggerClick : function(e)
14437 Roo.log('trigger click');
14439 if(this.disabled || !this.triggerList){
14444 this.loadNext = false;
14446 if(this.isExpanded()){
14448 if (!this.blockFocus) {
14449 this.inputEl().focus();
14453 this.hasFocus = true;
14454 if(this.triggerAction == 'all') {
14455 this.doQuery(this.allQuery, true);
14457 this.doQuery(this.getRawValue());
14459 if (!this.blockFocus) {
14460 this.inputEl().focus();
14465 onTickableTriggerClick : function(e)
14472 this.loadNext = false;
14473 this.hasFocus = true;
14475 if(this.triggerAction == 'all') {
14476 this.doQuery(this.allQuery, true);
14478 this.doQuery(this.getRawValue());
14482 onSearchFieldClick : function(e)
14484 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14485 this.onTickableFooterButtonClick(e, false, false);
14489 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14494 this.loadNext = false;
14495 this.hasFocus = true;
14497 if(this.triggerAction == 'all') {
14498 this.doQuery(this.allQuery, true);
14500 this.doQuery(this.getRawValue());
14504 listKeyPress : function(e)
14506 //Roo.log('listkeypress');
14507 // scroll to first matching element based on key pres..
14508 if (e.isSpecialKey()) {
14511 var k = String.fromCharCode(e.getKey()).toUpperCase();
14514 var csel = this.view.getSelectedNodes();
14515 var cselitem = false;
14517 var ix = this.view.indexOf(csel[0]);
14518 cselitem = this.store.getAt(ix);
14519 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14525 this.store.each(function(v) {
14527 // start at existing selection.
14528 if (cselitem.id == v.id) {
14534 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14535 match = this.store.indexOf(v);
14541 if (match === false) {
14542 return true; // no more action?
14545 this.view.select(match);
14546 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14547 sn.scrollIntoView(sn.dom.parentNode, false);
14550 onViewScroll : function(e, t){
14552 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){
14556 this.hasQuery = true;
14558 this.loading = this.list.select('.loading', true).first();
14560 if(this.loading === null){
14561 this.list.createChild({
14563 cls: 'loading roo-select2-more-results roo-select2-active',
14564 html: 'Loading more results...'
14567 this.loading = this.list.select('.loading', true).first();
14569 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14571 this.loading.hide();
14574 this.loading.show();
14579 this.loadNext = true;
14581 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14586 addItem : function(o)
14588 var dv = ''; // display value
14590 if (this.displayField) {
14591 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14593 // this is an error condition!!!
14594 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14601 var choice = this.choices.createChild({
14603 cls: 'roo-select2-search-choice',
14612 cls: 'roo-select2-search-choice-close fa fa-times',
14617 }, this.searchField);
14619 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14621 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14629 this.inputEl().dom.value = '';
14634 onRemoveItem : function(e, _self, o)
14636 e.preventDefault();
14638 this.lastItem = Roo.apply([], this.item);
14640 var index = this.item.indexOf(o.data) * 1;
14643 Roo.log('not this item?!');
14647 this.item.splice(index, 1);
14652 this.fireEvent('remove', this, e);
14658 syncValue : function()
14660 if(!this.item.length){
14667 Roo.each(this.item, function(i){
14668 if(_this.valueField){
14669 value.push(i[_this.valueField]);
14676 this.value = value.join(',');
14678 if(this.hiddenField){
14679 this.hiddenField.dom.value = this.value;
14682 this.store.fireEvent("datachanged", this.store);
14687 clearItem : function()
14689 if(!this.multiple){
14695 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14703 if(this.tickable && !Roo.isTouch){
14704 this.view.refresh();
14708 inputEl: function ()
14710 if(Roo.isIOS && this.useNativeIOS){
14711 return this.el.select('select.roo-ios-select', true).first();
14714 if(Roo.isTouch && this.mobileTouchView){
14715 return this.el.select('input.form-control',true).first();
14719 return this.searchField;
14722 return this.el.select('input.form-control',true).first();
14725 onTickableFooterButtonClick : function(e, btn, el)
14727 e.preventDefault();
14729 this.lastItem = Roo.apply([], this.item);
14731 if(btn && btn.name == 'cancel'){
14732 this.tickItems = Roo.apply([], this.item);
14741 Roo.each(this.tickItems, function(o){
14749 validate : function()
14751 if(this.getVisibilityEl().hasClass('hidden')){
14755 var v = this.getRawValue();
14758 v = this.getValue();
14761 if(this.disabled || this.allowBlank || v.length){
14766 this.markInvalid();
14770 tickableInputEl : function()
14772 if(!this.tickable || !this.editable){
14773 return this.inputEl();
14776 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14780 getAutoCreateTouchView : function()
14785 cls: 'form-group' //input-group
14791 type : this.inputType,
14792 cls : 'form-control x-combo-noedit',
14793 autocomplete: 'new-password',
14794 placeholder : this.placeholder || '',
14799 input.name = this.name;
14803 input.cls += ' input-' + this.size;
14806 if (this.disabled) {
14807 input.disabled = true;
14818 inputblock.cls += ' input-group';
14820 inputblock.cn.unshift({
14822 cls : 'input-group-addon',
14827 if(this.removable && !this.multiple){
14828 inputblock.cls += ' roo-removable';
14830 inputblock.cn.push({
14833 cls : 'roo-combo-removable-btn close'
14837 if(this.hasFeedback && !this.allowBlank){
14839 inputblock.cls += ' has-feedback';
14841 inputblock.cn.push({
14843 cls: 'glyphicon form-control-feedback'
14850 inputblock.cls += (this.before) ? '' : ' input-group';
14852 inputblock.cn.push({
14854 cls : 'input-group-addon',
14865 cls: 'form-hidden-field'
14879 cls: 'form-hidden-field'
14883 cls: 'roo-select2-choices',
14887 cls: 'roo-select2-search-field',
14900 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14906 if(!this.multiple && this.showToggleBtn){
14913 if (this.caret != false) {
14916 cls: 'fa fa-' + this.caret
14923 cls : 'input-group-addon btn dropdown-toggle',
14928 cls: 'combobox-clear',
14942 combobox.cls += ' roo-select2-container-multi';
14945 var align = this.labelAlign || this.parentLabelAlign();
14947 if (align ==='left' && this.fieldLabel.length) {
14952 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14953 tooltip : 'This field is required'
14957 cls : 'control-label',
14958 html : this.fieldLabel
14969 var labelCfg = cfg.cn[1];
14970 var contentCfg = cfg.cn[2];
14973 if(this.indicatorpos == 'right'){
14978 cls : 'control-label',
14982 html : this.fieldLabel
14986 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14987 tooltip : 'This field is required'
15000 labelCfg = cfg.cn[0];
15001 contentCfg = cfg.cn[1];
15006 if(this.labelWidth > 12){
15007 labelCfg.style = "width: " + this.labelWidth + 'px';
15010 if(this.labelWidth < 13 && this.labelmd == 0){
15011 this.labelmd = this.labelWidth;
15014 if(this.labellg > 0){
15015 labelCfg.cls += ' col-lg-' + this.labellg;
15016 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15019 if(this.labelmd > 0){
15020 labelCfg.cls += ' col-md-' + this.labelmd;
15021 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15024 if(this.labelsm > 0){
15025 labelCfg.cls += ' col-sm-' + this.labelsm;
15026 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15029 if(this.labelxs > 0){
15030 labelCfg.cls += ' col-xs-' + this.labelxs;
15031 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15035 } else if ( this.fieldLabel.length) {
15039 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15040 tooltip : 'This field is required'
15044 cls : 'control-label',
15045 html : this.fieldLabel
15056 if(this.indicatorpos == 'right'){
15060 cls : 'control-label',
15061 html : this.fieldLabel,
15065 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15066 tooltip : 'This field is required'
15083 var settings = this;
15085 ['xs','sm','md','lg'].map(function(size){
15086 if (settings[size]) {
15087 cfg.cls += ' col-' + size + '-' + settings[size];
15094 initTouchView : function()
15096 this.renderTouchView();
15098 this.touchViewEl.on('scroll', function(){
15099 this.el.dom.scrollTop = 0;
15102 this.originalValue = this.getValue();
15104 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15106 this.inputEl().on("click", this.showTouchView, this);
15107 if (this.triggerEl) {
15108 this.triggerEl.on("click", this.showTouchView, this);
15112 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15113 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15115 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15117 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15118 this.store.on('load', this.onTouchViewLoad, this);
15119 this.store.on('loadexception', this.onTouchViewLoadException, this);
15121 if(this.hiddenName){
15123 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15125 this.hiddenField.dom.value =
15126 this.hiddenValue !== undefined ? this.hiddenValue :
15127 this.value !== undefined ? this.value : '';
15129 this.el.dom.removeAttribute('name');
15130 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15134 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15135 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15138 if(this.removable && !this.multiple){
15139 var close = this.closeTriggerEl();
15141 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15142 close.on('click', this.removeBtnClick, this, close);
15146 * fix the bug in Safari iOS8
15148 this.inputEl().on("focus", function(e){
15149 document.activeElement.blur();
15157 renderTouchView : function()
15159 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15160 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15162 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15163 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15165 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15166 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15167 this.touchViewBodyEl.setStyle('overflow', 'auto');
15169 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15170 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15172 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15173 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15177 showTouchView : function()
15183 this.touchViewHeaderEl.hide();
15185 if(this.modalTitle.length){
15186 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15187 this.touchViewHeaderEl.show();
15190 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15191 this.touchViewEl.show();
15193 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15195 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15196 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15198 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15200 if(this.modalTitle.length){
15201 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15204 this.touchViewBodyEl.setHeight(bodyHeight);
15208 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15210 this.touchViewEl.addClass('in');
15213 this.doTouchViewQuery();
15217 hideTouchView : function()
15219 this.touchViewEl.removeClass('in');
15223 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15225 this.touchViewEl.setStyle('display', 'none');
15230 setTouchViewValue : function()
15237 Roo.each(this.tickItems, function(o){
15242 this.hideTouchView();
15245 doTouchViewQuery : function()
15254 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15258 if(!this.alwaysQuery || this.mode == 'local'){
15259 this.onTouchViewLoad();
15266 onTouchViewBeforeLoad : function(combo,opts)
15272 onTouchViewLoad : function()
15274 if(this.store.getCount() < 1){
15275 this.onTouchViewEmptyResults();
15279 this.clearTouchView();
15281 var rawValue = this.getRawValue();
15283 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15285 this.tickItems = [];
15287 this.store.data.each(function(d, rowIndex){
15288 var row = this.touchViewListGroup.createChild(template);
15290 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15291 row.addClass(d.data.cls);
15294 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15297 html : d.data[this.displayField]
15300 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15301 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15304 row.removeClass('selected');
15305 if(!this.multiple && this.valueField &&
15306 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15309 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15310 row.addClass('selected');
15313 if(this.multiple && this.valueField &&
15314 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15318 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15319 this.tickItems.push(d.data);
15322 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15326 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15328 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15330 if(this.modalTitle.length){
15331 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15334 var listHeight = this.touchViewListGroup.getHeight();
15338 if(firstChecked && listHeight > bodyHeight){
15339 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15344 onTouchViewLoadException : function()
15346 this.hideTouchView();
15349 onTouchViewEmptyResults : function()
15351 this.clearTouchView();
15353 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15355 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15359 clearTouchView : function()
15361 this.touchViewListGroup.dom.innerHTML = '';
15364 onTouchViewClick : function(e, el, o)
15366 e.preventDefault();
15369 var rowIndex = o.rowIndex;
15371 var r = this.store.getAt(rowIndex);
15373 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15375 if(!this.multiple){
15376 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15377 c.dom.removeAttribute('checked');
15380 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15382 this.setFromData(r.data);
15384 var close = this.closeTriggerEl();
15390 this.hideTouchView();
15392 this.fireEvent('select', this, r, rowIndex);
15397 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15398 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15399 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15403 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15404 this.addItem(r.data);
15405 this.tickItems.push(r.data);
15409 getAutoCreateNativeIOS : function()
15412 cls: 'form-group' //input-group,
15417 cls : 'roo-ios-select'
15421 combobox.name = this.name;
15424 if (this.disabled) {
15425 combobox.disabled = true;
15428 var settings = this;
15430 ['xs','sm','md','lg'].map(function(size){
15431 if (settings[size]) {
15432 cfg.cls += ' col-' + size + '-' + settings[size];
15442 initIOSView : function()
15444 this.store.on('load', this.onIOSViewLoad, this);
15449 onIOSViewLoad : function()
15451 if(this.store.getCount() < 1){
15455 this.clearIOSView();
15457 if(this.allowBlank) {
15459 var default_text = '-- SELECT --';
15461 if(this.placeholder.length){
15462 default_text = this.placeholder;
15465 if(this.emptyTitle.length){
15466 default_text += ' - ' + this.emptyTitle + ' -';
15469 var opt = this.inputEl().createChild({
15472 html : default_text
15476 o[this.valueField] = 0;
15477 o[this.displayField] = default_text;
15479 this.ios_options.push({
15486 this.store.data.each(function(d, rowIndex){
15490 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15491 html = d.data[this.displayField];
15496 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15497 value = d.data[this.valueField];
15506 if(this.value == d.data[this.valueField]){
15507 option['selected'] = true;
15510 var opt = this.inputEl().createChild(option);
15512 this.ios_options.push({
15519 this.inputEl().on('change', function(){
15520 this.fireEvent('select', this);
15525 clearIOSView: function()
15527 this.inputEl().dom.innerHTML = '';
15529 this.ios_options = [];
15532 setIOSValue: function(v)
15536 if(!this.ios_options){
15540 Roo.each(this.ios_options, function(opts){
15542 opts.el.dom.removeAttribute('selected');
15544 if(opts.data[this.valueField] != v){
15548 opts.el.dom.setAttribute('selected', true);
15554 * @cfg {Boolean} grow
15558 * @cfg {Number} growMin
15562 * @cfg {Number} growMax
15571 Roo.apply(Roo.bootstrap.ComboBox, {
15575 cls: 'modal-header',
15597 cls: 'list-group-item',
15601 cls: 'roo-combobox-list-group-item-value'
15605 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15619 listItemCheckbox : {
15621 cls: 'list-group-item',
15625 cls: 'roo-combobox-list-group-item-value'
15629 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15645 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15650 cls: 'modal-footer',
15658 cls: 'col-xs-6 text-left',
15661 cls: 'btn btn-danger roo-touch-view-cancel',
15667 cls: 'col-xs-6 text-right',
15670 cls: 'btn btn-success roo-touch-view-ok',
15681 Roo.apply(Roo.bootstrap.ComboBox, {
15683 touchViewTemplate : {
15685 cls: 'modal fade roo-combobox-touch-view',
15689 cls: 'modal-dialog',
15690 style : 'position:fixed', // we have to fix position....
15694 cls: 'modal-content',
15696 Roo.bootstrap.ComboBox.header,
15697 Roo.bootstrap.ComboBox.body,
15698 Roo.bootstrap.ComboBox.footer
15707 * Ext JS Library 1.1.1
15708 * Copyright(c) 2006-2007, Ext JS, LLC.
15710 * Originally Released Under LGPL - original licence link has changed is not relivant.
15713 * <script type="text/javascript">
15718 * @extends Roo.util.Observable
15719 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15720 * This class also supports single and multi selection modes. <br>
15721 * Create a data model bound view:
15723 var store = new Roo.data.Store(...);
15725 var view = new Roo.View({
15727 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15729 singleSelect: true,
15730 selectedClass: "ydataview-selected",
15734 // listen for node click?
15735 view.on("click", function(vw, index, node, e){
15736 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15740 dataModel.load("foobar.xml");
15742 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15744 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15745 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15747 * Note: old style constructor is still suported (container, template, config)
15750 * Create a new View
15751 * @param {Object} config The config object
15754 Roo.View = function(config, depreciated_tpl, depreciated_config){
15756 this.parent = false;
15758 if (typeof(depreciated_tpl) == 'undefined') {
15759 // new way.. - universal constructor.
15760 Roo.apply(this, config);
15761 this.el = Roo.get(this.el);
15764 this.el = Roo.get(config);
15765 this.tpl = depreciated_tpl;
15766 Roo.apply(this, depreciated_config);
15768 this.wrapEl = this.el.wrap().wrap();
15769 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15772 if(typeof(this.tpl) == "string"){
15773 this.tpl = new Roo.Template(this.tpl);
15775 // support xtype ctors..
15776 this.tpl = new Roo.factory(this.tpl, Roo);
15780 this.tpl.compile();
15785 * @event beforeclick
15786 * Fires before a click is processed. Returns false to cancel the default action.
15787 * @param {Roo.View} this
15788 * @param {Number} index The index of the target node
15789 * @param {HTMLElement} node The target node
15790 * @param {Roo.EventObject} e The raw event object
15792 "beforeclick" : true,
15795 * Fires when a template node is clicked.
15796 * @param {Roo.View} this
15797 * @param {Number} index The index of the target node
15798 * @param {HTMLElement} node The target node
15799 * @param {Roo.EventObject} e The raw event object
15804 * Fires when a template node is double clicked.
15805 * @param {Roo.View} this
15806 * @param {Number} index The index of the target node
15807 * @param {HTMLElement} node The target node
15808 * @param {Roo.EventObject} e The raw event object
15812 * @event contextmenu
15813 * Fires when a template node is right clicked.
15814 * @param {Roo.View} this
15815 * @param {Number} index The index of the target node
15816 * @param {HTMLElement} node The target node
15817 * @param {Roo.EventObject} e The raw event object
15819 "contextmenu" : true,
15821 * @event selectionchange
15822 * Fires when the selected nodes change.
15823 * @param {Roo.View} this
15824 * @param {Array} selections Array of the selected nodes
15826 "selectionchange" : true,
15829 * @event beforeselect
15830 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15831 * @param {Roo.View} this
15832 * @param {HTMLElement} node The node to be selected
15833 * @param {Array} selections Array of currently selected nodes
15835 "beforeselect" : true,
15837 * @event preparedata
15838 * Fires on every row to render, to allow you to change the data.
15839 * @param {Roo.View} this
15840 * @param {Object} data to be rendered (change this)
15842 "preparedata" : true
15850 "click": this.onClick,
15851 "dblclick": this.onDblClick,
15852 "contextmenu": this.onContextMenu,
15856 this.selections = [];
15858 this.cmp = new Roo.CompositeElementLite([]);
15860 this.store = Roo.factory(this.store, Roo.data);
15861 this.setStore(this.store, true);
15864 if ( this.footer && this.footer.xtype) {
15866 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15868 this.footer.dataSource = this.store;
15869 this.footer.container = fctr;
15870 this.footer = Roo.factory(this.footer, Roo);
15871 fctr.insertFirst(this.el);
15873 // this is a bit insane - as the paging toolbar seems to detach the el..
15874 // dom.parentNode.parentNode.parentNode
15875 // they get detached?
15879 Roo.View.superclass.constructor.call(this);
15884 Roo.extend(Roo.View, Roo.util.Observable, {
15887 * @cfg {Roo.data.Store} store Data store to load data from.
15892 * @cfg {String|Roo.Element} el The container element.
15897 * @cfg {String|Roo.Template} tpl The template used by this View
15901 * @cfg {String} dataName the named area of the template to use as the data area
15902 * Works with domtemplates roo-name="name"
15906 * @cfg {String} selectedClass The css class to add to selected nodes
15908 selectedClass : "x-view-selected",
15910 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15915 * @cfg {String} text to display on mask (default Loading)
15919 * @cfg {Boolean} multiSelect Allow multiple selection
15921 multiSelect : false,
15923 * @cfg {Boolean} singleSelect Allow single selection
15925 singleSelect: false,
15928 * @cfg {Boolean} toggleSelect - selecting
15930 toggleSelect : false,
15933 * @cfg {Boolean} tickable - selecting
15938 * Returns the element this view is bound to.
15939 * @return {Roo.Element}
15941 getEl : function(){
15942 return this.wrapEl;
15948 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15950 refresh : function(){
15951 //Roo.log('refresh');
15954 // if we are using something like 'domtemplate', then
15955 // the what gets used is:
15956 // t.applySubtemplate(NAME, data, wrapping data..)
15957 // the outer template then get' applied with
15958 // the store 'extra data'
15959 // and the body get's added to the
15960 // roo-name="data" node?
15961 // <span class='roo-tpl-{name}'></span> ?????
15965 this.clearSelections();
15966 this.el.update("");
15968 var records = this.store.getRange();
15969 if(records.length < 1) {
15971 // is this valid?? = should it render a template??
15973 this.el.update(this.emptyText);
15977 if (this.dataName) {
15978 this.el.update(t.apply(this.store.meta)); //????
15979 el = this.el.child('.roo-tpl-' + this.dataName);
15982 for(var i = 0, len = records.length; i < len; i++){
15983 var data = this.prepareData(records[i].data, i, records[i]);
15984 this.fireEvent("preparedata", this, data, i, records[i]);
15986 var d = Roo.apply({}, data);
15989 Roo.apply(d, {'roo-id' : Roo.id()});
15993 Roo.each(this.parent.item, function(item){
15994 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15997 Roo.apply(d, {'roo-data-checked' : 'checked'});
16001 html[html.length] = Roo.util.Format.trim(
16003 t.applySubtemplate(this.dataName, d, this.store.meta) :
16010 el.update(html.join(""));
16011 this.nodes = el.dom.childNodes;
16012 this.updateIndexes(0);
16017 * Function to override to reformat the data that is sent to
16018 * the template for each node.
16019 * DEPRICATED - use the preparedata event handler.
16020 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16021 * a JSON object for an UpdateManager bound view).
16023 prepareData : function(data, index, record)
16025 this.fireEvent("preparedata", this, data, index, record);
16029 onUpdate : function(ds, record){
16030 // Roo.log('on update');
16031 this.clearSelections();
16032 var index = this.store.indexOf(record);
16033 var n = this.nodes[index];
16034 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16035 n.parentNode.removeChild(n);
16036 this.updateIndexes(index, index);
16042 onAdd : function(ds, records, index)
16044 //Roo.log(['on Add', ds, records, index] );
16045 this.clearSelections();
16046 if(this.nodes.length == 0){
16050 var n = this.nodes[index];
16051 for(var i = 0, len = records.length; i < len; i++){
16052 var d = this.prepareData(records[i].data, i, records[i]);
16054 this.tpl.insertBefore(n, d);
16057 this.tpl.append(this.el, d);
16060 this.updateIndexes(index);
16063 onRemove : function(ds, record, index){
16064 // Roo.log('onRemove');
16065 this.clearSelections();
16066 var el = this.dataName ?
16067 this.el.child('.roo-tpl-' + this.dataName) :
16070 el.dom.removeChild(this.nodes[index]);
16071 this.updateIndexes(index);
16075 * Refresh an individual node.
16076 * @param {Number} index
16078 refreshNode : function(index){
16079 this.onUpdate(this.store, this.store.getAt(index));
16082 updateIndexes : function(startIndex, endIndex){
16083 var ns = this.nodes;
16084 startIndex = startIndex || 0;
16085 endIndex = endIndex || ns.length - 1;
16086 for(var i = startIndex; i <= endIndex; i++){
16087 ns[i].nodeIndex = i;
16092 * Changes the data store this view uses and refresh the view.
16093 * @param {Store} store
16095 setStore : function(store, initial){
16096 if(!initial && this.store){
16097 this.store.un("datachanged", this.refresh);
16098 this.store.un("add", this.onAdd);
16099 this.store.un("remove", this.onRemove);
16100 this.store.un("update", this.onUpdate);
16101 this.store.un("clear", this.refresh);
16102 this.store.un("beforeload", this.onBeforeLoad);
16103 this.store.un("load", this.onLoad);
16104 this.store.un("loadexception", this.onLoad);
16108 store.on("datachanged", this.refresh, this);
16109 store.on("add", this.onAdd, this);
16110 store.on("remove", this.onRemove, this);
16111 store.on("update", this.onUpdate, this);
16112 store.on("clear", this.refresh, this);
16113 store.on("beforeload", this.onBeforeLoad, this);
16114 store.on("load", this.onLoad, this);
16115 store.on("loadexception", this.onLoad, this);
16123 * onbeforeLoad - masks the loading area.
16126 onBeforeLoad : function(store,opts)
16128 //Roo.log('onBeforeLoad');
16130 this.el.update("");
16132 this.el.mask(this.mask ? this.mask : "Loading" );
16134 onLoad : function ()
16141 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16142 * @param {HTMLElement} node
16143 * @return {HTMLElement} The template node
16145 findItemFromChild : function(node){
16146 var el = this.dataName ?
16147 this.el.child('.roo-tpl-' + this.dataName,true) :
16150 if(!node || node.parentNode == el){
16153 var p = node.parentNode;
16154 while(p && p != el){
16155 if(p.parentNode == el){
16164 onClick : function(e){
16165 var item = this.findItemFromChild(e.getTarget());
16167 var index = this.indexOf(item);
16168 if(this.onItemClick(item, index, e) !== false){
16169 this.fireEvent("click", this, index, item, e);
16172 this.clearSelections();
16177 onContextMenu : function(e){
16178 var item = this.findItemFromChild(e.getTarget());
16180 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16185 onDblClick : function(e){
16186 var item = this.findItemFromChild(e.getTarget());
16188 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16192 onItemClick : function(item, index, e)
16194 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16197 if (this.toggleSelect) {
16198 var m = this.isSelected(item) ? 'unselect' : 'select';
16201 _t[m](item, true, false);
16204 if(this.multiSelect || this.singleSelect){
16205 if(this.multiSelect && e.shiftKey && this.lastSelection){
16206 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16208 this.select(item, this.multiSelect && e.ctrlKey);
16209 this.lastSelection = item;
16212 if(!this.tickable){
16213 e.preventDefault();
16221 * Get the number of selected nodes.
16224 getSelectionCount : function(){
16225 return this.selections.length;
16229 * Get the currently selected nodes.
16230 * @return {Array} An array of HTMLElements
16232 getSelectedNodes : function(){
16233 return this.selections;
16237 * Get the indexes of the selected nodes.
16240 getSelectedIndexes : function(){
16241 var indexes = [], s = this.selections;
16242 for(var i = 0, len = s.length; i < len; i++){
16243 indexes.push(s[i].nodeIndex);
16249 * Clear all selections
16250 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16252 clearSelections : function(suppressEvent){
16253 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16254 this.cmp.elements = this.selections;
16255 this.cmp.removeClass(this.selectedClass);
16256 this.selections = [];
16257 if(!suppressEvent){
16258 this.fireEvent("selectionchange", this, this.selections);
16264 * Returns true if the passed node is selected
16265 * @param {HTMLElement/Number} node The node or node index
16266 * @return {Boolean}
16268 isSelected : function(node){
16269 var s = this.selections;
16273 node = this.getNode(node);
16274 return s.indexOf(node) !== -1;
16279 * @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
16280 * @param {Boolean} keepExisting (optional) true to keep existing selections
16281 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16283 select : function(nodeInfo, keepExisting, suppressEvent){
16284 if(nodeInfo instanceof Array){
16286 this.clearSelections(true);
16288 for(var i = 0, len = nodeInfo.length; i < len; i++){
16289 this.select(nodeInfo[i], true, true);
16293 var node = this.getNode(nodeInfo);
16294 if(!node || this.isSelected(node)){
16295 return; // already selected.
16298 this.clearSelections(true);
16301 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16302 Roo.fly(node).addClass(this.selectedClass);
16303 this.selections.push(node);
16304 if(!suppressEvent){
16305 this.fireEvent("selectionchange", this, this.selections);
16313 * @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
16314 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16315 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16317 unselect : function(nodeInfo, keepExisting, suppressEvent)
16319 if(nodeInfo instanceof Array){
16320 Roo.each(this.selections, function(s) {
16321 this.unselect(s, nodeInfo);
16325 var node = this.getNode(nodeInfo);
16326 if(!node || !this.isSelected(node)){
16327 //Roo.log("not selected");
16328 return; // not selected.
16332 Roo.each(this.selections, function(s) {
16334 Roo.fly(node).removeClass(this.selectedClass);
16341 this.selections= ns;
16342 this.fireEvent("selectionchange", this, this.selections);
16346 * Gets a template node.
16347 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16348 * @return {HTMLElement} The node or null if it wasn't found
16350 getNode : function(nodeInfo){
16351 if(typeof nodeInfo == "string"){
16352 return document.getElementById(nodeInfo);
16353 }else if(typeof nodeInfo == "number"){
16354 return this.nodes[nodeInfo];
16360 * Gets a range template nodes.
16361 * @param {Number} startIndex
16362 * @param {Number} endIndex
16363 * @return {Array} An array of nodes
16365 getNodes : function(start, end){
16366 var ns = this.nodes;
16367 start = start || 0;
16368 end = typeof end == "undefined" ? ns.length - 1 : end;
16371 for(var i = start; i <= end; i++){
16375 for(var i = start; i >= end; i--){
16383 * Finds the index of the passed node
16384 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16385 * @return {Number} The index of the node or -1
16387 indexOf : function(node){
16388 node = this.getNode(node);
16389 if(typeof node.nodeIndex == "number"){
16390 return node.nodeIndex;
16392 var ns = this.nodes;
16393 for(var i = 0, len = ns.length; i < len; i++){
16404 * based on jquery fullcalendar
16408 Roo.bootstrap = Roo.bootstrap || {};
16410 * @class Roo.bootstrap.Calendar
16411 * @extends Roo.bootstrap.Component
16412 * Bootstrap Calendar class
16413 * @cfg {Boolean} loadMask (true|false) default false
16414 * @cfg {Object} header generate the user specific header of the calendar, default false
16417 * Create a new Container
16418 * @param {Object} config The config object
16423 Roo.bootstrap.Calendar = function(config){
16424 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16428 * Fires when a date is selected
16429 * @param {DatePicker} this
16430 * @param {Date} date The selected date
16434 * @event monthchange
16435 * Fires when the displayed month changes
16436 * @param {DatePicker} this
16437 * @param {Date} date The selected month
16439 'monthchange': true,
16441 * @event evententer
16442 * Fires when mouse over an event
16443 * @param {Calendar} this
16444 * @param {event} Event
16446 'evententer': true,
16448 * @event eventleave
16449 * Fires when the mouse leaves an
16450 * @param {Calendar} this
16453 'eventleave': true,
16455 * @event eventclick
16456 * Fires when the mouse click an
16457 * @param {Calendar} this
16466 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16469 * @cfg {Number} startDay
16470 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16478 getAutoCreate : function(){
16481 var fc_button = function(name, corner, style, content ) {
16482 return Roo.apply({},{
16484 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16486 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16489 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16500 style : 'width:100%',
16507 cls : 'fc-header-left',
16509 fc_button('prev', 'left', 'arrow', '‹' ),
16510 fc_button('next', 'right', 'arrow', '›' ),
16511 { tag: 'span', cls: 'fc-header-space' },
16512 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16520 cls : 'fc-header-center',
16524 cls: 'fc-header-title',
16527 html : 'month / year'
16535 cls : 'fc-header-right',
16537 /* fc_button('month', 'left', '', 'month' ),
16538 fc_button('week', '', '', 'week' ),
16539 fc_button('day', 'right', '', 'day' )
16551 header = this.header;
16554 var cal_heads = function() {
16556 // fixme - handle this.
16558 for (var i =0; i < Date.dayNames.length; i++) {
16559 var d = Date.dayNames[i];
16562 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16563 html : d.substring(0,3)
16567 ret[0].cls += ' fc-first';
16568 ret[6].cls += ' fc-last';
16571 var cal_cell = function(n) {
16574 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16579 cls: 'fc-day-number',
16583 cls: 'fc-day-content',
16587 style: 'position: relative;' // height: 17px;
16599 var cal_rows = function() {
16602 for (var r = 0; r < 6; r++) {
16609 for (var i =0; i < Date.dayNames.length; i++) {
16610 var d = Date.dayNames[i];
16611 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16614 row.cn[0].cls+=' fc-first';
16615 row.cn[0].cn[0].style = 'min-height:90px';
16616 row.cn[6].cls+=' fc-last';
16620 ret[0].cls += ' fc-first';
16621 ret[4].cls += ' fc-prev-last';
16622 ret[5].cls += ' fc-last';
16629 cls: 'fc-border-separate',
16630 style : 'width:100%',
16638 cls : 'fc-first fc-last',
16656 cls : 'fc-content',
16657 style : "position: relative;",
16660 cls : 'fc-view fc-view-month fc-grid',
16661 style : 'position: relative',
16662 unselectable : 'on',
16665 cls : 'fc-event-container',
16666 style : 'position:absolute;z-index:8;top:0;left:0;'
16684 initEvents : function()
16687 throw "can not find store for calendar";
16693 style: "text-align:center",
16697 style: "background-color:white;width:50%;margin:250 auto",
16701 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16712 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16714 var size = this.el.select('.fc-content', true).first().getSize();
16715 this.maskEl.setSize(size.width, size.height);
16716 this.maskEl.enableDisplayMode("block");
16717 if(!this.loadMask){
16718 this.maskEl.hide();
16721 this.store = Roo.factory(this.store, Roo.data);
16722 this.store.on('load', this.onLoad, this);
16723 this.store.on('beforeload', this.onBeforeLoad, this);
16727 this.cells = this.el.select('.fc-day',true);
16728 //Roo.log(this.cells);
16729 this.textNodes = this.el.query('.fc-day-number');
16730 this.cells.addClassOnOver('fc-state-hover');
16732 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16733 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16734 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16735 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16737 this.on('monthchange', this.onMonthChange, this);
16739 this.update(new Date().clearTime());
16742 resize : function() {
16743 var sz = this.el.getSize();
16745 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16746 this.el.select('.fc-day-content div',true).setHeight(34);
16751 showPrevMonth : function(e){
16752 this.update(this.activeDate.add("mo", -1));
16754 showToday : function(e){
16755 this.update(new Date().clearTime());
16758 showNextMonth : function(e){
16759 this.update(this.activeDate.add("mo", 1));
16763 showPrevYear : function(){
16764 this.update(this.activeDate.add("y", -1));
16768 showNextYear : function(){
16769 this.update(this.activeDate.add("y", 1));
16774 update : function(date)
16776 var vd = this.activeDate;
16777 this.activeDate = date;
16778 // if(vd && this.el){
16779 // var t = date.getTime();
16780 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16781 // Roo.log('using add remove');
16783 // this.fireEvent('monthchange', this, date);
16785 // this.cells.removeClass("fc-state-highlight");
16786 // this.cells.each(function(c){
16787 // if(c.dateValue == t){
16788 // c.addClass("fc-state-highlight");
16789 // setTimeout(function(){
16790 // try{c.dom.firstChild.focus();}catch(e){}
16800 var days = date.getDaysInMonth();
16802 var firstOfMonth = date.getFirstDateOfMonth();
16803 var startingPos = firstOfMonth.getDay()-this.startDay;
16805 if(startingPos < this.startDay){
16809 var pm = date.add(Date.MONTH, -1);
16810 var prevStart = pm.getDaysInMonth()-startingPos;
16812 this.cells = this.el.select('.fc-day',true);
16813 this.textNodes = this.el.query('.fc-day-number');
16814 this.cells.addClassOnOver('fc-state-hover');
16816 var cells = this.cells.elements;
16817 var textEls = this.textNodes;
16819 Roo.each(cells, function(cell){
16820 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16823 days += startingPos;
16825 // convert everything to numbers so it's fast
16826 var day = 86400000;
16827 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16830 //Roo.log(prevStart);
16832 var today = new Date().clearTime().getTime();
16833 var sel = date.clearTime().getTime();
16834 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16835 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16836 var ddMatch = this.disabledDatesRE;
16837 var ddText = this.disabledDatesText;
16838 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16839 var ddaysText = this.disabledDaysText;
16840 var format = this.format;
16842 var setCellClass = function(cal, cell){
16846 //Roo.log('set Cell Class');
16848 var t = d.getTime();
16852 cell.dateValue = t;
16854 cell.className += " fc-today";
16855 cell.className += " fc-state-highlight";
16856 cell.title = cal.todayText;
16859 // disable highlight in other month..
16860 //cell.className += " fc-state-highlight";
16865 cell.className = " fc-state-disabled";
16866 cell.title = cal.minText;
16870 cell.className = " fc-state-disabled";
16871 cell.title = cal.maxText;
16875 if(ddays.indexOf(d.getDay()) != -1){
16876 cell.title = ddaysText;
16877 cell.className = " fc-state-disabled";
16880 if(ddMatch && format){
16881 var fvalue = d.dateFormat(format);
16882 if(ddMatch.test(fvalue)){
16883 cell.title = ddText.replace("%0", fvalue);
16884 cell.className = " fc-state-disabled";
16888 if (!cell.initialClassName) {
16889 cell.initialClassName = cell.dom.className;
16892 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16897 for(; i < startingPos; i++) {
16898 textEls[i].innerHTML = (++prevStart);
16899 d.setDate(d.getDate()+1);
16901 cells[i].className = "fc-past fc-other-month";
16902 setCellClass(this, cells[i]);
16907 for(; i < days; i++){
16908 intDay = i - startingPos + 1;
16909 textEls[i].innerHTML = (intDay);
16910 d.setDate(d.getDate()+1);
16912 cells[i].className = ''; // "x-date-active";
16913 setCellClass(this, cells[i]);
16917 for(; i < 42; i++) {
16918 textEls[i].innerHTML = (++extraDays);
16919 d.setDate(d.getDate()+1);
16921 cells[i].className = "fc-future fc-other-month";
16922 setCellClass(this, cells[i]);
16925 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16927 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16929 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16930 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16932 if(totalRows != 6){
16933 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16934 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16937 this.fireEvent('monthchange', this, date);
16941 if(!this.internalRender){
16942 var main = this.el.dom.firstChild;
16943 var w = main.offsetWidth;
16944 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16945 Roo.fly(main).setWidth(w);
16946 this.internalRender = true;
16947 // opera does not respect the auto grow header center column
16948 // then, after it gets a width opera refuses to recalculate
16949 // without a second pass
16950 if(Roo.isOpera && !this.secondPass){
16951 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16952 this.secondPass = true;
16953 this.update.defer(10, this, [date]);
16960 findCell : function(dt) {
16961 dt = dt.clearTime().getTime();
16963 this.cells.each(function(c){
16964 //Roo.log("check " +c.dateValue + '?=' + dt);
16965 if(c.dateValue == dt){
16975 findCells : function(ev) {
16976 var s = ev.start.clone().clearTime().getTime();
16978 var e= ev.end.clone().clearTime().getTime();
16981 this.cells.each(function(c){
16982 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16984 if(c.dateValue > e){
16987 if(c.dateValue < s){
16996 // findBestRow: function(cells)
17000 // for (var i =0 ; i < cells.length;i++) {
17001 // ret = Math.max(cells[i].rows || 0,ret);
17008 addItem : function(ev)
17010 // look for vertical location slot in
17011 var cells = this.findCells(ev);
17013 // ev.row = this.findBestRow(cells);
17015 // work out the location.
17019 for(var i =0; i < cells.length; i++) {
17021 cells[i].row = cells[0].row;
17024 cells[i].row = cells[i].row + 1;
17034 if (crow.start.getY() == cells[i].getY()) {
17036 crow.end = cells[i];
17053 cells[0].events.push(ev);
17055 this.calevents.push(ev);
17058 clearEvents: function() {
17060 if(!this.calevents){
17064 Roo.each(this.cells.elements, function(c){
17070 Roo.each(this.calevents, function(e) {
17071 Roo.each(e.els, function(el) {
17072 el.un('mouseenter' ,this.onEventEnter, this);
17073 el.un('mouseleave' ,this.onEventLeave, this);
17078 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17084 renderEvents: function()
17088 this.cells.each(function(c) {
17097 if(c.row != c.events.length){
17098 r = 4 - (4 - (c.row - c.events.length));
17101 c.events = ev.slice(0, r);
17102 c.more = ev.slice(r);
17104 if(c.more.length && c.more.length == 1){
17105 c.events.push(c.more.pop());
17108 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17112 this.cells.each(function(c) {
17114 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17117 for (var e = 0; e < c.events.length; e++){
17118 var ev = c.events[e];
17119 var rows = ev.rows;
17121 for(var i = 0; i < rows.length; i++) {
17123 // how many rows should it span..
17126 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17127 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17129 unselectable : "on",
17132 cls: 'fc-event-inner',
17136 // cls: 'fc-event-time',
17137 // html : cells.length > 1 ? '' : ev.time
17141 cls: 'fc-event-title',
17142 html : String.format('{0}', ev.title)
17149 cls: 'ui-resizable-handle ui-resizable-e',
17150 html : '  '
17157 cfg.cls += ' fc-event-start';
17159 if ((i+1) == rows.length) {
17160 cfg.cls += ' fc-event-end';
17163 var ctr = _this.el.select('.fc-event-container',true).first();
17164 var cg = ctr.createChild(cfg);
17166 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17167 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17169 var r = (c.more.length) ? 1 : 0;
17170 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17171 cg.setWidth(ebox.right - sbox.x -2);
17173 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17174 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17175 cg.on('click', _this.onEventClick, _this, ev);
17186 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17187 style : 'position: absolute',
17188 unselectable : "on",
17191 cls: 'fc-event-inner',
17195 cls: 'fc-event-title',
17203 cls: 'ui-resizable-handle ui-resizable-e',
17204 html : '  '
17210 var ctr = _this.el.select('.fc-event-container',true).first();
17211 var cg = ctr.createChild(cfg);
17213 var sbox = c.select('.fc-day-content',true).first().getBox();
17214 var ebox = c.select('.fc-day-content',true).first().getBox();
17216 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17217 cg.setWidth(ebox.right - sbox.x -2);
17219 cg.on('click', _this.onMoreEventClick, _this, c.more);
17229 onEventEnter: function (e, el,event,d) {
17230 this.fireEvent('evententer', this, el, event);
17233 onEventLeave: function (e, el,event,d) {
17234 this.fireEvent('eventleave', this, el, event);
17237 onEventClick: function (e, el,event,d) {
17238 this.fireEvent('eventclick', this, el, event);
17241 onMonthChange: function () {
17245 onMoreEventClick: function(e, el, more)
17249 this.calpopover.placement = 'right';
17250 this.calpopover.setTitle('More');
17252 this.calpopover.setContent('');
17254 var ctr = this.calpopover.el.select('.popover-content', true).first();
17256 Roo.each(more, function(m){
17258 cls : 'fc-event-hori fc-event-draggable',
17261 var cg = ctr.createChild(cfg);
17263 cg.on('click', _this.onEventClick, _this, m);
17266 this.calpopover.show(el);
17271 onLoad: function ()
17273 this.calevents = [];
17276 if(this.store.getCount() > 0){
17277 this.store.data.each(function(d){
17280 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17281 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17282 time : d.data.start_time,
17283 title : d.data.title,
17284 description : d.data.description,
17285 venue : d.data.venue
17290 this.renderEvents();
17292 if(this.calevents.length && this.loadMask){
17293 this.maskEl.hide();
17297 onBeforeLoad: function()
17299 this.clearEvents();
17301 this.maskEl.show();
17315 * @class Roo.bootstrap.Popover
17316 * @extends Roo.bootstrap.Component
17317 * Bootstrap Popover class
17318 * @cfg {String} html contents of the popover (or false to use children..)
17319 * @cfg {String} title of popover (or false to hide)
17320 * @cfg {String} placement how it is placed
17321 * @cfg {String} trigger click || hover (or false to trigger manually)
17322 * @cfg {String} over what (parent or false to trigger manually.)
17323 * @cfg {Number} delay - delay before showing
17326 * Create a new Popover
17327 * @param {Object} config The config object
17330 Roo.bootstrap.Popover = function(config){
17331 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17337 * After the popover show
17339 * @param {Roo.bootstrap.Popover} this
17344 * After the popover hide
17346 * @param {Roo.bootstrap.Popover} this
17352 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17354 title: 'Fill in a title',
17357 placement : 'right',
17358 trigger : 'hover', // hover
17364 can_build_overlaid : false,
17366 getChildContainer : function()
17368 return this.el.select('.popover-content',true).first();
17371 getAutoCreate : function(){
17374 cls : 'popover roo-dynamic',
17375 style: 'display:block',
17381 cls : 'popover-inner',
17385 cls: 'popover-title',
17389 cls : 'popover-content',
17400 setTitle: function(str)
17403 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17405 setContent: function(str)
17408 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17410 // as it get's added to the bottom of the page.
17411 onRender : function(ct, position)
17413 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17415 var cfg = Roo.apply({}, this.getAutoCreate());
17419 cfg.cls += ' ' + this.cls;
17422 cfg.style = this.style;
17424 //Roo.log("adding to ");
17425 this.el = Roo.get(document.body).createChild(cfg, position);
17426 // Roo.log(this.el);
17431 initEvents : function()
17433 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17434 this.el.enableDisplayMode('block');
17436 if (this.over === false) {
17439 if (this.triggers === false) {
17442 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17443 var triggers = this.trigger ? this.trigger.split(' ') : [];
17444 Roo.each(triggers, function(trigger) {
17446 if (trigger == 'click') {
17447 on_el.on('click', this.toggle, this);
17448 } else if (trigger != 'manual') {
17449 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17450 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17452 on_el.on(eventIn ,this.enter, this);
17453 on_el.on(eventOut, this.leave, this);
17464 toggle : function () {
17465 this.hoverState == 'in' ? this.leave() : this.enter();
17468 enter : function () {
17470 clearTimeout(this.timeout);
17472 this.hoverState = 'in';
17474 if (!this.delay || !this.delay.show) {
17479 this.timeout = setTimeout(function () {
17480 if (_t.hoverState == 'in') {
17483 }, this.delay.show)
17486 leave : function() {
17487 clearTimeout(this.timeout);
17489 this.hoverState = 'out';
17491 if (!this.delay || !this.delay.hide) {
17496 this.timeout = setTimeout(function () {
17497 if (_t.hoverState == 'out') {
17500 }, this.delay.hide)
17503 show : function (on_el)
17506 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17510 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17511 if (this.html !== false) {
17512 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17514 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17515 if (!this.title.length) {
17516 this.el.select('.popover-title',true).hide();
17519 var placement = typeof this.placement == 'function' ?
17520 this.placement.call(this, this.el, on_el) :
17523 var autoToken = /\s?auto?\s?/i;
17524 var autoPlace = autoToken.test(placement);
17526 placement = placement.replace(autoToken, '') || 'top';
17530 //this.el.setXY([0,0]);
17532 this.el.dom.style.display='block';
17533 this.el.addClass(placement);
17535 //this.el.appendTo(on_el);
17537 var p = this.getPosition();
17538 var box = this.el.getBox();
17543 var align = Roo.bootstrap.Popover.alignment[placement];
17546 this.el.alignTo(on_el, align[0],align[1]);
17547 //var arrow = this.el.select('.arrow',true).first();
17548 //arrow.set(align[2],
17550 this.el.addClass('in');
17553 if (this.el.hasClass('fade')) {
17557 this.hoverState = 'in';
17559 this.fireEvent('show', this);
17564 this.el.setXY([0,0]);
17565 this.el.removeClass('in');
17567 this.hoverState = null;
17569 this.fireEvent('hide', this);
17574 Roo.bootstrap.Popover.alignment = {
17575 'left' : ['r-l', [-10,0], 'right'],
17576 'right' : ['l-r', [10,0], 'left'],
17577 'bottom' : ['t-b', [0,10], 'top'],
17578 'top' : [ 'b-t', [0,-10], 'bottom']
17589 * @class Roo.bootstrap.Progress
17590 * @extends Roo.bootstrap.Component
17591 * Bootstrap Progress class
17592 * @cfg {Boolean} striped striped of the progress bar
17593 * @cfg {Boolean} active animated of the progress bar
17597 * Create a new Progress
17598 * @param {Object} config The config object
17601 Roo.bootstrap.Progress = function(config){
17602 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17605 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17610 getAutoCreate : function(){
17618 cfg.cls += ' progress-striped';
17622 cfg.cls += ' active';
17641 * @class Roo.bootstrap.ProgressBar
17642 * @extends Roo.bootstrap.Component
17643 * Bootstrap ProgressBar class
17644 * @cfg {Number} aria_valuenow aria-value now
17645 * @cfg {Number} aria_valuemin aria-value min
17646 * @cfg {Number} aria_valuemax aria-value max
17647 * @cfg {String} label label for the progress bar
17648 * @cfg {String} panel (success | info | warning | danger )
17649 * @cfg {String} role role of the progress bar
17650 * @cfg {String} sr_only text
17654 * Create a new ProgressBar
17655 * @param {Object} config The config object
17658 Roo.bootstrap.ProgressBar = function(config){
17659 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17662 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17666 aria_valuemax : 100,
17672 getAutoCreate : function()
17677 cls: 'progress-bar',
17678 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17690 cfg.role = this.role;
17693 if(this.aria_valuenow){
17694 cfg['aria-valuenow'] = this.aria_valuenow;
17697 if(this.aria_valuemin){
17698 cfg['aria-valuemin'] = this.aria_valuemin;
17701 if(this.aria_valuemax){
17702 cfg['aria-valuemax'] = this.aria_valuemax;
17705 if(this.label && !this.sr_only){
17706 cfg.html = this.label;
17710 cfg.cls += ' progress-bar-' + this.panel;
17716 update : function(aria_valuenow)
17718 this.aria_valuenow = aria_valuenow;
17720 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17735 * @class Roo.bootstrap.TabGroup
17736 * @extends Roo.bootstrap.Column
17737 * Bootstrap Column class
17738 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17739 * @cfg {Boolean} carousel true to make the group behave like a carousel
17740 * @cfg {Boolean} bullets show bullets for the panels
17741 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17742 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17743 * @cfg {Boolean} showarrow (true|false) show arrow default true
17746 * Create a new TabGroup
17747 * @param {Object} config The config object
17750 Roo.bootstrap.TabGroup = function(config){
17751 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17753 this.navId = Roo.id();
17756 Roo.bootstrap.TabGroup.register(this);
17760 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17763 transition : false,
17768 slideOnTouch : false,
17771 getAutoCreate : function()
17773 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17775 cfg.cls += ' tab-content';
17777 if (this.carousel) {
17778 cfg.cls += ' carousel slide';
17781 cls : 'carousel-inner',
17785 if(this.bullets && !Roo.isTouch){
17788 cls : 'carousel-bullets',
17792 if(this.bullets_cls){
17793 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17800 cfg.cn[0].cn.push(bullets);
17803 if(this.showarrow){
17804 cfg.cn[0].cn.push({
17806 class : 'carousel-arrow',
17810 class : 'carousel-prev',
17814 class : 'fa fa-chevron-left'
17820 class : 'carousel-next',
17824 class : 'fa fa-chevron-right'
17837 initEvents: function()
17839 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17840 // this.el.on("touchstart", this.onTouchStart, this);
17843 if(this.autoslide){
17846 this.slideFn = window.setInterval(function() {
17847 _this.showPanelNext();
17851 if(this.showarrow){
17852 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17853 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17859 // onTouchStart : function(e, el, o)
17861 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17865 // this.showPanelNext();
17869 getChildContainer : function()
17871 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17875 * register a Navigation item
17876 * @param {Roo.bootstrap.NavItem} the navitem to add
17878 register : function(item)
17880 this.tabs.push( item);
17881 item.navId = this.navId; // not really needed..
17886 getActivePanel : function()
17889 Roo.each(this.tabs, function(t) {
17899 getPanelByName : function(n)
17902 Roo.each(this.tabs, function(t) {
17903 if (t.tabId == n) {
17911 indexOfPanel : function(p)
17914 Roo.each(this.tabs, function(t,i) {
17915 if (t.tabId == p.tabId) {
17924 * show a specific panel
17925 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17926 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17928 showPanel : function (pan)
17930 if(this.transition || typeof(pan) == 'undefined'){
17931 Roo.log("waiting for the transitionend");
17935 if (typeof(pan) == 'number') {
17936 pan = this.tabs[pan];
17939 if (typeof(pan) == 'string') {
17940 pan = this.getPanelByName(pan);
17943 var cur = this.getActivePanel();
17946 Roo.log('pan or acitve pan is undefined');
17950 if (pan.tabId == this.getActivePanel().tabId) {
17954 if (false === cur.fireEvent('beforedeactivate')) {
17958 if(this.bullets > 0 && !Roo.isTouch){
17959 this.setActiveBullet(this.indexOfPanel(pan));
17962 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17964 this.transition = true;
17965 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17966 var lr = dir == 'next' ? 'left' : 'right';
17967 pan.el.addClass(dir); // or prev
17968 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17969 cur.el.addClass(lr); // or right
17970 pan.el.addClass(lr);
17973 cur.el.on('transitionend', function() {
17974 Roo.log("trans end?");
17976 pan.el.removeClass([lr,dir]);
17977 pan.setActive(true);
17979 cur.el.removeClass([lr]);
17980 cur.setActive(false);
17982 _this.transition = false;
17984 }, this, { single: true } );
17989 cur.setActive(false);
17990 pan.setActive(true);
17995 showPanelNext : function()
17997 var i = this.indexOfPanel(this.getActivePanel());
17999 if (i >= this.tabs.length - 1 && !this.autoslide) {
18003 if (i >= this.tabs.length - 1 && this.autoslide) {
18007 this.showPanel(this.tabs[i+1]);
18010 showPanelPrev : function()
18012 var i = this.indexOfPanel(this.getActivePanel());
18014 if (i < 1 && !this.autoslide) {
18018 if (i < 1 && this.autoslide) {
18019 i = this.tabs.length;
18022 this.showPanel(this.tabs[i-1]);
18026 addBullet: function()
18028 if(!this.bullets || Roo.isTouch){
18031 var ctr = this.el.select('.carousel-bullets',true).first();
18032 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18033 var bullet = ctr.createChild({
18034 cls : 'bullet bullet-' + i
18035 },ctr.dom.lastChild);
18040 bullet.on('click', (function(e, el, o, ii, t){
18042 e.preventDefault();
18044 this.showPanel(ii);
18046 if(this.autoslide && this.slideFn){
18047 clearInterval(this.slideFn);
18048 this.slideFn = window.setInterval(function() {
18049 _this.showPanelNext();
18053 }).createDelegate(this, [i, bullet], true));
18058 setActiveBullet : function(i)
18064 Roo.each(this.el.select('.bullet', true).elements, function(el){
18065 el.removeClass('selected');
18068 var bullet = this.el.select('.bullet-' + i, true).first();
18074 bullet.addClass('selected');
18085 Roo.apply(Roo.bootstrap.TabGroup, {
18089 * register a Navigation Group
18090 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18092 register : function(navgrp)
18094 this.groups[navgrp.navId] = navgrp;
18098 * fetch a Navigation Group based on the navigation ID
18099 * if one does not exist , it will get created.
18100 * @param {string} the navgroup to add
18101 * @returns {Roo.bootstrap.NavGroup} the navgroup
18103 get: function(navId) {
18104 if (typeof(this.groups[navId]) == 'undefined') {
18105 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18107 return this.groups[navId] ;
18122 * @class Roo.bootstrap.TabPanel
18123 * @extends Roo.bootstrap.Component
18124 * Bootstrap TabPanel class
18125 * @cfg {Boolean} active panel active
18126 * @cfg {String} html panel content
18127 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18128 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18129 * @cfg {String} href click to link..
18133 * Create a new TabPanel
18134 * @param {Object} config The config object
18137 Roo.bootstrap.TabPanel = function(config){
18138 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18142 * Fires when the active status changes
18143 * @param {Roo.bootstrap.TabPanel} this
18144 * @param {Boolean} state the new state
18149 * @event beforedeactivate
18150 * Fires before a tab is de-activated - can be used to do validation on a form.
18151 * @param {Roo.bootstrap.TabPanel} this
18152 * @return {Boolean} false if there is an error
18155 'beforedeactivate': true
18158 this.tabId = this.tabId || Roo.id();
18162 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18170 getAutoCreate : function(){
18173 // item is needed for carousel - not sure if it has any effect otherwise
18174 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18175 html: this.html || ''
18179 cfg.cls += ' active';
18183 cfg.tabId = this.tabId;
18190 initEvents: function()
18192 var p = this.parent();
18194 this.navId = this.navId || p.navId;
18196 if (typeof(this.navId) != 'undefined') {
18197 // not really needed.. but just in case.. parent should be a NavGroup.
18198 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18202 var i = tg.tabs.length - 1;
18204 if(this.active && tg.bullets > 0 && i < tg.bullets){
18205 tg.setActiveBullet(i);
18209 this.el.on('click', this.onClick, this);
18212 this.el.on("touchstart", this.onTouchStart, this);
18213 this.el.on("touchmove", this.onTouchMove, this);
18214 this.el.on("touchend", this.onTouchEnd, this);
18219 onRender : function(ct, position)
18221 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18224 setActive : function(state)
18226 Roo.log("panel - set active " + this.tabId + "=" + state);
18228 this.active = state;
18230 this.el.removeClass('active');
18232 } else if (!this.el.hasClass('active')) {
18233 this.el.addClass('active');
18236 this.fireEvent('changed', this, state);
18239 onClick : function(e)
18241 e.preventDefault();
18243 if(!this.href.length){
18247 window.location.href = this.href;
18256 onTouchStart : function(e)
18258 this.swiping = false;
18260 this.startX = e.browserEvent.touches[0].clientX;
18261 this.startY = e.browserEvent.touches[0].clientY;
18264 onTouchMove : function(e)
18266 this.swiping = true;
18268 this.endX = e.browserEvent.touches[0].clientX;
18269 this.endY = e.browserEvent.touches[0].clientY;
18272 onTouchEnd : function(e)
18279 var tabGroup = this.parent();
18281 if(this.endX > this.startX){ // swiping right
18282 tabGroup.showPanelPrev();
18286 if(this.startX > this.endX){ // swiping left
18287 tabGroup.showPanelNext();
18306 * @class Roo.bootstrap.DateField
18307 * @extends Roo.bootstrap.Input
18308 * Bootstrap DateField class
18309 * @cfg {Number} weekStart default 0
18310 * @cfg {String} viewMode default empty, (months|years)
18311 * @cfg {String} minViewMode default empty, (months|years)
18312 * @cfg {Number} startDate default -Infinity
18313 * @cfg {Number} endDate default Infinity
18314 * @cfg {Boolean} todayHighlight default false
18315 * @cfg {Boolean} todayBtn default false
18316 * @cfg {Boolean} calendarWeeks default false
18317 * @cfg {Object} daysOfWeekDisabled default empty
18318 * @cfg {Boolean} singleMode default false (true | false)
18320 * @cfg {Boolean} keyboardNavigation default true
18321 * @cfg {String} language default en
18324 * Create a new DateField
18325 * @param {Object} config The config object
18328 Roo.bootstrap.DateField = function(config){
18329 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18333 * Fires when this field show.
18334 * @param {Roo.bootstrap.DateField} this
18335 * @param {Mixed} date The date value
18340 * Fires when this field hide.
18341 * @param {Roo.bootstrap.DateField} this
18342 * @param {Mixed} date The date value
18347 * Fires when select a date.
18348 * @param {Roo.bootstrap.DateField} this
18349 * @param {Mixed} date The date value
18353 * @event beforeselect
18354 * Fires when before select a date.
18355 * @param {Roo.bootstrap.DateField} this
18356 * @param {Mixed} date The date value
18358 beforeselect : true
18362 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18365 * @cfg {String} format
18366 * The default date format string which can be overriden for localization support. The format must be
18367 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18371 * @cfg {String} altFormats
18372 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18373 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18375 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18383 todayHighlight : false,
18389 keyboardNavigation: true,
18391 calendarWeeks: false,
18393 startDate: -Infinity,
18397 daysOfWeekDisabled: [],
18401 singleMode : false,
18403 UTCDate: function()
18405 return new Date(Date.UTC.apply(Date, arguments));
18408 UTCToday: function()
18410 var today = new Date();
18411 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18414 getDate: function() {
18415 var d = this.getUTCDate();
18416 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18419 getUTCDate: function() {
18423 setDate: function(d) {
18424 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18427 setUTCDate: function(d) {
18429 this.setValue(this.formatDate(this.date));
18432 onRender: function(ct, position)
18435 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18437 this.language = this.language || 'en';
18438 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18439 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18441 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18442 this.format = this.format || 'm/d/y';
18443 this.isInline = false;
18444 this.isInput = true;
18445 this.component = this.el.select('.add-on', true).first() || false;
18446 this.component = (this.component && this.component.length === 0) ? false : this.component;
18447 this.hasInput = this.component && this.inputEl().length;
18449 if (typeof(this.minViewMode === 'string')) {
18450 switch (this.minViewMode) {
18452 this.minViewMode = 1;
18455 this.minViewMode = 2;
18458 this.minViewMode = 0;
18463 if (typeof(this.viewMode === 'string')) {
18464 switch (this.viewMode) {
18477 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18479 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18481 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18483 this.picker().on('mousedown', this.onMousedown, this);
18484 this.picker().on('click', this.onClick, this);
18486 this.picker().addClass('datepicker-dropdown');
18488 this.startViewMode = this.viewMode;
18490 if(this.singleMode){
18491 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18492 v.setVisibilityMode(Roo.Element.DISPLAY);
18496 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18497 v.setStyle('width', '189px');
18501 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18502 if(!this.calendarWeeks){
18507 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18508 v.attr('colspan', function(i, val){
18509 return parseInt(val) + 1;
18514 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18516 this.setStartDate(this.startDate);
18517 this.setEndDate(this.endDate);
18519 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18526 if(this.isInline) {
18531 picker : function()
18533 return this.pickerEl;
18534 // return this.el.select('.datepicker', true).first();
18537 fillDow: function()
18539 var dowCnt = this.weekStart;
18548 if(this.calendarWeeks){
18556 while (dowCnt < this.weekStart + 7) {
18560 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18564 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18567 fillMonths: function()
18570 var months = this.picker().select('>.datepicker-months td', true).first();
18572 months.dom.innerHTML = '';
18578 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18581 months.createChild(month);
18588 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;
18590 if (this.date < this.startDate) {
18591 this.viewDate = new Date(this.startDate);
18592 } else if (this.date > this.endDate) {
18593 this.viewDate = new Date(this.endDate);
18595 this.viewDate = new Date(this.date);
18603 var d = new Date(this.viewDate),
18604 year = d.getUTCFullYear(),
18605 month = d.getUTCMonth(),
18606 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18607 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18608 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18609 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18610 currentDate = this.date && this.date.valueOf(),
18611 today = this.UTCToday();
18613 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18615 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18617 // this.picker.select('>tfoot th.today').
18618 // .text(dates[this.language].today)
18619 // .toggle(this.todayBtn !== false);
18621 this.updateNavArrows();
18624 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18626 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18628 prevMonth.setUTCDate(day);
18630 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18632 var nextMonth = new Date(prevMonth);
18634 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18636 nextMonth = nextMonth.valueOf();
18638 var fillMonths = false;
18640 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18642 while(prevMonth.valueOf() < nextMonth) {
18645 if (prevMonth.getUTCDay() === this.weekStart) {
18647 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18655 if(this.calendarWeeks){
18656 // ISO 8601: First week contains first thursday.
18657 // ISO also states week starts on Monday, but we can be more abstract here.
18659 // Start of current week: based on weekstart/current date
18660 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18661 // Thursday of this week
18662 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18663 // First Thursday of year, year from thursday
18664 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18665 // Calendar week: ms between thursdays, div ms per day, div 7 days
18666 calWeek = (th - yth) / 864e5 / 7 + 1;
18668 fillMonths.cn.push({
18676 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18678 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18681 if (this.todayHighlight &&
18682 prevMonth.getUTCFullYear() == today.getFullYear() &&
18683 prevMonth.getUTCMonth() == today.getMonth() &&
18684 prevMonth.getUTCDate() == today.getDate()) {
18685 clsName += ' today';
18688 if (currentDate && prevMonth.valueOf() === currentDate) {
18689 clsName += ' active';
18692 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18693 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18694 clsName += ' disabled';
18697 fillMonths.cn.push({
18699 cls: 'day ' + clsName,
18700 html: prevMonth.getDate()
18703 prevMonth.setDate(prevMonth.getDate()+1);
18706 var currentYear = this.date && this.date.getUTCFullYear();
18707 var currentMonth = this.date && this.date.getUTCMonth();
18709 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18711 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18712 v.removeClass('active');
18714 if(currentYear === year && k === currentMonth){
18715 v.addClass('active');
18718 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18719 v.addClass('disabled');
18725 year = parseInt(year/10, 10) * 10;
18727 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18729 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18732 for (var i = -1; i < 11; i++) {
18733 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18735 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18743 showMode: function(dir)
18746 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18749 Roo.each(this.picker().select('>div',true).elements, function(v){
18750 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18753 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18758 if(this.isInline) {
18762 this.picker().removeClass(['bottom', 'top']);
18764 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18766 * place to the top of element!
18770 this.picker().addClass('top');
18771 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18776 this.picker().addClass('bottom');
18778 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18781 parseDate : function(value)
18783 if(!value || value instanceof Date){
18786 var v = Date.parseDate(value, this.format);
18787 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18788 v = Date.parseDate(value, 'Y-m-d');
18790 if(!v && this.altFormats){
18791 if(!this.altFormatsArray){
18792 this.altFormatsArray = this.altFormats.split("|");
18794 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18795 v = Date.parseDate(value, this.altFormatsArray[i]);
18801 formatDate : function(date, fmt)
18803 return (!date || !(date instanceof Date)) ?
18804 date : date.dateFormat(fmt || this.format);
18807 onFocus : function()
18809 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18813 onBlur : function()
18815 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18817 var d = this.inputEl().getValue();
18826 this.picker().show();
18830 this.fireEvent('show', this, this.date);
18835 if(this.isInline) {
18838 this.picker().hide();
18839 this.viewMode = this.startViewMode;
18842 this.fireEvent('hide', this, this.date);
18846 onMousedown: function(e)
18848 e.stopPropagation();
18849 e.preventDefault();
18854 Roo.bootstrap.DateField.superclass.keyup.call(this);
18858 setValue: function(v)
18860 if(this.fireEvent('beforeselect', this, v) !== false){
18861 var d = new Date(this.parseDate(v) ).clearTime();
18863 if(isNaN(d.getTime())){
18864 this.date = this.viewDate = '';
18865 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18869 v = this.formatDate(d);
18871 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18873 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18877 this.fireEvent('select', this, this.date);
18881 getValue: function()
18883 return this.formatDate(this.date);
18886 fireKey: function(e)
18888 if (!this.picker().isVisible()){
18889 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18895 var dateChanged = false,
18897 newDate, newViewDate;
18902 e.preventDefault();
18906 if (!this.keyboardNavigation) {
18909 dir = e.keyCode == 37 ? -1 : 1;
18912 newDate = this.moveYear(this.date, dir);
18913 newViewDate = this.moveYear(this.viewDate, dir);
18914 } else if (e.shiftKey){
18915 newDate = this.moveMonth(this.date, dir);
18916 newViewDate = this.moveMonth(this.viewDate, dir);
18918 newDate = new Date(this.date);
18919 newDate.setUTCDate(this.date.getUTCDate() + dir);
18920 newViewDate = new Date(this.viewDate);
18921 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18923 if (this.dateWithinRange(newDate)){
18924 this.date = newDate;
18925 this.viewDate = newViewDate;
18926 this.setValue(this.formatDate(this.date));
18928 e.preventDefault();
18929 dateChanged = true;
18934 if (!this.keyboardNavigation) {
18937 dir = e.keyCode == 38 ? -1 : 1;
18939 newDate = this.moveYear(this.date, dir);
18940 newViewDate = this.moveYear(this.viewDate, dir);
18941 } else if (e.shiftKey){
18942 newDate = this.moveMonth(this.date, dir);
18943 newViewDate = this.moveMonth(this.viewDate, dir);
18945 newDate = new Date(this.date);
18946 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18947 newViewDate = new Date(this.viewDate);
18948 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18950 if (this.dateWithinRange(newDate)){
18951 this.date = newDate;
18952 this.viewDate = newViewDate;
18953 this.setValue(this.formatDate(this.date));
18955 e.preventDefault();
18956 dateChanged = true;
18960 this.setValue(this.formatDate(this.date));
18962 e.preventDefault();
18965 this.setValue(this.formatDate(this.date));
18979 onClick: function(e)
18981 e.stopPropagation();
18982 e.preventDefault();
18984 var target = e.getTarget();
18986 if(target.nodeName.toLowerCase() === 'i'){
18987 target = Roo.get(target).dom.parentNode;
18990 var nodeName = target.nodeName;
18991 var className = target.className;
18992 var html = target.innerHTML;
18993 //Roo.log(nodeName);
18995 switch(nodeName.toLowerCase()) {
18997 switch(className) {
19003 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19004 switch(this.viewMode){
19006 this.viewDate = this.moveMonth(this.viewDate, dir);
19010 this.viewDate = this.moveYear(this.viewDate, dir);
19016 var date = new Date();
19017 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19019 this.setValue(this.formatDate(this.date));
19026 if (className.indexOf('disabled') < 0) {
19027 this.viewDate.setUTCDate(1);
19028 if (className.indexOf('month') > -1) {
19029 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19031 var year = parseInt(html, 10) || 0;
19032 this.viewDate.setUTCFullYear(year);
19036 if(this.singleMode){
19037 this.setValue(this.formatDate(this.viewDate));
19048 //Roo.log(className);
19049 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19050 var day = parseInt(html, 10) || 1;
19051 var year = this.viewDate.getUTCFullYear(),
19052 month = this.viewDate.getUTCMonth();
19054 if (className.indexOf('old') > -1) {
19061 } else if (className.indexOf('new') > -1) {
19069 //Roo.log([year,month,day]);
19070 this.date = this.UTCDate(year, month, day,0,0,0,0);
19071 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19073 //Roo.log(this.formatDate(this.date));
19074 this.setValue(this.formatDate(this.date));
19081 setStartDate: function(startDate)
19083 this.startDate = startDate || -Infinity;
19084 if (this.startDate !== -Infinity) {
19085 this.startDate = this.parseDate(this.startDate);
19088 this.updateNavArrows();
19091 setEndDate: function(endDate)
19093 this.endDate = endDate || Infinity;
19094 if (this.endDate !== Infinity) {
19095 this.endDate = this.parseDate(this.endDate);
19098 this.updateNavArrows();
19101 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19103 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19104 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19105 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19107 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19108 return parseInt(d, 10);
19111 this.updateNavArrows();
19114 updateNavArrows: function()
19116 if(this.singleMode){
19120 var d = new Date(this.viewDate),
19121 year = d.getUTCFullYear(),
19122 month = d.getUTCMonth();
19124 Roo.each(this.picker().select('.prev', true).elements, function(v){
19126 switch (this.viewMode) {
19129 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19135 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19142 Roo.each(this.picker().select('.next', true).elements, function(v){
19144 switch (this.viewMode) {
19147 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19153 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19161 moveMonth: function(date, dir)
19166 var new_date = new Date(date.valueOf()),
19167 day = new_date.getUTCDate(),
19168 month = new_date.getUTCMonth(),
19169 mag = Math.abs(dir),
19171 dir = dir > 0 ? 1 : -1;
19174 // If going back one month, make sure month is not current month
19175 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19177 return new_date.getUTCMonth() == month;
19179 // If going forward one month, make sure month is as expected
19180 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19182 return new_date.getUTCMonth() != new_month;
19184 new_month = month + dir;
19185 new_date.setUTCMonth(new_month);
19186 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19187 if (new_month < 0 || new_month > 11) {
19188 new_month = (new_month + 12) % 12;
19191 // For magnitudes >1, move one month at a time...
19192 for (var i=0; i<mag; i++) {
19193 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19194 new_date = this.moveMonth(new_date, dir);
19196 // ...then reset the day, keeping it in the new month
19197 new_month = new_date.getUTCMonth();
19198 new_date.setUTCDate(day);
19200 return new_month != new_date.getUTCMonth();
19203 // Common date-resetting loop -- if date is beyond end of month, make it
19206 new_date.setUTCDate(--day);
19207 new_date.setUTCMonth(new_month);
19212 moveYear: function(date, dir)
19214 return this.moveMonth(date, dir*12);
19217 dateWithinRange: function(date)
19219 return date >= this.startDate && date <= this.endDate;
19225 this.picker().remove();
19228 validateValue : function(value)
19230 if(this.getVisibilityEl().hasClass('hidden')){
19234 if(value.length < 1) {
19235 if(this.allowBlank){
19241 if(value.length < this.minLength){
19244 if(value.length > this.maxLength){
19248 var vt = Roo.form.VTypes;
19249 if(!vt[this.vtype](value, this)){
19253 if(typeof this.validator == "function"){
19254 var msg = this.validator(value);
19260 if(this.regex && !this.regex.test(value)){
19264 if(typeof(this.parseDate(value)) == 'undefined'){
19268 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19272 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19280 setVisible : function(visible)
19286 this.getEl().removeClass('hidden');
19292 this.getEl().addClass('hidden');
19297 Roo.apply(Roo.bootstrap.DateField, {
19308 html: '<i class="fa fa-arrow-left"/>'
19318 html: '<i class="fa fa-arrow-right"/>'
19360 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19361 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19362 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19363 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19364 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19377 navFnc: 'FullYear',
19382 navFnc: 'FullYear',
19387 Roo.apply(Roo.bootstrap.DateField, {
19391 cls: 'datepicker dropdown-menu roo-dynamic',
19395 cls: 'datepicker-days',
19399 cls: 'table-condensed',
19401 Roo.bootstrap.DateField.head,
19405 Roo.bootstrap.DateField.footer
19412 cls: 'datepicker-months',
19416 cls: 'table-condensed',
19418 Roo.bootstrap.DateField.head,
19419 Roo.bootstrap.DateField.content,
19420 Roo.bootstrap.DateField.footer
19427 cls: 'datepicker-years',
19431 cls: 'table-condensed',
19433 Roo.bootstrap.DateField.head,
19434 Roo.bootstrap.DateField.content,
19435 Roo.bootstrap.DateField.footer
19454 * @class Roo.bootstrap.TimeField
19455 * @extends Roo.bootstrap.Input
19456 * Bootstrap DateField class
19460 * Create a new TimeField
19461 * @param {Object} config The config object
19464 Roo.bootstrap.TimeField = function(config){
19465 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19469 * Fires when this field show.
19470 * @param {Roo.bootstrap.DateField} thisthis
19471 * @param {Mixed} date The date value
19476 * Fires when this field hide.
19477 * @param {Roo.bootstrap.DateField} this
19478 * @param {Mixed} date The date value
19483 * Fires when select a date.
19484 * @param {Roo.bootstrap.DateField} this
19485 * @param {Mixed} date The date value
19491 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19494 * @cfg {String} format
19495 * The default time format string which can be overriden for localization support. The format must be
19496 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19500 onRender: function(ct, position)
19503 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19505 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19507 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19509 this.pop = this.picker().select('>.datepicker-time',true).first();
19510 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19512 this.picker().on('mousedown', this.onMousedown, this);
19513 this.picker().on('click', this.onClick, this);
19515 this.picker().addClass('datepicker-dropdown');
19520 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19521 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19522 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19523 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19524 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19525 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19529 fireKey: function(e){
19530 if (!this.picker().isVisible()){
19531 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19537 e.preventDefault();
19545 this.onTogglePeriod();
19548 this.onIncrementMinutes();
19551 this.onDecrementMinutes();
19560 onClick: function(e) {
19561 e.stopPropagation();
19562 e.preventDefault();
19565 picker : function()
19567 return this.el.select('.datepicker', true).first();
19570 fillTime: function()
19572 var time = this.pop.select('tbody', true).first();
19574 time.dom.innerHTML = '';
19589 cls: 'hours-up glyphicon glyphicon-chevron-up'
19609 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19630 cls: 'timepicker-hour',
19645 cls: 'timepicker-minute',
19660 cls: 'btn btn-primary period',
19682 cls: 'hours-down glyphicon glyphicon-chevron-down'
19702 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19720 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19727 var hours = this.time.getHours();
19728 var minutes = this.time.getMinutes();
19741 hours = hours - 12;
19745 hours = '0' + hours;
19749 minutes = '0' + minutes;
19752 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19753 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19754 this.pop.select('button', true).first().dom.innerHTML = period;
19760 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19762 var cls = ['bottom'];
19764 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19771 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19776 this.picker().addClass(cls.join('-'));
19780 Roo.each(cls, function(c){
19782 _this.picker().setTop(_this.inputEl().getHeight());
19786 _this.picker().setTop(0 - _this.picker().getHeight());
19791 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19795 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19802 onFocus : function()
19804 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19808 onBlur : function()
19810 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19816 this.picker().show();
19821 this.fireEvent('show', this, this.date);
19826 this.picker().hide();
19829 this.fireEvent('hide', this, this.date);
19832 setTime : function()
19835 this.setValue(this.time.format(this.format));
19837 this.fireEvent('select', this, this.date);
19842 onMousedown: function(e){
19843 e.stopPropagation();
19844 e.preventDefault();
19847 onIncrementHours: function()
19849 Roo.log('onIncrementHours');
19850 this.time = this.time.add(Date.HOUR, 1);
19855 onDecrementHours: function()
19857 Roo.log('onDecrementHours');
19858 this.time = this.time.add(Date.HOUR, -1);
19862 onIncrementMinutes: function()
19864 Roo.log('onIncrementMinutes');
19865 this.time = this.time.add(Date.MINUTE, 1);
19869 onDecrementMinutes: function()
19871 Roo.log('onDecrementMinutes');
19872 this.time = this.time.add(Date.MINUTE, -1);
19876 onTogglePeriod: function()
19878 Roo.log('onTogglePeriod');
19879 this.time = this.time.add(Date.HOUR, 12);
19886 Roo.apply(Roo.bootstrap.TimeField, {
19916 cls: 'btn btn-info ok',
19928 Roo.apply(Roo.bootstrap.TimeField, {
19932 cls: 'datepicker dropdown-menu',
19936 cls: 'datepicker-time',
19940 cls: 'table-condensed',
19942 Roo.bootstrap.TimeField.content,
19943 Roo.bootstrap.TimeField.footer
19962 * @class Roo.bootstrap.MonthField
19963 * @extends Roo.bootstrap.Input
19964 * Bootstrap MonthField class
19966 * @cfg {String} language default en
19969 * Create a new MonthField
19970 * @param {Object} config The config object
19973 Roo.bootstrap.MonthField = function(config){
19974 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19979 * Fires when this field show.
19980 * @param {Roo.bootstrap.MonthField} this
19981 * @param {Mixed} date The date value
19986 * Fires when this field hide.
19987 * @param {Roo.bootstrap.MonthField} this
19988 * @param {Mixed} date The date value
19993 * Fires when select a date.
19994 * @param {Roo.bootstrap.MonthField} this
19995 * @param {String} oldvalue The old value
19996 * @param {String} newvalue The new value
20002 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20004 onRender: function(ct, position)
20007 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20009 this.language = this.language || 'en';
20010 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20011 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20013 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20014 this.isInline = false;
20015 this.isInput = true;
20016 this.component = this.el.select('.add-on', true).first() || false;
20017 this.component = (this.component && this.component.length === 0) ? false : this.component;
20018 this.hasInput = this.component && this.inputEL().length;
20020 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20022 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20024 this.picker().on('mousedown', this.onMousedown, this);
20025 this.picker().on('click', this.onClick, this);
20027 this.picker().addClass('datepicker-dropdown');
20029 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20030 v.setStyle('width', '189px');
20037 if(this.isInline) {
20043 setValue: function(v, suppressEvent)
20045 var o = this.getValue();
20047 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20051 if(suppressEvent !== true){
20052 this.fireEvent('select', this, o, v);
20057 getValue: function()
20062 onClick: function(e)
20064 e.stopPropagation();
20065 e.preventDefault();
20067 var target = e.getTarget();
20069 if(target.nodeName.toLowerCase() === 'i'){
20070 target = Roo.get(target).dom.parentNode;
20073 var nodeName = target.nodeName;
20074 var className = target.className;
20075 var html = target.innerHTML;
20077 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20081 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20083 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20089 picker : function()
20091 return this.pickerEl;
20094 fillMonths: function()
20097 var months = this.picker().select('>.datepicker-months td', true).first();
20099 months.dom.innerHTML = '';
20105 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20108 months.createChild(month);
20117 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20118 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20121 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20122 e.removeClass('active');
20124 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20125 e.addClass('active');
20132 if(this.isInline) {
20136 this.picker().removeClass(['bottom', 'top']);
20138 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20140 * place to the top of element!
20144 this.picker().addClass('top');
20145 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20150 this.picker().addClass('bottom');
20152 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20155 onFocus : function()
20157 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20161 onBlur : function()
20163 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20165 var d = this.inputEl().getValue();
20174 this.picker().show();
20175 this.picker().select('>.datepicker-months', true).first().show();
20179 this.fireEvent('show', this, this.date);
20184 if(this.isInline) {
20187 this.picker().hide();
20188 this.fireEvent('hide', this, this.date);
20192 onMousedown: function(e)
20194 e.stopPropagation();
20195 e.preventDefault();
20200 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20204 fireKey: function(e)
20206 if (!this.picker().isVisible()){
20207 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20218 e.preventDefault();
20222 dir = e.keyCode == 37 ? -1 : 1;
20224 this.vIndex = this.vIndex + dir;
20226 if(this.vIndex < 0){
20230 if(this.vIndex > 11){
20234 if(isNaN(this.vIndex)){
20238 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20244 dir = e.keyCode == 38 ? -1 : 1;
20246 this.vIndex = this.vIndex + dir * 4;
20248 if(this.vIndex < 0){
20252 if(this.vIndex > 11){
20256 if(isNaN(this.vIndex)){
20260 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20265 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20266 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20270 e.preventDefault();
20273 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20274 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20290 this.picker().remove();
20295 Roo.apply(Roo.bootstrap.MonthField, {
20314 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20315 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20320 Roo.apply(Roo.bootstrap.MonthField, {
20324 cls: 'datepicker dropdown-menu roo-dynamic',
20328 cls: 'datepicker-months',
20332 cls: 'table-condensed',
20334 Roo.bootstrap.DateField.content
20354 * @class Roo.bootstrap.CheckBox
20355 * @extends Roo.bootstrap.Input
20356 * Bootstrap CheckBox class
20358 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20359 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20360 * @cfg {String} boxLabel The text that appears beside the checkbox
20361 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20362 * @cfg {Boolean} checked initnal the element
20363 * @cfg {Boolean} inline inline the element (default false)
20364 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20365 * @cfg {String} tooltip label tooltip
20368 * Create a new CheckBox
20369 * @param {Object} config The config object
20372 Roo.bootstrap.CheckBox = function(config){
20373 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20378 * Fires when the element is checked or unchecked.
20379 * @param {Roo.bootstrap.CheckBox} this This input
20380 * @param {Boolean} checked The new checked value
20385 * Fires when the element is click.
20386 * @param {Roo.bootstrap.CheckBox} this This input
20393 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20395 inputType: 'checkbox',
20404 getAutoCreate : function()
20406 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20412 cfg.cls = 'form-group ' + this.inputType; //input-group
20415 cfg.cls += ' ' + this.inputType + '-inline';
20421 type : this.inputType,
20422 value : this.inputValue,
20423 cls : 'roo-' + this.inputType, //'form-box',
20424 placeholder : this.placeholder || ''
20428 if(this.inputType != 'radio'){
20432 cls : 'roo-hidden-value',
20433 value : this.checked ? this.inputValue : this.valueOff
20438 if (this.weight) { // Validity check?
20439 cfg.cls += " " + this.inputType + "-" + this.weight;
20442 if (this.disabled) {
20443 input.disabled=true;
20447 input.checked = this.checked;
20452 input.name = this.name;
20454 if(this.inputType != 'radio'){
20455 hidden.name = this.name;
20456 input.name = '_hidden_' + this.name;
20461 input.cls += ' input-' + this.size;
20466 ['xs','sm','md','lg'].map(function(size){
20467 if (settings[size]) {
20468 cfg.cls += ' col-' + size + '-' + settings[size];
20472 var inputblock = input;
20474 if (this.before || this.after) {
20477 cls : 'input-group',
20482 inputblock.cn.push({
20484 cls : 'input-group-addon',
20489 inputblock.cn.push(input);
20491 if(this.inputType != 'radio'){
20492 inputblock.cn.push(hidden);
20496 inputblock.cn.push({
20498 cls : 'input-group-addon',
20505 if (align ==='left' && this.fieldLabel.length) {
20506 // Roo.log("left and has label");
20511 cls : 'control-label',
20512 html : this.fieldLabel
20522 if(this.labelWidth > 12){
20523 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20526 if(this.labelWidth < 13 && this.labelmd == 0){
20527 this.labelmd = this.labelWidth;
20530 if(this.labellg > 0){
20531 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20532 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20535 if(this.labelmd > 0){
20536 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20537 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20540 if(this.labelsm > 0){
20541 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20542 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20545 if(this.labelxs > 0){
20546 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20547 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20550 } else if ( this.fieldLabel.length) {
20551 // Roo.log(" label");
20555 tag: this.boxLabel ? 'span' : 'label',
20557 cls: 'control-label box-input-label',
20558 //cls : 'input-group-addon',
20559 html : this.fieldLabel
20568 // Roo.log(" no label && no align");
20569 cfg.cn = [ inputblock ] ;
20575 var boxLabelCfg = {
20577 //'for': id, // box label is handled by onclick - so no for...
20579 html: this.boxLabel
20583 boxLabelCfg.tooltip = this.tooltip;
20586 cfg.cn.push(boxLabelCfg);
20589 if(this.inputType != 'radio'){
20590 cfg.cn.push(hidden);
20598 * return the real input element.
20600 inputEl: function ()
20602 return this.el.select('input.roo-' + this.inputType,true).first();
20604 hiddenEl: function ()
20606 return this.el.select('input.roo-hidden-value',true).first();
20609 labelEl: function()
20611 return this.el.select('label.control-label',true).first();
20613 /* depricated... */
20617 return this.labelEl();
20620 boxLabelEl: function()
20622 return this.el.select('label.box-label',true).first();
20625 initEvents : function()
20627 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20629 this.inputEl().on('click', this.onClick, this);
20631 if (this.boxLabel) {
20632 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20635 this.startValue = this.getValue();
20638 Roo.bootstrap.CheckBox.register(this);
20642 onClick : function(e)
20644 if(this.fireEvent('click', this, e) !== false){
20645 this.setChecked(!this.checked);
20650 setChecked : function(state,suppressEvent)
20652 this.startValue = this.getValue();
20654 if(this.inputType == 'radio'){
20656 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20657 e.dom.checked = false;
20660 this.inputEl().dom.checked = true;
20662 this.inputEl().dom.value = this.inputValue;
20664 if(suppressEvent !== true){
20665 this.fireEvent('check', this, true);
20673 this.checked = state;
20675 this.inputEl().dom.checked = state;
20678 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20680 if(suppressEvent !== true){
20681 this.fireEvent('check', this, state);
20687 getValue : function()
20689 if(this.inputType == 'radio'){
20690 return this.getGroupValue();
20693 return this.hiddenEl().dom.value;
20697 getGroupValue : function()
20699 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20703 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20706 setValue : function(v,suppressEvent)
20708 if(this.inputType == 'radio'){
20709 this.setGroupValue(v, suppressEvent);
20713 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20718 setGroupValue : function(v, suppressEvent)
20720 this.startValue = this.getValue();
20722 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20723 e.dom.checked = false;
20725 if(e.dom.value == v){
20726 e.dom.checked = true;
20730 if(suppressEvent !== true){
20731 this.fireEvent('check', this, true);
20739 validate : function()
20741 if(this.getVisibilityEl().hasClass('hidden')){
20747 (this.inputType == 'radio' && this.validateRadio()) ||
20748 (this.inputType == 'checkbox' && this.validateCheckbox())
20754 this.markInvalid();
20758 validateRadio : function()
20760 if(this.getVisibilityEl().hasClass('hidden')){
20764 if(this.allowBlank){
20770 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20771 if(!e.dom.checked){
20783 validateCheckbox : function()
20786 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20787 //return (this.getValue() == this.inputValue) ? true : false;
20790 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20798 for(var i in group){
20799 if(group[i].el.isVisible(true)){
20807 for(var i in group){
20812 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20819 * Mark this field as valid
20821 markValid : function()
20825 this.fireEvent('valid', this);
20827 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20830 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20837 if(this.inputType == 'radio'){
20838 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20839 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20840 e.findParent('.form-group', false, true).addClass(_this.validClass);
20847 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20848 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20852 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20858 for(var i in group){
20859 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20860 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20865 * Mark this field as invalid
20866 * @param {String} msg The validation message
20868 markInvalid : function(msg)
20870 if(this.allowBlank){
20876 this.fireEvent('invalid', this, msg);
20878 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20881 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20885 label.markInvalid();
20888 if(this.inputType == 'radio'){
20889 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20890 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20891 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20898 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20899 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20903 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20909 for(var i in group){
20910 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20911 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20916 clearInvalid : function()
20918 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20920 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20922 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20924 if (label && label.iconEl) {
20925 label.iconEl.removeClass(label.validClass);
20926 label.iconEl.removeClass(label.invalidClass);
20930 disable : function()
20932 if(this.inputType != 'radio'){
20933 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20940 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20941 _this.getActionEl().addClass(this.disabledClass);
20942 e.dom.disabled = true;
20946 this.disabled = true;
20947 this.fireEvent("disable", this);
20951 enable : function()
20953 if(this.inputType != 'radio'){
20954 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20961 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20962 _this.getActionEl().removeClass(this.disabledClass);
20963 e.dom.disabled = false;
20967 this.disabled = false;
20968 this.fireEvent("enable", this);
20972 setBoxLabel : function(v)
20977 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20983 Roo.apply(Roo.bootstrap.CheckBox, {
20988 * register a CheckBox Group
20989 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20991 register : function(checkbox)
20993 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20994 this.groups[checkbox.groupId] = {};
20997 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21001 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21005 * fetch a CheckBox Group based on the group ID
21006 * @param {string} the group ID
21007 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21009 get: function(groupId) {
21010 if (typeof(this.groups[groupId]) == 'undefined') {
21014 return this.groups[groupId] ;
21027 * @class Roo.bootstrap.Radio
21028 * @extends Roo.bootstrap.Component
21029 * Bootstrap Radio class
21030 * @cfg {String} boxLabel - the label associated
21031 * @cfg {String} value - the value of radio
21034 * Create a new Radio
21035 * @param {Object} config The config object
21037 Roo.bootstrap.Radio = function(config){
21038 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21042 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21048 getAutoCreate : function()
21052 cls : 'form-group radio',
21057 html : this.boxLabel
21065 initEvents : function()
21067 this.parent().register(this);
21069 this.el.on('click', this.onClick, this);
21073 onClick : function(e)
21075 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21076 this.setChecked(true);
21080 setChecked : function(state, suppressEvent)
21082 this.parent().setValue(this.value, suppressEvent);
21086 setBoxLabel : function(v)
21091 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21106 * @class Roo.bootstrap.SecurePass
21107 * @extends Roo.bootstrap.Input
21108 * Bootstrap SecurePass class
21112 * Create a new SecurePass
21113 * @param {Object} config The config object
21116 Roo.bootstrap.SecurePass = function (config) {
21117 // these go here, so the translation tool can replace them..
21119 PwdEmpty: "Please type a password, and then retype it to confirm.",
21120 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21121 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21122 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21123 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21124 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21125 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21126 TooWeak: "Your password is Too Weak."
21128 this.meterLabel = "Password strength:";
21129 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21130 this.meterClass = [
21131 "roo-password-meter-tooweak",
21132 "roo-password-meter-weak",
21133 "roo-password-meter-medium",
21134 "roo-password-meter-strong",
21135 "roo-password-meter-grey"
21140 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21143 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21145 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21147 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21148 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21149 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21150 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21151 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21152 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21153 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21163 * @cfg {String/Object} Label for the strength meter (defaults to
21164 * 'Password strength:')
21169 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21170 * ['Weak', 'Medium', 'Strong'])
21173 pwdStrengths: false,
21186 initEvents: function ()
21188 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21190 if (this.el.is('input[type=password]') && Roo.isSafari) {
21191 this.el.on('keydown', this.SafariOnKeyDown, this);
21194 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21197 onRender: function (ct, position)
21199 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21200 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21201 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21203 this.trigger.createChild({
21208 cls: 'roo-password-meter-grey col-xs-12',
21211 //width: this.meterWidth + 'px'
21215 cls: 'roo-password-meter-text'
21221 if (this.hideTrigger) {
21222 this.trigger.setDisplayed(false);
21224 this.setSize(this.width || '', this.height || '');
21227 onDestroy: function ()
21229 if (this.trigger) {
21230 this.trigger.removeAllListeners();
21231 this.trigger.remove();
21234 this.wrap.remove();
21236 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21239 checkStrength: function ()
21241 var pwd = this.inputEl().getValue();
21242 if (pwd == this._lastPwd) {
21247 if (this.ClientSideStrongPassword(pwd)) {
21249 } else if (this.ClientSideMediumPassword(pwd)) {
21251 } else if (this.ClientSideWeakPassword(pwd)) {
21257 Roo.log('strength1: ' + strength);
21259 //var pm = this.trigger.child('div/div/div').dom;
21260 var pm = this.trigger.child('div/div');
21261 pm.removeClass(this.meterClass);
21262 pm.addClass(this.meterClass[strength]);
21265 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21267 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21269 this._lastPwd = pwd;
21273 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21275 this._lastPwd = '';
21277 var pm = this.trigger.child('div/div');
21278 pm.removeClass(this.meterClass);
21279 pm.addClass('roo-password-meter-grey');
21282 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21285 this.inputEl().dom.type='password';
21288 validateValue: function (value)
21291 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21294 if (value.length == 0) {
21295 if (this.allowBlank) {
21296 this.clearInvalid();
21300 this.markInvalid(this.errors.PwdEmpty);
21301 this.errorMsg = this.errors.PwdEmpty;
21309 if ('[\x21-\x7e]*'.match(value)) {
21310 this.markInvalid(this.errors.PwdBadChar);
21311 this.errorMsg = this.errors.PwdBadChar;
21314 if (value.length < 6) {
21315 this.markInvalid(this.errors.PwdShort);
21316 this.errorMsg = this.errors.PwdShort;
21319 if (value.length > 16) {
21320 this.markInvalid(this.errors.PwdLong);
21321 this.errorMsg = this.errors.PwdLong;
21325 if (this.ClientSideStrongPassword(value)) {
21327 } else if (this.ClientSideMediumPassword(value)) {
21329 } else if (this.ClientSideWeakPassword(value)) {
21336 if (strength < 2) {
21337 //this.markInvalid(this.errors.TooWeak);
21338 this.errorMsg = this.errors.TooWeak;
21343 console.log('strength2: ' + strength);
21345 //var pm = this.trigger.child('div/div/div').dom;
21347 var pm = this.trigger.child('div/div');
21348 pm.removeClass(this.meterClass);
21349 pm.addClass(this.meterClass[strength]);
21351 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21353 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21355 this.errorMsg = '';
21359 CharacterSetChecks: function (type)
21362 this.fResult = false;
21365 isctype: function (character, type)
21368 case this.kCapitalLetter:
21369 if (character >= 'A' && character <= 'Z') {
21374 case this.kSmallLetter:
21375 if (character >= 'a' && character <= 'z') {
21381 if (character >= '0' && character <= '9') {
21386 case this.kPunctuation:
21387 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21398 IsLongEnough: function (pwd, size)
21400 return !(pwd == null || isNaN(size) || pwd.length < size);
21403 SpansEnoughCharacterSets: function (word, nb)
21405 if (!this.IsLongEnough(word, nb))
21410 var characterSetChecks = new Array(
21411 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21412 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21415 for (var index = 0; index < word.length; ++index) {
21416 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21417 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21418 characterSetChecks[nCharSet].fResult = true;
21425 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21426 if (characterSetChecks[nCharSet].fResult) {
21431 if (nCharSets < nb) {
21437 ClientSideStrongPassword: function (pwd)
21439 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21442 ClientSideMediumPassword: function (pwd)
21444 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21447 ClientSideWeakPassword: function (pwd)
21449 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21452 })//<script type="text/javascript">
21455 * Based Ext JS Library 1.1.1
21456 * Copyright(c) 2006-2007, Ext JS, LLC.
21462 * @class Roo.HtmlEditorCore
21463 * @extends Roo.Component
21464 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21466 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21469 Roo.HtmlEditorCore = function(config){
21472 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21477 * @event initialize
21478 * Fires when the editor is fully initialized (including the iframe)
21479 * @param {Roo.HtmlEditorCore} this
21484 * Fires when the editor is first receives the focus. Any insertion must wait
21485 * until after this event.
21486 * @param {Roo.HtmlEditorCore} this
21490 * @event beforesync
21491 * Fires before the textarea is updated with content from the editor iframe. Return false
21492 * to cancel the sync.
21493 * @param {Roo.HtmlEditorCore} this
21494 * @param {String} html
21498 * @event beforepush
21499 * Fires before the iframe editor is updated with content from the textarea. Return false
21500 * to cancel the push.
21501 * @param {Roo.HtmlEditorCore} this
21502 * @param {String} html
21507 * Fires when the textarea is updated with content from the editor iframe.
21508 * @param {Roo.HtmlEditorCore} this
21509 * @param {String} html
21514 * Fires when the iframe editor is updated with content from the textarea.
21515 * @param {Roo.HtmlEditorCore} this
21516 * @param {String} html
21521 * @event editorevent
21522 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21523 * @param {Roo.HtmlEditorCore} this
21529 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21531 // defaults : white / black...
21532 this.applyBlacklists();
21539 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21543 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21549 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21554 * @cfg {Number} height (in pixels)
21558 * @cfg {Number} width (in pixels)
21563 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21566 stylesheets: false,
21571 // private properties
21572 validationEvent : false,
21574 initialized : false,
21576 sourceEditMode : false,
21577 onFocus : Roo.emptyFn,
21579 hideMode:'offsets',
21583 // blacklist + whitelisted elements..
21590 * Protected method that will not generally be called directly. It
21591 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21592 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21594 getDocMarkup : function(){
21598 // inherit styels from page...??
21599 if (this.stylesheets === false) {
21601 Roo.get(document.head).select('style').each(function(node) {
21602 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21605 Roo.get(document.head).select('link').each(function(node) {
21606 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21609 } else if (!this.stylesheets.length) {
21611 st = '<style type="text/css">' +
21612 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21615 st = '<style type="text/css">' +
21620 st += '<style type="text/css">' +
21621 'IMG { cursor: pointer } ' +
21624 var cls = 'roo-htmleditor-body';
21626 if(this.bodyCls.length){
21627 cls += ' ' + this.bodyCls;
21630 return '<html><head>' + st +
21631 //<style type="text/css">' +
21632 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21634 ' </head><body class="' + cls + '"></body></html>';
21638 onRender : function(ct, position)
21641 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21642 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21645 this.el.dom.style.border = '0 none';
21646 this.el.dom.setAttribute('tabIndex', -1);
21647 this.el.addClass('x-hidden hide');
21651 if(Roo.isIE){ // fix IE 1px bogus margin
21652 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21656 this.frameId = Roo.id();
21660 var iframe = this.owner.wrap.createChild({
21662 cls: 'form-control', // bootstrap..
21664 name: this.frameId,
21665 frameBorder : 'no',
21666 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21671 this.iframe = iframe.dom;
21673 this.assignDocWin();
21675 this.doc.designMode = 'on';
21678 this.doc.write(this.getDocMarkup());
21682 var task = { // must defer to wait for browser to be ready
21684 //console.log("run task?" + this.doc.readyState);
21685 this.assignDocWin();
21686 if(this.doc.body || this.doc.readyState == 'complete'){
21688 this.doc.designMode="on";
21692 Roo.TaskMgr.stop(task);
21693 this.initEditor.defer(10, this);
21700 Roo.TaskMgr.start(task);
21705 onResize : function(w, h)
21707 Roo.log('resize: ' +w + ',' + h );
21708 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21712 if(typeof w == 'number'){
21714 this.iframe.style.width = w + 'px';
21716 if(typeof h == 'number'){
21718 this.iframe.style.height = h + 'px';
21720 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21727 * Toggles the editor between standard and source edit mode.
21728 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21730 toggleSourceEdit : function(sourceEditMode){
21732 this.sourceEditMode = sourceEditMode === true;
21734 if(this.sourceEditMode){
21736 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21739 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21740 //this.iframe.className = '';
21743 //this.setSize(this.owner.wrap.getSize());
21744 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21751 * Protected method that will not generally be called directly. If you need/want
21752 * custom HTML cleanup, this is the method you should override.
21753 * @param {String} html The HTML to be cleaned
21754 * return {String} The cleaned HTML
21756 cleanHtml : function(html){
21757 html = String(html);
21758 if(html.length > 5){
21759 if(Roo.isSafari){ // strip safari nonsense
21760 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21763 if(html == ' '){
21770 * HTML Editor -> Textarea
21771 * Protected method that will not generally be called directly. Syncs the contents
21772 * of the editor iframe with the textarea.
21774 syncValue : function(){
21775 if(this.initialized){
21776 var bd = (this.doc.body || this.doc.documentElement);
21777 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21778 var html = bd.innerHTML;
21780 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21781 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21783 html = '<div style="'+m[0]+'">' + html + '</div>';
21786 html = this.cleanHtml(html);
21787 // fix up the special chars.. normaly like back quotes in word...
21788 // however we do not want to do this with chinese..
21789 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21790 var cc = b.charCodeAt();
21792 (cc >= 0x4E00 && cc < 0xA000 ) ||
21793 (cc >= 0x3400 && cc < 0x4E00 ) ||
21794 (cc >= 0xf900 && cc < 0xfb00 )
21800 if(this.owner.fireEvent('beforesync', this, html) !== false){
21801 this.el.dom.value = html;
21802 this.owner.fireEvent('sync', this, html);
21808 * Protected method that will not generally be called directly. Pushes the value of the textarea
21809 * into the iframe editor.
21811 pushValue : function(){
21812 if(this.initialized){
21813 var v = this.el.dom.value.trim();
21815 // if(v.length < 1){
21819 if(this.owner.fireEvent('beforepush', this, v) !== false){
21820 var d = (this.doc.body || this.doc.documentElement);
21822 this.cleanUpPaste();
21823 this.el.dom.value = d.innerHTML;
21824 this.owner.fireEvent('push', this, v);
21830 deferFocus : function(){
21831 this.focus.defer(10, this);
21835 focus : function(){
21836 if(this.win && !this.sourceEditMode){
21843 assignDocWin: function()
21845 var iframe = this.iframe;
21848 this.doc = iframe.contentWindow.document;
21849 this.win = iframe.contentWindow;
21851 // if (!Roo.get(this.frameId)) {
21854 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21855 // this.win = Roo.get(this.frameId).dom.contentWindow;
21857 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21861 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21862 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21867 initEditor : function(){
21868 //console.log("INIT EDITOR");
21869 this.assignDocWin();
21873 this.doc.designMode="on";
21875 this.doc.write(this.getDocMarkup());
21878 var dbody = (this.doc.body || this.doc.documentElement);
21879 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21880 // this copies styles from the containing element into thsi one..
21881 // not sure why we need all of this..
21882 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21884 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21885 //ss['background-attachment'] = 'fixed'; // w3c
21886 dbody.bgProperties = 'fixed'; // ie
21887 //Roo.DomHelper.applyStyles(dbody, ss);
21888 Roo.EventManager.on(this.doc, {
21889 //'mousedown': this.onEditorEvent,
21890 'mouseup': this.onEditorEvent,
21891 'dblclick': this.onEditorEvent,
21892 'click': this.onEditorEvent,
21893 'keyup': this.onEditorEvent,
21898 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21900 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21901 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21903 this.initialized = true;
21905 this.owner.fireEvent('initialize', this);
21910 onDestroy : function(){
21916 //for (var i =0; i < this.toolbars.length;i++) {
21917 // // fixme - ask toolbars for heights?
21918 // this.toolbars[i].onDestroy();
21921 //this.wrap.dom.innerHTML = '';
21922 //this.wrap.remove();
21927 onFirstFocus : function(){
21929 this.assignDocWin();
21932 this.activated = true;
21935 if(Roo.isGecko){ // prevent silly gecko errors
21937 var s = this.win.getSelection();
21938 if(!s.focusNode || s.focusNode.nodeType != 3){
21939 var r = s.getRangeAt(0);
21940 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21945 this.execCmd('useCSS', true);
21946 this.execCmd('styleWithCSS', false);
21949 this.owner.fireEvent('activate', this);
21953 adjustFont: function(btn){
21954 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21955 //if(Roo.isSafari){ // safari
21958 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21959 if(Roo.isSafari){ // safari
21960 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21961 v = (v < 10) ? 10 : v;
21962 v = (v > 48) ? 48 : v;
21963 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21968 v = Math.max(1, v+adjust);
21970 this.execCmd('FontSize', v );
21973 onEditorEvent : function(e)
21975 this.owner.fireEvent('editorevent', this, e);
21976 // this.updateToolbar();
21977 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21980 insertTag : function(tg)
21982 // could be a bit smarter... -> wrap the current selected tRoo..
21983 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21985 range = this.createRange(this.getSelection());
21986 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21987 wrappingNode.appendChild(range.extractContents());
21988 range.insertNode(wrappingNode);
21995 this.execCmd("formatblock", tg);
21999 insertText : function(txt)
22003 var range = this.createRange();
22004 range.deleteContents();
22005 //alert(Sender.getAttribute('label'));
22007 range.insertNode(this.doc.createTextNode(txt));
22013 * Executes a Midas editor command on the editor document and performs necessary focus and
22014 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22015 * @param {String} cmd The Midas command
22016 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22018 relayCmd : function(cmd, value){
22020 this.execCmd(cmd, value);
22021 this.owner.fireEvent('editorevent', this);
22022 //this.updateToolbar();
22023 this.owner.deferFocus();
22027 * Executes a Midas editor command directly on the editor document.
22028 * For visual commands, you should use {@link #relayCmd} instead.
22029 * <b>This should only be called after the editor is initialized.</b>
22030 * @param {String} cmd The Midas command
22031 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22033 execCmd : function(cmd, value){
22034 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22041 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22043 * @param {String} text | dom node..
22045 insertAtCursor : function(text)
22048 if(!this.activated){
22054 var r = this.doc.selection.createRange();
22065 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22069 // from jquery ui (MIT licenced)
22071 var win = this.win;
22073 if (win.getSelection && win.getSelection().getRangeAt) {
22074 range = win.getSelection().getRangeAt(0);
22075 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22076 range.insertNode(node);
22077 } else if (win.document.selection && win.document.selection.createRange) {
22078 // no firefox support
22079 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22080 win.document.selection.createRange().pasteHTML(txt);
22082 // no firefox support
22083 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22084 this.execCmd('InsertHTML', txt);
22093 mozKeyPress : function(e){
22095 var c = e.getCharCode(), cmd;
22098 c = String.fromCharCode(c).toLowerCase();
22112 this.cleanUpPaste.defer(100, this);
22120 e.preventDefault();
22128 fixKeys : function(){ // load time branching for fastest keydown performance
22130 return function(e){
22131 var k = e.getKey(), r;
22134 r = this.doc.selection.createRange();
22137 r.pasteHTML('    ');
22144 r = this.doc.selection.createRange();
22146 var target = r.parentElement();
22147 if(!target || target.tagName.toLowerCase() != 'li'){
22149 r.pasteHTML('<br />');
22155 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22156 this.cleanUpPaste.defer(100, this);
22162 }else if(Roo.isOpera){
22163 return function(e){
22164 var k = e.getKey();
22168 this.execCmd('InsertHTML','    ');
22171 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22172 this.cleanUpPaste.defer(100, this);
22177 }else if(Roo.isSafari){
22178 return function(e){
22179 var k = e.getKey();
22183 this.execCmd('InsertText','\t');
22187 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22188 this.cleanUpPaste.defer(100, this);
22196 getAllAncestors: function()
22198 var p = this.getSelectedNode();
22201 a.push(p); // push blank onto stack..
22202 p = this.getParentElement();
22206 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22210 a.push(this.doc.body);
22214 lastSelNode : false,
22217 getSelection : function()
22219 this.assignDocWin();
22220 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22223 getSelectedNode: function()
22225 // this may only work on Gecko!!!
22227 // should we cache this!!!!
22232 var range = this.createRange(this.getSelection()).cloneRange();
22235 var parent = range.parentElement();
22237 var testRange = range.duplicate();
22238 testRange.moveToElementText(parent);
22239 if (testRange.inRange(range)) {
22242 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22245 parent = parent.parentElement;
22250 // is ancestor a text element.
22251 var ac = range.commonAncestorContainer;
22252 if (ac.nodeType == 3) {
22253 ac = ac.parentNode;
22256 var ar = ac.childNodes;
22259 var other_nodes = [];
22260 var has_other_nodes = false;
22261 for (var i=0;i<ar.length;i++) {
22262 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22265 // fullly contained node.
22267 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22272 // probably selected..
22273 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22274 other_nodes.push(ar[i]);
22278 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22283 has_other_nodes = true;
22285 if (!nodes.length && other_nodes.length) {
22286 nodes= other_nodes;
22288 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22294 createRange: function(sel)
22296 // this has strange effects when using with
22297 // top toolbar - not sure if it's a great idea.
22298 //this.editor.contentWindow.focus();
22299 if (typeof sel != "undefined") {
22301 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22303 return this.doc.createRange();
22306 return this.doc.createRange();
22309 getParentElement: function()
22312 this.assignDocWin();
22313 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22315 var range = this.createRange(sel);
22318 var p = range.commonAncestorContainer;
22319 while (p.nodeType == 3) { // text node
22330 * Range intersection.. the hard stuff...
22334 * [ -- selected range --- ]
22338 * if end is before start or hits it. fail.
22339 * if start is after end or hits it fail.
22341 * if either hits (but other is outside. - then it's not
22347 // @see http://www.thismuchiknow.co.uk/?p=64.
22348 rangeIntersectsNode : function(range, node)
22350 var nodeRange = node.ownerDocument.createRange();
22352 nodeRange.selectNode(node);
22354 nodeRange.selectNodeContents(node);
22357 var rangeStartRange = range.cloneRange();
22358 rangeStartRange.collapse(true);
22360 var rangeEndRange = range.cloneRange();
22361 rangeEndRange.collapse(false);
22363 var nodeStartRange = nodeRange.cloneRange();
22364 nodeStartRange.collapse(true);
22366 var nodeEndRange = nodeRange.cloneRange();
22367 nodeEndRange.collapse(false);
22369 return rangeStartRange.compareBoundaryPoints(
22370 Range.START_TO_START, nodeEndRange) == -1 &&
22371 rangeEndRange.compareBoundaryPoints(
22372 Range.START_TO_START, nodeStartRange) == 1;
22376 rangeCompareNode : function(range, node)
22378 var nodeRange = node.ownerDocument.createRange();
22380 nodeRange.selectNode(node);
22382 nodeRange.selectNodeContents(node);
22386 range.collapse(true);
22388 nodeRange.collapse(true);
22390 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22391 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22393 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22395 var nodeIsBefore = ss == 1;
22396 var nodeIsAfter = ee == -1;
22398 if (nodeIsBefore && nodeIsAfter) {
22401 if (!nodeIsBefore && nodeIsAfter) {
22402 return 1; //right trailed.
22405 if (nodeIsBefore && !nodeIsAfter) {
22406 return 2; // left trailed.
22412 // private? - in a new class?
22413 cleanUpPaste : function()
22415 // cleans up the whole document..
22416 Roo.log('cleanuppaste');
22418 this.cleanUpChildren(this.doc.body);
22419 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22420 if (clean != this.doc.body.innerHTML) {
22421 this.doc.body.innerHTML = clean;
22426 cleanWordChars : function(input) {// change the chars to hex code
22427 var he = Roo.HtmlEditorCore;
22429 var output = input;
22430 Roo.each(he.swapCodes, function(sw) {
22431 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22433 output = output.replace(swapper, sw[1]);
22440 cleanUpChildren : function (n)
22442 if (!n.childNodes.length) {
22445 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22446 this.cleanUpChild(n.childNodes[i]);
22453 cleanUpChild : function (node)
22456 //console.log(node);
22457 if (node.nodeName == "#text") {
22458 // clean up silly Windows -- stuff?
22461 if (node.nodeName == "#comment") {
22462 node.parentNode.removeChild(node);
22463 // clean up silly Windows -- stuff?
22466 var lcname = node.tagName.toLowerCase();
22467 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22468 // whitelist of tags..
22470 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22472 node.parentNode.removeChild(node);
22477 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22479 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22480 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22482 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22483 // remove_keep_children = true;
22486 if (remove_keep_children) {
22487 this.cleanUpChildren(node);
22488 // inserts everything just before this node...
22489 while (node.childNodes.length) {
22490 var cn = node.childNodes[0];
22491 node.removeChild(cn);
22492 node.parentNode.insertBefore(cn, node);
22494 node.parentNode.removeChild(node);
22498 if (!node.attributes || !node.attributes.length) {
22499 this.cleanUpChildren(node);
22503 function cleanAttr(n,v)
22506 if (v.match(/^\./) || v.match(/^\//)) {
22509 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22512 if (v.match(/^#/)) {
22515 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22516 node.removeAttribute(n);
22520 var cwhite = this.cwhite;
22521 var cblack = this.cblack;
22523 function cleanStyle(n,v)
22525 if (v.match(/expression/)) { //XSS?? should we even bother..
22526 node.removeAttribute(n);
22530 var parts = v.split(/;/);
22533 Roo.each(parts, function(p) {
22534 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22538 var l = p.split(':').shift().replace(/\s+/g,'');
22539 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22541 if ( cwhite.length && cblack.indexOf(l) > -1) {
22542 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22543 //node.removeAttribute(n);
22547 // only allow 'c whitelisted system attributes'
22548 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22549 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22550 //node.removeAttribute(n);
22560 if (clean.length) {
22561 node.setAttribute(n, clean.join(';'));
22563 node.removeAttribute(n);
22569 for (var i = node.attributes.length-1; i > -1 ; i--) {
22570 var a = node.attributes[i];
22573 if (a.name.toLowerCase().substr(0,2)=='on') {
22574 node.removeAttribute(a.name);
22577 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22578 node.removeAttribute(a.name);
22581 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22582 cleanAttr(a.name,a.value); // fixme..
22585 if (a.name == 'style') {
22586 cleanStyle(a.name,a.value);
22589 /// clean up MS crap..
22590 // tecnically this should be a list of valid class'es..
22593 if (a.name == 'class') {
22594 if (a.value.match(/^Mso/)) {
22595 node.className = '';
22598 if (a.value.match(/^body$/)) {
22599 node.className = '';
22610 this.cleanUpChildren(node);
22616 * Clean up MS wordisms...
22618 cleanWord : function(node)
22623 this.cleanWord(this.doc.body);
22626 if (node.nodeName == "#text") {
22627 // clean up silly Windows -- stuff?
22630 if (node.nodeName == "#comment") {
22631 node.parentNode.removeChild(node);
22632 // clean up silly Windows -- stuff?
22636 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22637 node.parentNode.removeChild(node);
22641 // remove - but keep children..
22642 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22643 while (node.childNodes.length) {
22644 var cn = node.childNodes[0];
22645 node.removeChild(cn);
22646 node.parentNode.insertBefore(cn, node);
22648 node.parentNode.removeChild(node);
22649 this.iterateChildren(node, this.cleanWord);
22653 if (node.className.length) {
22655 var cn = node.className.split(/\W+/);
22657 Roo.each(cn, function(cls) {
22658 if (cls.match(/Mso[a-zA-Z]+/)) {
22663 node.className = cna.length ? cna.join(' ') : '';
22665 node.removeAttribute("class");
22669 if (node.hasAttribute("lang")) {
22670 node.removeAttribute("lang");
22673 if (node.hasAttribute("style")) {
22675 var styles = node.getAttribute("style").split(";");
22677 Roo.each(styles, function(s) {
22678 if (!s.match(/:/)) {
22681 var kv = s.split(":");
22682 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22685 // what ever is left... we allow.
22688 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22689 if (!nstyle.length) {
22690 node.removeAttribute('style');
22693 this.iterateChildren(node, this.cleanWord);
22699 * iterateChildren of a Node, calling fn each time, using this as the scole..
22700 * @param {DomNode} node node to iterate children of.
22701 * @param {Function} fn method of this class to call on each item.
22703 iterateChildren : function(node, fn)
22705 if (!node.childNodes.length) {
22708 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22709 fn.call(this, node.childNodes[i])
22715 * cleanTableWidths.
22717 * Quite often pasting from word etc.. results in tables with column and widths.
22718 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22721 cleanTableWidths : function(node)
22726 this.cleanTableWidths(this.doc.body);
22731 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22734 Roo.log(node.tagName);
22735 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22736 this.iterateChildren(node, this.cleanTableWidths);
22739 if (node.hasAttribute('width')) {
22740 node.removeAttribute('width');
22744 if (node.hasAttribute("style")) {
22747 var styles = node.getAttribute("style").split(";");
22749 Roo.each(styles, function(s) {
22750 if (!s.match(/:/)) {
22753 var kv = s.split(":");
22754 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22757 // what ever is left... we allow.
22760 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22761 if (!nstyle.length) {
22762 node.removeAttribute('style');
22766 this.iterateChildren(node, this.cleanTableWidths);
22774 domToHTML : function(currentElement, depth, nopadtext) {
22776 depth = depth || 0;
22777 nopadtext = nopadtext || false;
22779 if (!currentElement) {
22780 return this.domToHTML(this.doc.body);
22783 //Roo.log(currentElement);
22785 var allText = false;
22786 var nodeName = currentElement.nodeName;
22787 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22789 if (nodeName == '#text') {
22791 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22796 if (nodeName != 'BODY') {
22799 // Prints the node tagName, such as <A>, <IMG>, etc
22802 for(i = 0; i < currentElement.attributes.length;i++) {
22804 var aname = currentElement.attributes.item(i).name;
22805 if (!currentElement.attributes.item(i).value.length) {
22808 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22811 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22820 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22823 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22828 // Traverse the tree
22830 var currentElementChild = currentElement.childNodes.item(i);
22831 var allText = true;
22832 var innerHTML = '';
22834 while (currentElementChild) {
22835 // Formatting code (indent the tree so it looks nice on the screen)
22836 var nopad = nopadtext;
22837 if (lastnode == 'SPAN') {
22841 if (currentElementChild.nodeName == '#text') {
22842 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22843 toadd = nopadtext ? toadd : toadd.trim();
22844 if (!nopad && toadd.length > 80) {
22845 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22847 innerHTML += toadd;
22850 currentElementChild = currentElement.childNodes.item(i);
22856 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22858 // Recursively traverse the tree structure of the child node
22859 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22860 lastnode = currentElementChild.nodeName;
22862 currentElementChild=currentElement.childNodes.item(i);
22868 // The remaining code is mostly for formatting the tree
22869 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22874 ret+= "</"+tagName+">";
22880 applyBlacklists : function()
22882 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22883 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22887 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22888 if (b.indexOf(tag) > -1) {
22891 this.white.push(tag);
22895 Roo.each(w, function(tag) {
22896 if (b.indexOf(tag) > -1) {
22899 if (this.white.indexOf(tag) > -1) {
22902 this.white.push(tag);
22907 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22908 if (w.indexOf(tag) > -1) {
22911 this.black.push(tag);
22915 Roo.each(b, function(tag) {
22916 if (w.indexOf(tag) > -1) {
22919 if (this.black.indexOf(tag) > -1) {
22922 this.black.push(tag);
22927 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22928 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22932 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22933 if (b.indexOf(tag) > -1) {
22936 this.cwhite.push(tag);
22940 Roo.each(w, function(tag) {
22941 if (b.indexOf(tag) > -1) {
22944 if (this.cwhite.indexOf(tag) > -1) {
22947 this.cwhite.push(tag);
22952 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22953 if (w.indexOf(tag) > -1) {
22956 this.cblack.push(tag);
22960 Roo.each(b, function(tag) {
22961 if (w.indexOf(tag) > -1) {
22964 if (this.cblack.indexOf(tag) > -1) {
22967 this.cblack.push(tag);
22972 setStylesheets : function(stylesheets)
22974 if(typeof(stylesheets) == 'string'){
22975 Roo.get(this.iframe.contentDocument.head).createChild({
22977 rel : 'stylesheet',
22986 Roo.each(stylesheets, function(s) {
22991 Roo.get(_this.iframe.contentDocument.head).createChild({
22993 rel : 'stylesheet',
23002 removeStylesheets : function()
23006 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23011 setStyle : function(style)
23013 Roo.get(this.iframe.contentDocument.head).createChild({
23022 // hide stuff that is not compatible
23036 * @event specialkey
23040 * @cfg {String} fieldClass @hide
23043 * @cfg {String} focusClass @hide
23046 * @cfg {String} autoCreate @hide
23049 * @cfg {String} inputType @hide
23052 * @cfg {String} invalidClass @hide
23055 * @cfg {String} invalidText @hide
23058 * @cfg {String} msgFx @hide
23061 * @cfg {String} validateOnBlur @hide
23065 Roo.HtmlEditorCore.white = [
23066 'area', 'br', 'img', 'input', 'hr', 'wbr',
23068 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23069 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23070 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23071 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23072 'table', 'ul', 'xmp',
23074 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23077 'dir', 'menu', 'ol', 'ul', 'dl',
23083 Roo.HtmlEditorCore.black = [
23084 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23086 'base', 'basefont', 'bgsound', 'blink', 'body',
23087 'frame', 'frameset', 'head', 'html', 'ilayer',
23088 'iframe', 'layer', 'link', 'meta', 'object',
23089 'script', 'style' ,'title', 'xml' // clean later..
23091 Roo.HtmlEditorCore.clean = [
23092 'script', 'style', 'title', 'xml'
23094 Roo.HtmlEditorCore.remove = [
23099 Roo.HtmlEditorCore.ablack = [
23103 Roo.HtmlEditorCore.aclean = [
23104 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23108 Roo.HtmlEditorCore.pwhite= [
23109 'http', 'https', 'mailto'
23112 // white listed style attributes.
23113 Roo.HtmlEditorCore.cwhite= [
23114 // 'text-align', /// default is to allow most things..
23120 // black listed style attributes.
23121 Roo.HtmlEditorCore.cblack= [
23122 // 'font-size' -- this can be set by the project
23126 Roo.HtmlEditorCore.swapCodes =[
23145 * @class Roo.bootstrap.HtmlEditor
23146 * @extends Roo.bootstrap.TextArea
23147 * Bootstrap HtmlEditor class
23150 * Create a new HtmlEditor
23151 * @param {Object} config The config object
23154 Roo.bootstrap.HtmlEditor = function(config){
23155 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23156 if (!this.toolbars) {
23157 this.toolbars = [];
23160 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23163 * @event initialize
23164 * Fires when the editor is fully initialized (including the iframe)
23165 * @param {HtmlEditor} this
23170 * Fires when the editor is first receives the focus. Any insertion must wait
23171 * until after this event.
23172 * @param {HtmlEditor} this
23176 * @event beforesync
23177 * Fires before the textarea is updated with content from the editor iframe. Return false
23178 * to cancel the sync.
23179 * @param {HtmlEditor} this
23180 * @param {String} html
23184 * @event beforepush
23185 * Fires before the iframe editor is updated with content from the textarea. Return false
23186 * to cancel the push.
23187 * @param {HtmlEditor} this
23188 * @param {String} html
23193 * Fires when the textarea is updated with content from the editor iframe.
23194 * @param {HtmlEditor} this
23195 * @param {String} html
23200 * Fires when the iframe editor is updated with content from the textarea.
23201 * @param {HtmlEditor} this
23202 * @param {String} html
23206 * @event editmodechange
23207 * Fires when the editor switches edit modes
23208 * @param {HtmlEditor} this
23209 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23211 editmodechange: true,
23213 * @event editorevent
23214 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23215 * @param {HtmlEditor} this
23219 * @event firstfocus
23220 * Fires when on first focus - needed by toolbars..
23221 * @param {HtmlEditor} this
23226 * Auto save the htmlEditor value as a file into Events
23227 * @param {HtmlEditor} this
23231 * @event savedpreview
23232 * preview the saved version of htmlEditor
23233 * @param {HtmlEditor} this
23240 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23244 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23249 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23254 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23259 * @cfg {Number} height (in pixels)
23263 * @cfg {Number} width (in pixels)
23268 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23271 stylesheets: false,
23276 // private properties
23277 validationEvent : false,
23279 initialized : false,
23282 onFocus : Roo.emptyFn,
23284 hideMode:'offsets',
23286 tbContainer : false,
23290 toolbarContainer :function() {
23291 return this.wrap.select('.x-html-editor-tb',true).first();
23295 * Protected method that will not generally be called directly. It
23296 * is called when the editor creates its toolbar. Override this method if you need to
23297 * add custom toolbar buttons.
23298 * @param {HtmlEditor} editor
23300 createToolbar : function(){
23301 Roo.log('renewing');
23302 Roo.log("create toolbars");
23304 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23305 this.toolbars[0].render(this.toolbarContainer());
23309 // if (!editor.toolbars || !editor.toolbars.length) {
23310 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23313 // for (var i =0 ; i < editor.toolbars.length;i++) {
23314 // editor.toolbars[i] = Roo.factory(
23315 // typeof(editor.toolbars[i]) == 'string' ?
23316 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23317 // Roo.bootstrap.HtmlEditor);
23318 // editor.toolbars[i].init(editor);
23324 onRender : function(ct, position)
23326 // Roo.log("Call onRender: " + this.xtype);
23328 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23330 this.wrap = this.inputEl().wrap({
23331 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23334 this.editorcore.onRender(ct, position);
23336 if (this.resizable) {
23337 this.resizeEl = new Roo.Resizable(this.wrap, {
23341 minHeight : this.height,
23342 height: this.height,
23343 handles : this.resizable,
23346 resize : function(r, w, h) {
23347 _t.onResize(w,h); // -something
23353 this.createToolbar(this);
23356 if(!this.width && this.resizable){
23357 this.setSize(this.wrap.getSize());
23359 if (this.resizeEl) {
23360 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23361 // should trigger onReize..
23367 onResize : function(w, h)
23369 Roo.log('resize: ' +w + ',' + h );
23370 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23374 if(this.inputEl() ){
23375 if(typeof w == 'number'){
23376 var aw = w - this.wrap.getFrameWidth('lr');
23377 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23380 if(typeof h == 'number'){
23381 var tbh = -11; // fixme it needs to tool bar size!
23382 for (var i =0; i < this.toolbars.length;i++) {
23383 // fixme - ask toolbars for heights?
23384 tbh += this.toolbars[i].el.getHeight();
23385 //if (this.toolbars[i].footer) {
23386 // tbh += this.toolbars[i].footer.el.getHeight();
23394 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23395 ah -= 5; // knock a few pixes off for look..
23396 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23400 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23401 this.editorcore.onResize(ew,eh);
23406 * Toggles the editor between standard and source edit mode.
23407 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23409 toggleSourceEdit : function(sourceEditMode)
23411 this.editorcore.toggleSourceEdit(sourceEditMode);
23413 if(this.editorcore.sourceEditMode){
23414 Roo.log('editor - showing textarea');
23417 // Roo.log(this.syncValue());
23419 this.inputEl().removeClass(['hide', 'x-hidden']);
23420 this.inputEl().dom.removeAttribute('tabIndex');
23421 this.inputEl().focus();
23423 Roo.log('editor - hiding textarea');
23425 // Roo.log(this.pushValue());
23428 this.inputEl().addClass(['hide', 'x-hidden']);
23429 this.inputEl().dom.setAttribute('tabIndex', -1);
23430 //this.deferFocus();
23433 if(this.resizable){
23434 this.setSize(this.wrap.getSize());
23437 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23440 // private (for BoxComponent)
23441 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23443 // private (for BoxComponent)
23444 getResizeEl : function(){
23448 // private (for BoxComponent)
23449 getPositionEl : function(){
23454 initEvents : function(){
23455 this.originalValue = this.getValue();
23459 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23462 // markInvalid : Roo.emptyFn,
23464 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23467 // clearInvalid : Roo.emptyFn,
23469 setValue : function(v){
23470 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23471 this.editorcore.pushValue();
23476 deferFocus : function(){
23477 this.focus.defer(10, this);
23481 focus : function(){
23482 this.editorcore.focus();
23488 onDestroy : function(){
23494 for (var i =0; i < this.toolbars.length;i++) {
23495 // fixme - ask toolbars for heights?
23496 this.toolbars[i].onDestroy();
23499 this.wrap.dom.innerHTML = '';
23500 this.wrap.remove();
23505 onFirstFocus : function(){
23506 //Roo.log("onFirstFocus");
23507 this.editorcore.onFirstFocus();
23508 for (var i =0; i < this.toolbars.length;i++) {
23509 this.toolbars[i].onFirstFocus();
23515 syncValue : function()
23517 this.editorcore.syncValue();
23520 pushValue : function()
23522 this.editorcore.pushValue();
23526 // hide stuff that is not compatible
23540 * @event specialkey
23544 * @cfg {String} fieldClass @hide
23547 * @cfg {String} focusClass @hide
23550 * @cfg {String} autoCreate @hide
23553 * @cfg {String} inputType @hide
23556 * @cfg {String} invalidClass @hide
23559 * @cfg {String} invalidText @hide
23562 * @cfg {String} msgFx @hide
23565 * @cfg {String} validateOnBlur @hide
23574 Roo.namespace('Roo.bootstrap.htmleditor');
23576 * @class Roo.bootstrap.HtmlEditorToolbar1
23581 new Roo.bootstrap.HtmlEditor({
23584 new Roo.bootstrap.HtmlEditorToolbar1({
23585 disable : { fonts: 1 , format: 1, ..., ... , ...],
23591 * @cfg {Object} disable List of elements to disable..
23592 * @cfg {Array} btns List of additional buttons.
23596 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23599 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23602 Roo.apply(this, config);
23604 // default disabled, based on 'good practice'..
23605 this.disable = this.disable || {};
23606 Roo.applyIf(this.disable, {
23609 specialElements : true
23611 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23613 this.editor = config.editor;
23614 this.editorcore = config.editor.editorcore;
23616 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23618 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23619 // dont call parent... till later.
23621 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23626 editorcore : false,
23631 "h1","h2","h3","h4","h5","h6",
23633 "abbr", "acronym", "address", "cite", "samp", "var",
23637 onRender : function(ct, position)
23639 // Roo.log("Call onRender: " + this.xtype);
23641 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23643 this.el.dom.style.marginBottom = '0';
23645 var editorcore = this.editorcore;
23646 var editor= this.editor;
23649 var btn = function(id,cmd , toggle, handler, html){
23651 var event = toggle ? 'toggle' : 'click';
23656 xns: Roo.bootstrap,
23659 enableToggle:toggle !== false,
23661 pressed : toggle ? false : null,
23664 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23665 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23671 // var cb_box = function...
23676 xns: Roo.bootstrap,
23677 glyphicon : 'font',
23681 xns: Roo.bootstrap,
23685 Roo.each(this.formats, function(f) {
23686 style.menu.items.push({
23688 xns: Roo.bootstrap,
23689 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23694 editorcore.insertTag(this.tagname);
23701 children.push(style);
23703 btn('bold',false,true);
23704 btn('italic',false,true);
23705 btn('align-left', 'justifyleft',true);
23706 btn('align-center', 'justifycenter',true);
23707 btn('align-right' , 'justifyright',true);
23708 btn('link', false, false, function(btn) {
23709 //Roo.log("create link?");
23710 var url = prompt(this.createLinkText, this.defaultLinkValue);
23711 if(url && url != 'http:/'+'/'){
23712 this.editorcore.relayCmd('createlink', url);
23715 btn('list','insertunorderedlist',true);
23716 btn('pencil', false,true, function(btn){
23718 this.toggleSourceEdit(btn.pressed);
23721 if (this.editor.btns.length > 0) {
23722 for (var i = 0; i<this.editor.btns.length; i++) {
23723 children.push(this.editor.btns[i]);
23731 xns: Roo.bootstrap,
23736 xns: Roo.bootstrap,
23741 cog.menu.items.push({
23743 xns: Roo.bootstrap,
23744 html : Clean styles,
23749 editorcore.insertTag(this.tagname);
23758 this.xtype = 'NavSimplebar';
23760 for(var i=0;i< children.length;i++) {
23762 this.buttons.add(this.addxtypeChild(children[i]));
23766 editor.on('editorevent', this.updateToolbar, this);
23768 onBtnClick : function(id)
23770 this.editorcore.relayCmd(id);
23771 this.editorcore.focus();
23775 * Protected method that will not generally be called directly. It triggers
23776 * a toolbar update by reading the markup state of the current selection in the editor.
23778 updateToolbar: function(){
23780 if(!this.editorcore.activated){
23781 this.editor.onFirstFocus(); // is this neeed?
23785 var btns = this.buttons;
23786 var doc = this.editorcore.doc;
23787 btns.get('bold').setActive(doc.queryCommandState('bold'));
23788 btns.get('italic').setActive(doc.queryCommandState('italic'));
23789 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23791 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23792 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23793 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23795 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23796 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23799 var ans = this.editorcore.getAllAncestors();
23800 if (this.formatCombo) {
23803 var store = this.formatCombo.store;
23804 this.formatCombo.setValue("");
23805 for (var i =0; i < ans.length;i++) {
23806 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23808 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23816 // hides menus... - so this cant be on a menu...
23817 Roo.bootstrap.MenuMgr.hideAll();
23819 Roo.bootstrap.MenuMgr.hideAll();
23820 //this.editorsyncValue();
23822 onFirstFocus: function() {
23823 this.buttons.each(function(item){
23827 toggleSourceEdit : function(sourceEditMode){
23830 if(sourceEditMode){
23831 Roo.log("disabling buttons");
23832 this.buttons.each( function(item){
23833 if(item.cmd != 'pencil'){
23839 Roo.log("enabling buttons");
23840 if(this.editorcore.initialized){
23841 this.buttons.each( function(item){
23847 Roo.log("calling toggole on editor");
23848 // tell the editor that it's been pressed..
23849 this.editor.toggleSourceEdit(sourceEditMode);
23859 * @class Roo.bootstrap.Table.AbstractSelectionModel
23860 * @extends Roo.util.Observable
23861 * Abstract base class for grid SelectionModels. It provides the interface that should be
23862 * implemented by descendant classes. This class should not be directly instantiated.
23865 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23866 this.locked = false;
23867 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23871 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23872 /** @ignore Called by the grid automatically. Do not call directly. */
23873 init : function(grid){
23879 * Locks the selections.
23882 this.locked = true;
23886 * Unlocks the selections.
23888 unlock : function(){
23889 this.locked = false;
23893 * Returns true if the selections are locked.
23894 * @return {Boolean}
23896 isLocked : function(){
23897 return this.locked;
23901 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23902 * @class Roo.bootstrap.Table.RowSelectionModel
23903 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23904 * It supports multiple selections and keyboard selection/navigation.
23906 * @param {Object} config
23909 Roo.bootstrap.Table.RowSelectionModel = function(config){
23910 Roo.apply(this, config);
23911 this.selections = new Roo.util.MixedCollection(false, function(o){
23916 this.lastActive = false;
23920 * @event selectionchange
23921 * Fires when the selection changes
23922 * @param {SelectionModel} this
23924 "selectionchange" : true,
23926 * @event afterselectionchange
23927 * Fires after the selection changes (eg. by key press or clicking)
23928 * @param {SelectionModel} this
23930 "afterselectionchange" : true,
23932 * @event beforerowselect
23933 * Fires when a row is selected being selected, return false to cancel.
23934 * @param {SelectionModel} this
23935 * @param {Number} rowIndex The selected index
23936 * @param {Boolean} keepExisting False if other selections will be cleared
23938 "beforerowselect" : true,
23941 * Fires when a row is selected.
23942 * @param {SelectionModel} this
23943 * @param {Number} rowIndex The selected index
23944 * @param {Roo.data.Record} r The record
23946 "rowselect" : true,
23948 * @event rowdeselect
23949 * Fires when a row is deselected.
23950 * @param {SelectionModel} this
23951 * @param {Number} rowIndex The selected index
23953 "rowdeselect" : true
23955 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23956 this.locked = false;
23959 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
23961 * @cfg {Boolean} singleSelect
23962 * True to allow selection of only one row at a time (defaults to false)
23964 singleSelect : false,
23967 initEvents : function()
23970 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23971 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
23972 //}else{ // allow click to work like normal
23973 // this.grid.on("rowclick", this.handleDragableRowClick, this);
23975 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23976 this.grid.on("rowclick", this.handleMouseDown, this);
23978 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23979 "up" : function(e){
23981 this.selectPrevious(e.shiftKey);
23982 }else if(this.last !== false && this.lastActive !== false){
23983 var last = this.last;
23984 this.selectRange(this.last, this.lastActive-1);
23985 this.grid.getView().focusRow(this.lastActive);
23986 if(last !== false){
23990 this.selectFirstRow();
23992 this.fireEvent("afterselectionchange", this);
23994 "down" : function(e){
23996 this.selectNext(e.shiftKey);
23997 }else if(this.last !== false && this.lastActive !== false){
23998 var last = this.last;
23999 this.selectRange(this.last, this.lastActive+1);
24000 this.grid.getView().focusRow(this.lastActive);
24001 if(last !== false){
24005 this.selectFirstRow();
24007 this.fireEvent("afterselectionchange", this);
24011 this.grid.store.on('load', function(){
24012 this.selections.clear();
24015 var view = this.grid.view;
24016 view.on("refresh", this.onRefresh, this);
24017 view.on("rowupdated", this.onRowUpdated, this);
24018 view.on("rowremoved", this.onRemove, this);
24023 onRefresh : function()
24025 var ds = this.grid.store, i, v = this.grid.view;
24026 var s = this.selections;
24027 s.each(function(r){
24028 if((i = ds.indexOfId(r.id)) != -1){
24037 onRemove : function(v, index, r){
24038 this.selections.remove(r);
24042 onRowUpdated : function(v, index, r){
24043 if(this.isSelected(r)){
24044 v.onRowSelect(index);
24050 * @param {Array} records The records to select
24051 * @param {Boolean} keepExisting (optional) True to keep existing selections
24053 selectRecords : function(records, keepExisting)
24056 this.clearSelections();
24058 var ds = this.grid.store;
24059 for(var i = 0, len = records.length; i < len; i++){
24060 this.selectRow(ds.indexOf(records[i]), true);
24065 * Gets the number of selected rows.
24068 getCount : function(){
24069 return this.selections.length;
24073 * Selects the first row in the grid.
24075 selectFirstRow : function(){
24080 * Select the last row.
24081 * @param {Boolean} keepExisting (optional) True to keep existing selections
24083 selectLastRow : function(keepExisting){
24084 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24085 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24089 * Selects the row immediately following the last selected row.
24090 * @param {Boolean} keepExisting (optional) True to keep existing selections
24092 selectNext : function(keepExisting)
24094 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24095 this.selectRow(this.last+1, keepExisting);
24096 this.grid.getView().focusRow(this.last);
24101 * Selects the row that precedes the last selected row.
24102 * @param {Boolean} keepExisting (optional) True to keep existing selections
24104 selectPrevious : function(keepExisting){
24106 this.selectRow(this.last-1, keepExisting);
24107 this.grid.getView().focusRow(this.last);
24112 * Returns the selected records
24113 * @return {Array} Array of selected records
24115 getSelections : function(){
24116 return [].concat(this.selections.items);
24120 * Returns the first selected record.
24123 getSelected : function(){
24124 return this.selections.itemAt(0);
24129 * Clears all selections.
24131 clearSelections : function(fast)
24137 var ds = this.grid.store;
24138 var s = this.selections;
24139 s.each(function(r){
24140 this.deselectRow(ds.indexOfId(r.id));
24144 this.selections.clear();
24151 * Selects all rows.
24153 selectAll : function(){
24157 this.selections.clear();
24158 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24159 this.selectRow(i, true);
24164 * Returns True if there is a selection.
24165 * @return {Boolean}
24167 hasSelection : function(){
24168 return this.selections.length > 0;
24172 * Returns True if the specified row is selected.
24173 * @param {Number/Record} record The record or index of the record to check
24174 * @return {Boolean}
24176 isSelected : function(index){
24177 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24178 return (r && this.selections.key(r.id) ? true : false);
24182 * Returns True if the specified record id is selected.
24183 * @param {String} id The id of record to check
24184 * @return {Boolean}
24186 isIdSelected : function(id){
24187 return (this.selections.key(id) ? true : false);
24192 handleMouseDBClick : function(e, t){
24196 handleMouseDown : function(e, t)
24198 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24199 if(this.isLocked() || rowIndex < 0 ){
24202 if(e.shiftKey && this.last !== false){
24203 var last = this.last;
24204 this.selectRange(last, rowIndex, e.ctrlKey);
24205 this.last = last; // reset the last
24209 var isSelected = this.isSelected(rowIndex);
24210 //Roo.log("select row:" + rowIndex);
24212 this.deselectRow(rowIndex);
24214 this.selectRow(rowIndex, true);
24218 if(e.button !== 0 && isSelected){
24219 alert('rowIndex 2: ' + rowIndex);
24220 view.focusRow(rowIndex);
24221 }else if(e.ctrlKey && isSelected){
24222 this.deselectRow(rowIndex);
24223 }else if(!isSelected){
24224 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24225 view.focusRow(rowIndex);
24229 this.fireEvent("afterselectionchange", this);
24232 handleDragableRowClick : function(grid, rowIndex, e)
24234 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24235 this.selectRow(rowIndex, false);
24236 grid.view.focusRow(rowIndex);
24237 this.fireEvent("afterselectionchange", this);
24242 * Selects multiple rows.
24243 * @param {Array} rows Array of the indexes of the row to select
24244 * @param {Boolean} keepExisting (optional) True to keep existing selections
24246 selectRows : function(rows, keepExisting){
24248 this.clearSelections();
24250 for(var i = 0, len = rows.length; i < len; i++){
24251 this.selectRow(rows[i], true);
24256 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24257 * @param {Number} startRow The index of the first row in the range
24258 * @param {Number} endRow The index of the last row in the range
24259 * @param {Boolean} keepExisting (optional) True to retain existing selections
24261 selectRange : function(startRow, endRow, keepExisting){
24266 this.clearSelections();
24268 if(startRow <= endRow){
24269 for(var i = startRow; i <= endRow; i++){
24270 this.selectRow(i, true);
24273 for(var i = startRow; i >= endRow; i--){
24274 this.selectRow(i, true);
24280 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24281 * @param {Number} startRow The index of the first row in the range
24282 * @param {Number} endRow The index of the last row in the range
24284 deselectRange : function(startRow, endRow, preventViewNotify){
24288 for(var i = startRow; i <= endRow; i++){
24289 this.deselectRow(i, preventViewNotify);
24295 * @param {Number} row The index of the row to select
24296 * @param {Boolean} keepExisting (optional) True to keep existing selections
24298 selectRow : function(index, keepExisting, preventViewNotify)
24300 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24303 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24304 if(!keepExisting || this.singleSelect){
24305 this.clearSelections();
24308 var r = this.grid.store.getAt(index);
24309 //console.log('selectRow - record id :' + r.id);
24311 this.selections.add(r);
24312 this.last = this.lastActive = index;
24313 if(!preventViewNotify){
24314 var proxy = new Roo.Element(
24315 this.grid.getRowDom(index)
24317 proxy.addClass('bg-info info');
24319 this.fireEvent("rowselect", this, index, r);
24320 this.fireEvent("selectionchange", this);
24326 * @param {Number} row The index of the row to deselect
24328 deselectRow : function(index, preventViewNotify)
24333 if(this.last == index){
24336 if(this.lastActive == index){
24337 this.lastActive = false;
24340 var r = this.grid.store.getAt(index);
24345 this.selections.remove(r);
24346 //.console.log('deselectRow - record id :' + r.id);
24347 if(!preventViewNotify){
24349 var proxy = new Roo.Element(
24350 this.grid.getRowDom(index)
24352 proxy.removeClass('bg-info info');
24354 this.fireEvent("rowdeselect", this, index);
24355 this.fireEvent("selectionchange", this);
24359 restoreLast : function(){
24361 this.last = this._last;
24366 acceptsNav : function(row, col, cm){
24367 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24371 onEditorKey : function(field, e){
24372 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24377 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24379 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24381 }else if(k == e.ENTER && !e.ctrlKey){
24385 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24387 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24389 }else if(k == e.ESC){
24393 g.startEditing(newCell[0], newCell[1]);
24399 * Ext JS Library 1.1.1
24400 * Copyright(c) 2006-2007, Ext JS, LLC.
24402 * Originally Released Under LGPL - original licence link has changed is not relivant.
24405 * <script type="text/javascript">
24409 * @class Roo.bootstrap.PagingToolbar
24410 * @extends Roo.bootstrap.NavSimplebar
24411 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24413 * Create a new PagingToolbar
24414 * @param {Object} config The config object
24415 * @param {Roo.data.Store} store
24417 Roo.bootstrap.PagingToolbar = function(config)
24419 // old args format still supported... - xtype is prefered..
24420 // created from xtype...
24422 this.ds = config.dataSource;
24424 if (config.store && !this.ds) {
24425 this.store= Roo.factory(config.store, Roo.data);
24426 this.ds = this.store;
24427 this.ds.xmodule = this.xmodule || false;
24430 this.toolbarItems = [];
24431 if (config.items) {
24432 this.toolbarItems = config.items;
24435 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24440 this.bind(this.ds);
24443 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24447 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24449 * @cfg {Roo.data.Store} dataSource
24450 * The underlying data store providing the paged data
24453 * @cfg {String/HTMLElement/Element} container
24454 * container The id or element that will contain the toolbar
24457 * @cfg {Boolean} displayInfo
24458 * True to display the displayMsg (defaults to false)
24461 * @cfg {Number} pageSize
24462 * The number of records to display per page (defaults to 20)
24466 * @cfg {String} displayMsg
24467 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24469 displayMsg : 'Displaying {0} - {1} of {2}',
24471 * @cfg {String} emptyMsg
24472 * The message to display when no records are found (defaults to "No data to display")
24474 emptyMsg : 'No data to display',
24476 * Customizable piece of the default paging text (defaults to "Page")
24479 beforePageText : "Page",
24481 * Customizable piece of the default paging text (defaults to "of %0")
24484 afterPageText : "of {0}",
24486 * Customizable piece of the default paging text (defaults to "First Page")
24489 firstText : "First Page",
24491 * Customizable piece of the default paging text (defaults to "Previous Page")
24494 prevText : "Previous Page",
24496 * Customizable piece of the default paging text (defaults to "Next Page")
24499 nextText : "Next Page",
24501 * Customizable piece of the default paging text (defaults to "Last Page")
24504 lastText : "Last Page",
24506 * Customizable piece of the default paging text (defaults to "Refresh")
24509 refreshText : "Refresh",
24513 onRender : function(ct, position)
24515 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24516 this.navgroup.parentId = this.id;
24517 this.navgroup.onRender(this.el, null);
24518 // add the buttons to the navgroup
24520 if(this.displayInfo){
24521 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24522 this.displayEl = this.el.select('.x-paging-info', true).first();
24523 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24524 // this.displayEl = navel.el.select('span',true).first();
24530 Roo.each(_this.buttons, function(e){ // this might need to use render????
24531 Roo.factory(e).onRender(_this.el, null);
24535 Roo.each(_this.toolbarItems, function(e) {
24536 _this.navgroup.addItem(e);
24540 this.first = this.navgroup.addItem({
24541 tooltip: this.firstText,
24543 icon : 'fa fa-backward',
24545 preventDefault: true,
24546 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24549 this.prev = this.navgroup.addItem({
24550 tooltip: this.prevText,
24552 icon : 'fa fa-step-backward',
24554 preventDefault: true,
24555 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24557 //this.addSeparator();
24560 var field = this.navgroup.addItem( {
24562 cls : 'x-paging-position',
24564 html : this.beforePageText +
24565 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24566 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24569 this.field = field.el.select('input', true).first();
24570 this.field.on("keydown", this.onPagingKeydown, this);
24571 this.field.on("focus", function(){this.dom.select();});
24574 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24575 //this.field.setHeight(18);
24576 //this.addSeparator();
24577 this.next = this.navgroup.addItem({
24578 tooltip: this.nextText,
24580 html : ' <i class="fa fa-step-forward">',
24582 preventDefault: true,
24583 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24585 this.last = this.navgroup.addItem({
24586 tooltip: this.lastText,
24587 icon : 'fa fa-forward',
24590 preventDefault: true,
24591 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24593 //this.addSeparator();
24594 this.loading = this.navgroup.addItem({
24595 tooltip: this.refreshText,
24596 icon: 'fa fa-refresh',
24597 preventDefault: true,
24598 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24604 updateInfo : function(){
24605 if(this.displayEl){
24606 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24607 var msg = count == 0 ?
24611 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24613 this.displayEl.update(msg);
24618 onLoad : function(ds, r, o)
24620 this.cursor = o.params.start ? o.params.start : 0;
24622 var d = this.getPageData(),
24627 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24628 this.field.dom.value = ap;
24629 this.first.setDisabled(ap == 1);
24630 this.prev.setDisabled(ap == 1);
24631 this.next.setDisabled(ap == ps);
24632 this.last.setDisabled(ap == ps);
24633 this.loading.enable();
24638 getPageData : function(){
24639 var total = this.ds.getTotalCount();
24642 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24643 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24648 onLoadError : function(){
24649 this.loading.enable();
24653 onPagingKeydown : function(e){
24654 var k = e.getKey();
24655 var d = this.getPageData();
24657 var v = this.field.dom.value, pageNum;
24658 if(!v || isNaN(pageNum = parseInt(v, 10))){
24659 this.field.dom.value = d.activePage;
24662 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24663 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24666 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))
24668 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24669 this.field.dom.value = pageNum;
24670 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24673 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24675 var v = this.field.dom.value, pageNum;
24676 var increment = (e.shiftKey) ? 10 : 1;
24677 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24680 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24681 this.field.dom.value = d.activePage;
24684 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24686 this.field.dom.value = parseInt(v, 10) + increment;
24687 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24688 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24695 beforeLoad : function(){
24697 this.loading.disable();
24702 onClick : function(which){
24711 ds.load({params:{start: 0, limit: this.pageSize}});
24714 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24717 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24720 var total = ds.getTotalCount();
24721 var extra = total % this.pageSize;
24722 var lastStart = extra ? (total - extra) : total-this.pageSize;
24723 ds.load({params:{start: lastStart, limit: this.pageSize}});
24726 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24732 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24733 * @param {Roo.data.Store} store The data store to unbind
24735 unbind : function(ds){
24736 ds.un("beforeload", this.beforeLoad, this);
24737 ds.un("load", this.onLoad, this);
24738 ds.un("loadexception", this.onLoadError, this);
24739 ds.un("remove", this.updateInfo, this);
24740 ds.un("add", this.updateInfo, this);
24741 this.ds = undefined;
24745 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24746 * @param {Roo.data.Store} store The data store to bind
24748 bind : function(ds){
24749 ds.on("beforeload", this.beforeLoad, this);
24750 ds.on("load", this.onLoad, this);
24751 ds.on("loadexception", this.onLoadError, this);
24752 ds.on("remove", this.updateInfo, this);
24753 ds.on("add", this.updateInfo, this);
24764 * @class Roo.bootstrap.MessageBar
24765 * @extends Roo.bootstrap.Component
24766 * Bootstrap MessageBar class
24767 * @cfg {String} html contents of the MessageBar
24768 * @cfg {String} weight (info | success | warning | danger) default info
24769 * @cfg {String} beforeClass insert the bar before the given class
24770 * @cfg {Boolean} closable (true | false) default false
24771 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24774 * Create a new Element
24775 * @param {Object} config The config object
24778 Roo.bootstrap.MessageBar = function(config){
24779 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24782 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24788 beforeClass: 'bootstrap-sticky-wrap',
24790 getAutoCreate : function(){
24794 cls: 'alert alert-dismissable alert-' + this.weight,
24799 html: this.html || ''
24805 cfg.cls += ' alert-messages-fixed';
24819 onRender : function(ct, position)
24821 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24824 var cfg = Roo.apply({}, this.getAutoCreate());
24828 cfg.cls += ' ' + this.cls;
24831 cfg.style = this.style;
24833 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24835 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24838 this.el.select('>button.close').on('click', this.hide, this);
24844 if (!this.rendered) {
24850 this.fireEvent('show', this);
24856 if (!this.rendered) {
24862 this.fireEvent('hide', this);
24865 update : function()
24867 // var e = this.el.dom.firstChild;
24869 // if(this.closable){
24870 // e = e.nextSibling;
24873 // e.data = this.html || '';
24875 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24891 * @class Roo.bootstrap.Graph
24892 * @extends Roo.bootstrap.Component
24893 * Bootstrap Graph class
24897 @cfg {String} graphtype bar | vbar | pie
24898 @cfg {number} g_x coodinator | centre x (pie)
24899 @cfg {number} g_y coodinator | centre y (pie)
24900 @cfg {number} g_r radius (pie)
24901 @cfg {number} g_height height of the chart (respected by all elements in the set)
24902 @cfg {number} g_width width of the chart (respected by all elements in the set)
24903 @cfg {Object} title The title of the chart
24906 -opts (object) options for the chart
24908 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24909 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24911 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.
24912 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24914 o stretch (boolean)
24916 -opts (object) options for the pie
24919 o startAngle (number)
24920 o endAngle (number)
24924 * Create a new Input
24925 * @param {Object} config The config object
24928 Roo.bootstrap.Graph = function(config){
24929 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24935 * The img click event for the img.
24936 * @param {Roo.EventObject} e
24942 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24953 //g_colors: this.colors,
24960 getAutoCreate : function(){
24971 onRender : function(ct,position){
24974 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24976 if (typeof(Raphael) == 'undefined') {
24977 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24981 this.raphael = Raphael(this.el.dom);
24983 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24984 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24985 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24986 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24988 r.text(160, 10, "Single Series Chart").attr(txtattr);
24989 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24990 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24991 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24993 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24994 r.barchart(330, 10, 300, 220, data1);
24995 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24996 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24999 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25000 // r.barchart(30, 30, 560, 250, xdata, {
25001 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25002 // axis : "0 0 1 1",
25003 // axisxlabels : xdata
25004 // //yvalues : cols,
25007 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25009 // this.load(null,xdata,{
25010 // axis : "0 0 1 1",
25011 // axisxlabels : xdata
25016 load : function(graphtype,xdata,opts)
25018 this.raphael.clear();
25020 graphtype = this.graphtype;
25025 var r = this.raphael,
25026 fin = function () {
25027 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25029 fout = function () {
25030 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25032 pfin = function() {
25033 this.sector.stop();
25034 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25037 this.label[0].stop();
25038 this.label[0].attr({ r: 7.5 });
25039 this.label[1].attr({ "font-weight": 800 });
25042 pfout = function() {
25043 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25046 this.label[0].animate({ r: 5 }, 500, "bounce");
25047 this.label[1].attr({ "font-weight": 400 });
25053 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25056 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25059 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25060 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25062 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25069 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25074 setTitle: function(o)
25079 initEvents: function() {
25082 this.el.on('click', this.onClick, this);
25086 onClick : function(e)
25088 Roo.log('img onclick');
25089 this.fireEvent('click', this, e);
25101 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25104 * @class Roo.bootstrap.dash.NumberBox
25105 * @extends Roo.bootstrap.Component
25106 * Bootstrap NumberBox class
25107 * @cfg {String} headline Box headline
25108 * @cfg {String} content Box content
25109 * @cfg {String} icon Box icon
25110 * @cfg {String} footer Footer text
25111 * @cfg {String} fhref Footer href
25114 * Create a new NumberBox
25115 * @param {Object} config The config object
25119 Roo.bootstrap.dash.NumberBox = function(config){
25120 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25124 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25133 getAutoCreate : function(){
25137 cls : 'small-box ',
25145 cls : 'roo-headline',
25146 html : this.headline
25150 cls : 'roo-content',
25151 html : this.content
25165 cls : 'ion ' + this.icon
25174 cls : 'small-box-footer',
25175 href : this.fhref || '#',
25179 cfg.cn.push(footer);
25186 onRender : function(ct,position){
25187 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25194 setHeadline: function (value)
25196 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25199 setFooter: function (value, href)
25201 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25204 this.el.select('a.small-box-footer',true).first().attr('href', href);
25209 setContent: function (value)
25211 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25214 initEvents: function()
25228 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25231 * @class Roo.bootstrap.dash.TabBox
25232 * @extends Roo.bootstrap.Component
25233 * Bootstrap TabBox class
25234 * @cfg {String} title Title of the TabBox
25235 * @cfg {String} icon Icon of the TabBox
25236 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25237 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25240 * Create a new TabBox
25241 * @param {Object} config The config object
25245 Roo.bootstrap.dash.TabBox = function(config){
25246 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25251 * When a pane is added
25252 * @param {Roo.bootstrap.dash.TabPane} pane
25256 * @event activatepane
25257 * When a pane is activated
25258 * @param {Roo.bootstrap.dash.TabPane} pane
25260 "activatepane" : true
25268 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25273 tabScrollable : false,
25275 getChildContainer : function()
25277 return this.el.select('.tab-content', true).first();
25280 getAutoCreate : function(){
25284 cls: 'pull-left header',
25292 cls: 'fa ' + this.icon
25298 cls: 'nav nav-tabs pull-right',
25304 if(this.tabScrollable){
25311 cls: 'nav nav-tabs pull-right',
25322 cls: 'nav-tabs-custom',
25327 cls: 'tab-content no-padding',
25335 initEvents : function()
25337 //Roo.log('add add pane handler');
25338 this.on('addpane', this.onAddPane, this);
25341 * Updates the box title
25342 * @param {String} html to set the title to.
25344 setTitle : function(value)
25346 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25348 onAddPane : function(pane)
25350 this.panes.push(pane);
25351 //Roo.log('addpane');
25353 // tabs are rendere left to right..
25354 if(!this.showtabs){
25358 var ctr = this.el.select('.nav-tabs', true).first();
25361 var existing = ctr.select('.nav-tab',true);
25362 var qty = existing.getCount();;
25365 var tab = ctr.createChild({
25367 cls : 'nav-tab' + (qty ? '' : ' active'),
25375 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25378 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25380 pane.el.addClass('active');
25385 onTabClick : function(ev,un,ob,pane)
25387 //Roo.log('tab - prev default');
25388 ev.preventDefault();
25391 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25392 pane.tab.addClass('active');
25393 //Roo.log(pane.title);
25394 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25395 // technically we should have a deactivate event.. but maybe add later.
25396 // and it should not de-activate the selected tab...
25397 this.fireEvent('activatepane', pane);
25398 pane.el.addClass('active');
25399 pane.fireEvent('activate');
25404 getActivePane : function()
25407 Roo.each(this.panes, function(p) {
25408 if(p.el.hasClass('active')){
25429 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25431 * @class Roo.bootstrap.TabPane
25432 * @extends Roo.bootstrap.Component
25433 * Bootstrap TabPane class
25434 * @cfg {Boolean} active (false | true) Default false
25435 * @cfg {String} title title of panel
25439 * Create a new TabPane
25440 * @param {Object} config The config object
25443 Roo.bootstrap.dash.TabPane = function(config){
25444 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25450 * When a pane is activated
25451 * @param {Roo.bootstrap.dash.TabPane} pane
25458 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25463 // the tabBox that this is attached to.
25466 getAutoCreate : function()
25474 cfg.cls += ' active';
25479 initEvents : function()
25481 //Roo.log('trigger add pane handler');
25482 this.parent().fireEvent('addpane', this)
25486 * Updates the tab title
25487 * @param {String} html to set the title to.
25489 setTitle: function(str)
25495 this.tab.select('a', true).first().dom.innerHTML = str;
25512 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25515 * @class Roo.bootstrap.menu.Menu
25516 * @extends Roo.bootstrap.Component
25517 * Bootstrap Menu class - container for Menu
25518 * @cfg {String} html Text of the menu
25519 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25520 * @cfg {String} icon Font awesome icon
25521 * @cfg {String} pos Menu align to (top | bottom) default bottom
25525 * Create a new Menu
25526 * @param {Object} config The config object
25530 Roo.bootstrap.menu.Menu = function(config){
25531 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25535 * @event beforeshow
25536 * Fires before this menu is displayed
25537 * @param {Roo.bootstrap.menu.Menu} this
25541 * @event beforehide
25542 * Fires before this menu is hidden
25543 * @param {Roo.bootstrap.menu.Menu} this
25548 * Fires after this menu is displayed
25549 * @param {Roo.bootstrap.menu.Menu} this
25554 * Fires after this menu is hidden
25555 * @param {Roo.bootstrap.menu.Menu} this
25560 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25561 * @param {Roo.bootstrap.menu.Menu} this
25562 * @param {Roo.EventObject} e
25569 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25573 weight : 'default',
25578 getChildContainer : function() {
25579 if(this.isSubMenu){
25583 return this.el.select('ul.dropdown-menu', true).first();
25586 getAutoCreate : function()
25591 cls : 'roo-menu-text',
25599 cls : 'fa ' + this.icon
25610 cls : 'dropdown-button btn btn-' + this.weight,
25615 cls : 'dropdown-toggle btn btn-' + this.weight,
25625 cls : 'dropdown-menu'
25631 if(this.pos == 'top'){
25632 cfg.cls += ' dropup';
25635 if(this.isSubMenu){
25638 cls : 'dropdown-menu'
25645 onRender : function(ct, position)
25647 this.isSubMenu = ct.hasClass('dropdown-submenu');
25649 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25652 initEvents : function()
25654 if(this.isSubMenu){
25658 this.hidden = true;
25660 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25661 this.triggerEl.on('click', this.onTriggerPress, this);
25663 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25664 this.buttonEl.on('click', this.onClick, this);
25670 if(this.isSubMenu){
25674 return this.el.select('ul.dropdown-menu', true).first();
25677 onClick : function(e)
25679 this.fireEvent("click", this, e);
25682 onTriggerPress : function(e)
25684 if (this.isVisible()) {
25691 isVisible : function(){
25692 return !this.hidden;
25697 this.fireEvent("beforeshow", this);
25699 this.hidden = false;
25700 this.el.addClass('open');
25702 Roo.get(document).on("mouseup", this.onMouseUp, this);
25704 this.fireEvent("show", this);
25711 this.fireEvent("beforehide", this);
25713 this.hidden = true;
25714 this.el.removeClass('open');
25716 Roo.get(document).un("mouseup", this.onMouseUp);
25718 this.fireEvent("hide", this);
25721 onMouseUp : function()
25735 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25738 * @class Roo.bootstrap.menu.Item
25739 * @extends Roo.bootstrap.Component
25740 * Bootstrap MenuItem class
25741 * @cfg {Boolean} submenu (true | false) default false
25742 * @cfg {String} html text of the item
25743 * @cfg {String} href the link
25744 * @cfg {Boolean} disable (true | false) default false
25745 * @cfg {Boolean} preventDefault (true | false) default true
25746 * @cfg {String} icon Font awesome icon
25747 * @cfg {String} pos Submenu align to (left | right) default right
25751 * Create a new Item
25752 * @param {Object} config The config object
25756 Roo.bootstrap.menu.Item = function(config){
25757 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25761 * Fires when the mouse is hovering over this menu
25762 * @param {Roo.bootstrap.menu.Item} this
25763 * @param {Roo.EventObject} e
25768 * Fires when the mouse exits this menu
25769 * @param {Roo.bootstrap.menu.Item} this
25770 * @param {Roo.EventObject} e
25776 * The raw click event for the entire grid.
25777 * @param {Roo.EventObject} e
25783 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25788 preventDefault: true,
25793 getAutoCreate : function()
25798 cls : 'roo-menu-item-text',
25806 cls : 'fa ' + this.icon
25815 href : this.href || '#',
25822 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25826 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25828 if(this.pos == 'left'){
25829 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25836 initEvents : function()
25838 this.el.on('mouseover', this.onMouseOver, this);
25839 this.el.on('mouseout', this.onMouseOut, this);
25841 this.el.select('a', true).first().on('click', this.onClick, this);
25845 onClick : function(e)
25847 if(this.preventDefault){
25848 e.preventDefault();
25851 this.fireEvent("click", this, e);
25854 onMouseOver : function(e)
25856 if(this.submenu && this.pos == 'left'){
25857 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25860 this.fireEvent("mouseover", this, e);
25863 onMouseOut : function(e)
25865 this.fireEvent("mouseout", this, e);
25877 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25880 * @class Roo.bootstrap.menu.Separator
25881 * @extends Roo.bootstrap.Component
25882 * Bootstrap Separator class
25885 * Create a new Separator
25886 * @param {Object} config The config object
25890 Roo.bootstrap.menu.Separator = function(config){
25891 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25894 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25896 getAutoCreate : function(){
25917 * @class Roo.bootstrap.Tooltip
25918 * Bootstrap Tooltip class
25919 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25920 * to determine which dom element triggers the tooltip.
25922 * It needs to add support for additional attributes like tooltip-position
25925 * Create a new Toolti
25926 * @param {Object} config The config object
25929 Roo.bootstrap.Tooltip = function(config){
25930 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25932 this.alignment = Roo.bootstrap.Tooltip.alignment;
25934 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25935 this.alignment = config.alignment;
25940 Roo.apply(Roo.bootstrap.Tooltip, {
25942 * @function init initialize tooltip monitoring.
25946 currentTip : false,
25947 currentRegion : false,
25953 Roo.get(document).on('mouseover', this.enter ,this);
25954 Roo.get(document).on('mouseout', this.leave, this);
25957 this.currentTip = new Roo.bootstrap.Tooltip();
25960 enter : function(ev)
25962 var dom = ev.getTarget();
25964 //Roo.log(['enter',dom]);
25965 var el = Roo.fly(dom);
25966 if (this.currentEl) {
25968 //Roo.log(this.currentEl);
25969 //Roo.log(this.currentEl.contains(dom));
25970 if (this.currentEl == el) {
25973 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25979 if (this.currentTip.el) {
25980 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25984 if(!el || el.dom == document){
25990 // you can not look for children, as if el is the body.. then everythign is the child..
25991 if (!el.attr('tooltip')) { //
25992 if (!el.select("[tooltip]").elements.length) {
25995 // is the mouse over this child...?
25996 bindEl = el.select("[tooltip]").first();
25997 var xy = ev.getXY();
25998 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25999 //Roo.log("not in region.");
26002 //Roo.log("child element over..");
26005 this.currentEl = bindEl;
26006 this.currentTip.bind(bindEl);
26007 this.currentRegion = Roo.lib.Region.getRegion(dom);
26008 this.currentTip.enter();
26011 leave : function(ev)
26013 var dom = ev.getTarget();
26014 //Roo.log(['leave',dom]);
26015 if (!this.currentEl) {
26020 if (dom != this.currentEl.dom) {
26023 var xy = ev.getXY();
26024 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26027 // only activate leave if mouse cursor is outside... bounding box..
26032 if (this.currentTip) {
26033 this.currentTip.leave();
26035 //Roo.log('clear currentEl');
26036 this.currentEl = false;
26041 'left' : ['r-l', [-2,0], 'right'],
26042 'right' : ['l-r', [2,0], 'left'],
26043 'bottom' : ['t-b', [0,2], 'top'],
26044 'top' : [ 'b-t', [0,-2], 'bottom']
26050 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26055 delay : null, // can be { show : 300 , hide: 500}
26059 hoverState : null, //???
26061 placement : 'bottom',
26065 getAutoCreate : function(){
26072 cls : 'tooltip-arrow'
26075 cls : 'tooltip-inner'
26082 bind : function(el)
26088 enter : function () {
26090 if (this.timeout != null) {
26091 clearTimeout(this.timeout);
26094 this.hoverState = 'in';
26095 //Roo.log("enter - show");
26096 if (!this.delay || !this.delay.show) {
26101 this.timeout = setTimeout(function () {
26102 if (_t.hoverState == 'in') {
26105 }, this.delay.show);
26109 clearTimeout(this.timeout);
26111 this.hoverState = 'out';
26112 if (!this.delay || !this.delay.hide) {
26118 this.timeout = setTimeout(function () {
26119 //Roo.log("leave - timeout");
26121 if (_t.hoverState == 'out') {
26123 Roo.bootstrap.Tooltip.currentEl = false;
26128 show : function (msg)
26131 this.render(document.body);
26134 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26136 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26138 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26140 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26142 var placement = typeof this.placement == 'function' ?
26143 this.placement.call(this, this.el, on_el) :
26146 var autoToken = /\s?auto?\s?/i;
26147 var autoPlace = autoToken.test(placement);
26149 placement = placement.replace(autoToken, '') || 'top';
26153 //this.el.setXY([0,0]);
26155 //this.el.dom.style.display='block';
26157 //this.el.appendTo(on_el);
26159 var p = this.getPosition();
26160 var box = this.el.getBox();
26166 var align = this.alignment[placement];
26168 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26170 if(placement == 'top' || placement == 'bottom'){
26172 placement = 'right';
26175 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26176 placement = 'left';
26179 var scroll = Roo.select('body', true).first().getScroll();
26181 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26187 this.el.alignTo(this.bindEl, align[0],align[1]);
26188 //var arrow = this.el.select('.arrow',true).first();
26189 //arrow.set(align[2],
26191 this.el.addClass(placement);
26193 this.el.addClass('in fade');
26195 this.hoverState = null;
26197 if (this.el.hasClass('fade')) {
26208 //this.el.setXY([0,0]);
26209 this.el.removeClass('in');
26225 * @class Roo.bootstrap.LocationPicker
26226 * @extends Roo.bootstrap.Component
26227 * Bootstrap LocationPicker class
26228 * @cfg {Number} latitude Position when init default 0
26229 * @cfg {Number} longitude Position when init default 0
26230 * @cfg {Number} zoom default 15
26231 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26232 * @cfg {Boolean} mapTypeControl default false
26233 * @cfg {Boolean} disableDoubleClickZoom default false
26234 * @cfg {Boolean} scrollwheel default true
26235 * @cfg {Boolean} streetViewControl default false
26236 * @cfg {Number} radius default 0
26237 * @cfg {String} locationName
26238 * @cfg {Boolean} draggable default true
26239 * @cfg {Boolean} enableAutocomplete default false
26240 * @cfg {Boolean} enableReverseGeocode default true
26241 * @cfg {String} markerTitle
26244 * Create a new LocationPicker
26245 * @param {Object} config The config object
26249 Roo.bootstrap.LocationPicker = function(config){
26251 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26256 * Fires when the picker initialized.
26257 * @param {Roo.bootstrap.LocationPicker} this
26258 * @param {Google Location} location
26262 * @event positionchanged
26263 * Fires when the picker position changed.
26264 * @param {Roo.bootstrap.LocationPicker} this
26265 * @param {Google Location} location
26267 positionchanged : true,
26270 * Fires when the map resize.
26271 * @param {Roo.bootstrap.LocationPicker} this
26276 * Fires when the map show.
26277 * @param {Roo.bootstrap.LocationPicker} this
26282 * Fires when the map hide.
26283 * @param {Roo.bootstrap.LocationPicker} this
26288 * Fires when click the map.
26289 * @param {Roo.bootstrap.LocationPicker} this
26290 * @param {Map event} e
26294 * @event mapRightClick
26295 * Fires when right click the map.
26296 * @param {Roo.bootstrap.LocationPicker} this
26297 * @param {Map event} e
26299 mapRightClick : true,
26301 * @event markerClick
26302 * Fires when click the marker.
26303 * @param {Roo.bootstrap.LocationPicker} this
26304 * @param {Map event} e
26306 markerClick : true,
26308 * @event markerRightClick
26309 * Fires when right click the marker.
26310 * @param {Roo.bootstrap.LocationPicker} this
26311 * @param {Map event} e
26313 markerRightClick : true,
26315 * @event OverlayViewDraw
26316 * Fires when OverlayView Draw
26317 * @param {Roo.bootstrap.LocationPicker} this
26319 OverlayViewDraw : true,
26321 * @event OverlayViewOnAdd
26322 * Fires when OverlayView Draw
26323 * @param {Roo.bootstrap.LocationPicker} this
26325 OverlayViewOnAdd : true,
26327 * @event OverlayViewOnRemove
26328 * Fires when OverlayView Draw
26329 * @param {Roo.bootstrap.LocationPicker} this
26331 OverlayViewOnRemove : true,
26333 * @event OverlayViewShow
26334 * Fires when OverlayView Draw
26335 * @param {Roo.bootstrap.LocationPicker} this
26336 * @param {Pixel} cpx
26338 OverlayViewShow : true,
26340 * @event OverlayViewHide
26341 * Fires when OverlayView Draw
26342 * @param {Roo.bootstrap.LocationPicker} this
26344 OverlayViewHide : true,
26346 * @event loadexception
26347 * Fires when load google lib failed.
26348 * @param {Roo.bootstrap.LocationPicker} this
26350 loadexception : true
26355 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26357 gMapContext: false,
26363 mapTypeControl: false,
26364 disableDoubleClickZoom: false,
26366 streetViewControl: false,
26370 enableAutocomplete: false,
26371 enableReverseGeocode: true,
26374 getAutoCreate: function()
26379 cls: 'roo-location-picker'
26385 initEvents: function(ct, position)
26387 if(!this.el.getWidth() || this.isApplied()){
26391 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26396 initial: function()
26398 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26399 this.fireEvent('loadexception', this);
26403 if(!this.mapTypeId){
26404 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26407 this.gMapContext = this.GMapContext();
26409 this.initOverlayView();
26411 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26415 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26416 _this.setPosition(_this.gMapContext.marker.position);
26419 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26420 _this.fireEvent('mapClick', this, event);
26424 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26425 _this.fireEvent('mapRightClick', this, event);
26429 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26430 _this.fireEvent('markerClick', this, event);
26434 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26435 _this.fireEvent('markerRightClick', this, event);
26439 this.setPosition(this.gMapContext.location);
26441 this.fireEvent('initial', this, this.gMapContext.location);
26444 initOverlayView: function()
26448 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26452 _this.fireEvent('OverlayViewDraw', _this);
26457 _this.fireEvent('OverlayViewOnAdd', _this);
26460 onRemove: function()
26462 _this.fireEvent('OverlayViewOnRemove', _this);
26465 show: function(cpx)
26467 _this.fireEvent('OverlayViewShow', _this, cpx);
26472 _this.fireEvent('OverlayViewHide', _this);
26478 fromLatLngToContainerPixel: function(event)
26480 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26483 isApplied: function()
26485 return this.getGmapContext() == false ? false : true;
26488 getGmapContext: function()
26490 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26493 GMapContext: function()
26495 var position = new google.maps.LatLng(this.latitude, this.longitude);
26497 var _map = new google.maps.Map(this.el.dom, {
26500 mapTypeId: this.mapTypeId,
26501 mapTypeControl: this.mapTypeControl,
26502 disableDoubleClickZoom: this.disableDoubleClickZoom,
26503 scrollwheel: this.scrollwheel,
26504 streetViewControl: this.streetViewControl,
26505 locationName: this.locationName,
26506 draggable: this.draggable,
26507 enableAutocomplete: this.enableAutocomplete,
26508 enableReverseGeocode: this.enableReverseGeocode
26511 var _marker = new google.maps.Marker({
26512 position: position,
26514 title: this.markerTitle,
26515 draggable: this.draggable
26522 location: position,
26523 radius: this.radius,
26524 locationName: this.locationName,
26525 addressComponents: {
26526 formatted_address: null,
26527 addressLine1: null,
26528 addressLine2: null,
26530 streetNumber: null,
26534 stateOrProvince: null
26537 domContainer: this.el.dom,
26538 geodecoder: new google.maps.Geocoder()
26542 drawCircle: function(center, radius, options)
26544 if (this.gMapContext.circle != null) {
26545 this.gMapContext.circle.setMap(null);
26549 options = Roo.apply({}, options, {
26550 strokeColor: "#0000FF",
26551 strokeOpacity: .35,
26553 fillColor: "#0000FF",
26557 options.map = this.gMapContext.map;
26558 options.radius = radius;
26559 options.center = center;
26560 this.gMapContext.circle = new google.maps.Circle(options);
26561 return this.gMapContext.circle;
26567 setPosition: function(location)
26569 this.gMapContext.location = location;
26570 this.gMapContext.marker.setPosition(location);
26571 this.gMapContext.map.panTo(location);
26572 this.drawCircle(location, this.gMapContext.radius, {});
26576 if (this.gMapContext.settings.enableReverseGeocode) {
26577 this.gMapContext.geodecoder.geocode({
26578 latLng: this.gMapContext.location
26579 }, function(results, status) {
26581 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26582 _this.gMapContext.locationName = results[0].formatted_address;
26583 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26585 _this.fireEvent('positionchanged', this, location);
26592 this.fireEvent('positionchanged', this, location);
26597 google.maps.event.trigger(this.gMapContext.map, "resize");
26599 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26601 this.fireEvent('resize', this);
26604 setPositionByLatLng: function(latitude, longitude)
26606 this.setPosition(new google.maps.LatLng(latitude, longitude));
26609 getCurrentPosition: function()
26612 latitude: this.gMapContext.location.lat(),
26613 longitude: this.gMapContext.location.lng()
26617 getAddressName: function()
26619 return this.gMapContext.locationName;
26622 getAddressComponents: function()
26624 return this.gMapContext.addressComponents;
26627 address_component_from_google_geocode: function(address_components)
26631 for (var i = 0; i < address_components.length; i++) {
26632 var component = address_components[i];
26633 if (component.types.indexOf("postal_code") >= 0) {
26634 result.postalCode = component.short_name;
26635 } else if (component.types.indexOf("street_number") >= 0) {
26636 result.streetNumber = component.short_name;
26637 } else if (component.types.indexOf("route") >= 0) {
26638 result.streetName = component.short_name;
26639 } else if (component.types.indexOf("neighborhood") >= 0) {
26640 result.city = component.short_name;
26641 } else if (component.types.indexOf("locality") >= 0) {
26642 result.city = component.short_name;
26643 } else if (component.types.indexOf("sublocality") >= 0) {
26644 result.district = component.short_name;
26645 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26646 result.stateOrProvince = component.short_name;
26647 } else if (component.types.indexOf("country") >= 0) {
26648 result.country = component.short_name;
26652 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26653 result.addressLine2 = "";
26657 setZoomLevel: function(zoom)
26659 this.gMapContext.map.setZoom(zoom);
26672 this.fireEvent('show', this);
26683 this.fireEvent('hide', this);
26688 Roo.apply(Roo.bootstrap.LocationPicker, {
26690 OverlayView : function(map, options)
26692 options = options || {};
26706 * @class Roo.bootstrap.Alert
26707 * @extends Roo.bootstrap.Component
26708 * Bootstrap Alert class
26709 * @cfg {String} title The title of alert
26710 * @cfg {String} html The content of alert
26711 * @cfg {String} weight ( success | info | warning | danger )
26712 * @cfg {String} faicon font-awesomeicon
26715 * Create a new alert
26716 * @param {Object} config The config object
26720 Roo.bootstrap.Alert = function(config){
26721 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26725 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26732 getAutoCreate : function()
26741 cls : 'roo-alert-icon'
26746 cls : 'roo-alert-title',
26751 cls : 'roo-alert-text',
26758 cfg.cn[0].cls += ' fa ' + this.faicon;
26762 cfg.cls += ' alert-' + this.weight;
26768 initEvents: function()
26770 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26773 setTitle : function(str)
26775 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26778 setText : function(str)
26780 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26783 setWeight : function(weight)
26786 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26789 this.weight = weight;
26791 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26794 setIcon : function(icon)
26797 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26800 this.faicon = icon;
26802 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26823 * @class Roo.bootstrap.UploadCropbox
26824 * @extends Roo.bootstrap.Component
26825 * Bootstrap UploadCropbox class
26826 * @cfg {String} emptyText show when image has been loaded
26827 * @cfg {String} rotateNotify show when image too small to rotate
26828 * @cfg {Number} errorTimeout default 3000
26829 * @cfg {Number} minWidth default 300
26830 * @cfg {Number} minHeight default 300
26831 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26832 * @cfg {Boolean} isDocument (true|false) default false
26833 * @cfg {String} url action url
26834 * @cfg {String} paramName default 'imageUpload'
26835 * @cfg {String} method default POST
26836 * @cfg {Boolean} loadMask (true|false) default true
26837 * @cfg {Boolean} loadingText default 'Loading...'
26840 * Create a new UploadCropbox
26841 * @param {Object} config The config object
26844 Roo.bootstrap.UploadCropbox = function(config){
26845 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26849 * @event beforeselectfile
26850 * Fire before select file
26851 * @param {Roo.bootstrap.UploadCropbox} this
26853 "beforeselectfile" : true,
26856 * Fire after initEvent
26857 * @param {Roo.bootstrap.UploadCropbox} this
26862 * Fire after initEvent
26863 * @param {Roo.bootstrap.UploadCropbox} this
26864 * @param {String} data
26869 * Fire when preparing the file data
26870 * @param {Roo.bootstrap.UploadCropbox} this
26871 * @param {Object} file
26876 * Fire when get exception
26877 * @param {Roo.bootstrap.UploadCropbox} this
26878 * @param {XMLHttpRequest} xhr
26880 "exception" : true,
26882 * @event beforeloadcanvas
26883 * Fire before load the canvas
26884 * @param {Roo.bootstrap.UploadCropbox} this
26885 * @param {String} src
26887 "beforeloadcanvas" : true,
26890 * Fire when trash image
26891 * @param {Roo.bootstrap.UploadCropbox} this
26896 * Fire when download the image
26897 * @param {Roo.bootstrap.UploadCropbox} this
26901 * @event footerbuttonclick
26902 * Fire when footerbuttonclick
26903 * @param {Roo.bootstrap.UploadCropbox} this
26904 * @param {String} type
26906 "footerbuttonclick" : true,
26910 * @param {Roo.bootstrap.UploadCropbox} this
26915 * Fire when rotate the image
26916 * @param {Roo.bootstrap.UploadCropbox} this
26917 * @param {String} pos
26922 * Fire when inspect the file
26923 * @param {Roo.bootstrap.UploadCropbox} this
26924 * @param {Object} file
26929 * Fire when xhr upload the file
26930 * @param {Roo.bootstrap.UploadCropbox} this
26931 * @param {Object} data
26936 * Fire when arrange the file data
26937 * @param {Roo.bootstrap.UploadCropbox} this
26938 * @param {Object} formData
26943 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26946 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26948 emptyText : 'Click to upload image',
26949 rotateNotify : 'Image is too small to rotate',
26950 errorTimeout : 3000,
26964 cropType : 'image/jpeg',
26966 canvasLoaded : false,
26967 isDocument : false,
26969 paramName : 'imageUpload',
26971 loadingText : 'Loading...',
26974 getAutoCreate : function()
26978 cls : 'roo-upload-cropbox',
26982 cls : 'roo-upload-cropbox-selector',
26987 cls : 'roo-upload-cropbox-body',
26988 style : 'cursor:pointer',
26992 cls : 'roo-upload-cropbox-preview'
26996 cls : 'roo-upload-cropbox-thumb'
27000 cls : 'roo-upload-cropbox-empty-notify',
27001 html : this.emptyText
27005 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27006 html : this.rotateNotify
27012 cls : 'roo-upload-cropbox-footer',
27015 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27025 onRender : function(ct, position)
27027 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27029 if (this.buttons.length) {
27031 Roo.each(this.buttons, function(bb) {
27033 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27035 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27041 this.maskEl = this.el;
27045 initEvents : function()
27047 this.urlAPI = (window.createObjectURL && window) ||
27048 (window.URL && URL.revokeObjectURL && URL) ||
27049 (window.webkitURL && webkitURL);
27051 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27052 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27054 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27055 this.selectorEl.hide();
27057 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27058 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27060 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27061 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27062 this.thumbEl.hide();
27064 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27065 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27067 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27068 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27069 this.errorEl.hide();
27071 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27072 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27073 this.footerEl.hide();
27075 this.setThumbBoxSize();
27081 this.fireEvent('initial', this);
27088 window.addEventListener("resize", function() { _this.resize(); } );
27090 this.bodyEl.on('click', this.beforeSelectFile, this);
27093 this.bodyEl.on('touchstart', this.onTouchStart, this);
27094 this.bodyEl.on('touchmove', this.onTouchMove, this);
27095 this.bodyEl.on('touchend', this.onTouchEnd, this);
27099 this.bodyEl.on('mousedown', this.onMouseDown, this);
27100 this.bodyEl.on('mousemove', this.onMouseMove, this);
27101 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27102 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27103 Roo.get(document).on('mouseup', this.onMouseUp, this);
27106 this.selectorEl.on('change', this.onFileSelected, this);
27112 this.baseScale = 1;
27114 this.baseRotate = 1;
27115 this.dragable = false;
27116 this.pinching = false;
27119 this.cropData = false;
27120 this.notifyEl.dom.innerHTML = this.emptyText;
27122 this.selectorEl.dom.value = '';
27126 resize : function()
27128 if(this.fireEvent('resize', this) != false){
27129 this.setThumbBoxPosition();
27130 this.setCanvasPosition();
27134 onFooterButtonClick : function(e, el, o, type)
27137 case 'rotate-left' :
27138 this.onRotateLeft(e);
27140 case 'rotate-right' :
27141 this.onRotateRight(e);
27144 this.beforeSelectFile(e);
27159 this.fireEvent('footerbuttonclick', this, type);
27162 beforeSelectFile : function(e)
27164 e.preventDefault();
27166 if(this.fireEvent('beforeselectfile', this) != false){
27167 this.selectorEl.dom.click();
27171 onFileSelected : function(e)
27173 e.preventDefault();
27175 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27179 var file = this.selectorEl.dom.files[0];
27181 if(this.fireEvent('inspect', this, file) != false){
27182 this.prepare(file);
27187 trash : function(e)
27189 this.fireEvent('trash', this);
27192 download : function(e)
27194 this.fireEvent('download', this);
27197 loadCanvas : function(src)
27199 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27203 this.imageEl = document.createElement('img');
27207 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27209 this.imageEl.src = src;
27213 onLoadCanvas : function()
27215 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27216 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27218 this.bodyEl.un('click', this.beforeSelectFile, this);
27220 this.notifyEl.hide();
27221 this.thumbEl.show();
27222 this.footerEl.show();
27224 this.baseRotateLevel();
27226 if(this.isDocument){
27227 this.setThumbBoxSize();
27230 this.setThumbBoxPosition();
27232 this.baseScaleLevel();
27238 this.canvasLoaded = true;
27241 this.maskEl.unmask();
27246 setCanvasPosition : function()
27248 if(!this.canvasEl){
27252 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27253 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27255 this.previewEl.setLeft(pw);
27256 this.previewEl.setTop(ph);
27260 onMouseDown : function(e)
27264 this.dragable = true;
27265 this.pinching = false;
27267 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27268 this.dragable = false;
27272 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27273 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27277 onMouseMove : function(e)
27281 if(!this.canvasLoaded){
27285 if (!this.dragable){
27289 var minX = Math.ceil(this.thumbEl.getLeft(true));
27290 var minY = Math.ceil(this.thumbEl.getTop(true));
27292 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27293 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27295 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27296 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27298 x = x - this.mouseX;
27299 y = y - this.mouseY;
27301 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27302 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27304 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27305 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27307 this.previewEl.setLeft(bgX);
27308 this.previewEl.setTop(bgY);
27310 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27311 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27314 onMouseUp : function(e)
27318 this.dragable = false;
27321 onMouseWheel : function(e)
27325 this.startScale = this.scale;
27327 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27329 if(!this.zoomable()){
27330 this.scale = this.startScale;
27339 zoomable : function()
27341 var minScale = this.thumbEl.getWidth() / this.minWidth;
27343 if(this.minWidth < this.minHeight){
27344 minScale = this.thumbEl.getHeight() / this.minHeight;
27347 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27348 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27352 (this.rotate == 0 || this.rotate == 180) &&
27354 width > this.imageEl.OriginWidth ||
27355 height > this.imageEl.OriginHeight ||
27356 (width < this.minWidth && height < this.minHeight)
27364 (this.rotate == 90 || this.rotate == 270) &&
27366 width > this.imageEl.OriginWidth ||
27367 height > this.imageEl.OriginHeight ||
27368 (width < this.minHeight && height < this.minWidth)
27375 !this.isDocument &&
27376 (this.rotate == 0 || this.rotate == 180) &&
27378 width < this.minWidth ||
27379 width > this.imageEl.OriginWidth ||
27380 height < this.minHeight ||
27381 height > this.imageEl.OriginHeight
27388 !this.isDocument &&
27389 (this.rotate == 90 || this.rotate == 270) &&
27391 width < this.minHeight ||
27392 width > this.imageEl.OriginWidth ||
27393 height < this.minWidth ||
27394 height > this.imageEl.OriginHeight
27404 onRotateLeft : function(e)
27406 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27408 var minScale = this.thumbEl.getWidth() / this.minWidth;
27410 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27411 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27413 this.startScale = this.scale;
27415 while (this.getScaleLevel() < minScale){
27417 this.scale = this.scale + 1;
27419 if(!this.zoomable()){
27424 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27425 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27430 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27437 this.scale = this.startScale;
27439 this.onRotateFail();
27444 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27446 if(this.isDocument){
27447 this.setThumbBoxSize();
27448 this.setThumbBoxPosition();
27449 this.setCanvasPosition();
27454 this.fireEvent('rotate', this, 'left');
27458 onRotateRight : function(e)
27460 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27462 var minScale = this.thumbEl.getWidth() / this.minWidth;
27464 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27465 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27467 this.startScale = this.scale;
27469 while (this.getScaleLevel() < minScale){
27471 this.scale = this.scale + 1;
27473 if(!this.zoomable()){
27478 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27479 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27484 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27491 this.scale = this.startScale;
27493 this.onRotateFail();
27498 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27500 if(this.isDocument){
27501 this.setThumbBoxSize();
27502 this.setThumbBoxPosition();
27503 this.setCanvasPosition();
27508 this.fireEvent('rotate', this, 'right');
27511 onRotateFail : function()
27513 this.errorEl.show(true);
27517 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27522 this.previewEl.dom.innerHTML = '';
27524 var canvasEl = document.createElement("canvas");
27526 var contextEl = canvasEl.getContext("2d");
27528 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27529 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27530 var center = this.imageEl.OriginWidth / 2;
27532 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27533 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27534 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27535 center = this.imageEl.OriginHeight / 2;
27538 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27540 contextEl.translate(center, center);
27541 contextEl.rotate(this.rotate * Math.PI / 180);
27543 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27545 this.canvasEl = document.createElement("canvas");
27547 this.contextEl = this.canvasEl.getContext("2d");
27549 switch (this.rotate) {
27552 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27553 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27555 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27560 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27561 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27563 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27564 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);
27568 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27573 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27574 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27576 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27577 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);
27581 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);
27586 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27587 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27589 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27590 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27594 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);
27601 this.previewEl.appendChild(this.canvasEl);
27603 this.setCanvasPosition();
27608 if(!this.canvasLoaded){
27612 var imageCanvas = document.createElement("canvas");
27614 var imageContext = imageCanvas.getContext("2d");
27616 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27617 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27619 var center = imageCanvas.width / 2;
27621 imageContext.translate(center, center);
27623 imageContext.rotate(this.rotate * Math.PI / 180);
27625 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27627 var canvas = document.createElement("canvas");
27629 var context = canvas.getContext("2d");
27631 canvas.width = this.minWidth;
27632 canvas.height = this.minHeight;
27634 switch (this.rotate) {
27637 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27638 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27640 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27641 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27643 var targetWidth = this.minWidth - 2 * x;
27644 var targetHeight = this.minHeight - 2 * y;
27648 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27649 scale = targetWidth / width;
27652 if(x > 0 && y == 0){
27653 scale = targetHeight / height;
27656 if(x > 0 && y > 0){
27657 scale = targetWidth / width;
27659 if(width < height){
27660 scale = targetHeight / height;
27664 context.scale(scale, scale);
27666 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27667 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27669 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27670 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27672 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27677 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27678 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27680 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27681 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27683 var targetWidth = this.minWidth - 2 * x;
27684 var targetHeight = this.minHeight - 2 * y;
27688 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27689 scale = targetWidth / width;
27692 if(x > 0 && y == 0){
27693 scale = targetHeight / height;
27696 if(x > 0 && y > 0){
27697 scale = targetWidth / width;
27699 if(width < height){
27700 scale = targetHeight / height;
27704 context.scale(scale, scale);
27706 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27707 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27709 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27710 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27712 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27714 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27719 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27720 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27722 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27723 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27725 var targetWidth = this.minWidth - 2 * x;
27726 var targetHeight = this.minHeight - 2 * y;
27730 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27731 scale = targetWidth / width;
27734 if(x > 0 && y == 0){
27735 scale = targetHeight / height;
27738 if(x > 0 && y > 0){
27739 scale = targetWidth / width;
27741 if(width < height){
27742 scale = targetHeight / height;
27746 context.scale(scale, scale);
27748 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27749 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27751 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27752 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27754 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27755 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27757 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27762 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27763 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27765 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27766 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27768 var targetWidth = this.minWidth - 2 * x;
27769 var targetHeight = this.minHeight - 2 * y;
27773 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27774 scale = targetWidth / width;
27777 if(x > 0 && y == 0){
27778 scale = targetHeight / height;
27781 if(x > 0 && y > 0){
27782 scale = targetWidth / width;
27784 if(width < height){
27785 scale = targetHeight / height;
27789 context.scale(scale, scale);
27791 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27792 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27794 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27795 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27797 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27799 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27806 this.cropData = canvas.toDataURL(this.cropType);
27808 if(this.fireEvent('crop', this, this.cropData) !== false){
27809 this.process(this.file, this.cropData);
27816 setThumbBoxSize : function()
27820 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27821 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27822 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27824 this.minWidth = width;
27825 this.minHeight = height;
27827 if(this.rotate == 90 || this.rotate == 270){
27828 this.minWidth = height;
27829 this.minHeight = width;
27834 width = Math.ceil(this.minWidth * height / this.minHeight);
27836 if(this.minWidth > this.minHeight){
27838 height = Math.ceil(this.minHeight * width / this.minWidth);
27841 this.thumbEl.setStyle({
27842 width : width + 'px',
27843 height : height + 'px'
27850 setThumbBoxPosition : function()
27852 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27853 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27855 this.thumbEl.setLeft(x);
27856 this.thumbEl.setTop(y);
27860 baseRotateLevel : function()
27862 this.baseRotate = 1;
27865 typeof(this.exif) != 'undefined' &&
27866 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27867 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27869 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27872 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27876 baseScaleLevel : function()
27880 if(this.isDocument){
27882 if(this.baseRotate == 6 || this.baseRotate == 8){
27884 height = this.thumbEl.getHeight();
27885 this.baseScale = height / this.imageEl.OriginWidth;
27887 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27888 width = this.thumbEl.getWidth();
27889 this.baseScale = width / this.imageEl.OriginHeight;
27895 height = this.thumbEl.getHeight();
27896 this.baseScale = height / this.imageEl.OriginHeight;
27898 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27899 width = this.thumbEl.getWidth();
27900 this.baseScale = width / this.imageEl.OriginWidth;
27906 if(this.baseRotate == 6 || this.baseRotate == 8){
27908 width = this.thumbEl.getHeight();
27909 this.baseScale = width / this.imageEl.OriginHeight;
27911 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27912 height = this.thumbEl.getWidth();
27913 this.baseScale = height / this.imageEl.OriginHeight;
27916 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27917 height = this.thumbEl.getWidth();
27918 this.baseScale = height / this.imageEl.OriginHeight;
27920 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27921 width = this.thumbEl.getHeight();
27922 this.baseScale = width / this.imageEl.OriginWidth;
27929 width = this.thumbEl.getWidth();
27930 this.baseScale = width / this.imageEl.OriginWidth;
27932 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27933 height = this.thumbEl.getHeight();
27934 this.baseScale = height / this.imageEl.OriginHeight;
27937 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27939 height = this.thumbEl.getHeight();
27940 this.baseScale = height / this.imageEl.OriginHeight;
27942 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27943 width = this.thumbEl.getWidth();
27944 this.baseScale = width / this.imageEl.OriginWidth;
27952 getScaleLevel : function()
27954 return this.baseScale * Math.pow(1.1, this.scale);
27957 onTouchStart : function(e)
27959 if(!this.canvasLoaded){
27960 this.beforeSelectFile(e);
27964 var touches = e.browserEvent.touches;
27970 if(touches.length == 1){
27971 this.onMouseDown(e);
27975 if(touches.length != 2){
27981 for(var i = 0, finger; finger = touches[i]; i++){
27982 coords.push(finger.pageX, finger.pageY);
27985 var x = Math.pow(coords[0] - coords[2], 2);
27986 var y = Math.pow(coords[1] - coords[3], 2);
27988 this.startDistance = Math.sqrt(x + y);
27990 this.startScale = this.scale;
27992 this.pinching = true;
27993 this.dragable = false;
27997 onTouchMove : function(e)
27999 if(!this.pinching && !this.dragable){
28003 var touches = e.browserEvent.touches;
28010 this.onMouseMove(e);
28016 for(var i = 0, finger; finger = touches[i]; i++){
28017 coords.push(finger.pageX, finger.pageY);
28020 var x = Math.pow(coords[0] - coords[2], 2);
28021 var y = Math.pow(coords[1] - coords[3], 2);
28023 this.endDistance = Math.sqrt(x + y);
28025 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28027 if(!this.zoomable()){
28028 this.scale = this.startScale;
28036 onTouchEnd : function(e)
28038 this.pinching = false;
28039 this.dragable = false;
28043 process : function(file, crop)
28046 this.maskEl.mask(this.loadingText);
28049 this.xhr = new XMLHttpRequest();
28051 file.xhr = this.xhr;
28053 this.xhr.open(this.method, this.url, true);
28056 "Accept": "application/json",
28057 "Cache-Control": "no-cache",
28058 "X-Requested-With": "XMLHttpRequest"
28061 for (var headerName in headers) {
28062 var headerValue = headers[headerName];
28064 this.xhr.setRequestHeader(headerName, headerValue);
28070 this.xhr.onload = function()
28072 _this.xhrOnLoad(_this.xhr);
28075 this.xhr.onerror = function()
28077 _this.xhrOnError(_this.xhr);
28080 var formData = new FormData();
28082 formData.append('returnHTML', 'NO');
28085 formData.append('crop', crop);
28088 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28089 formData.append(this.paramName, file, file.name);
28092 if(typeof(file.filename) != 'undefined'){
28093 formData.append('filename', file.filename);
28096 if(typeof(file.mimetype) != 'undefined'){
28097 formData.append('mimetype', file.mimetype);
28100 if(this.fireEvent('arrange', this, formData) != false){
28101 this.xhr.send(formData);
28105 xhrOnLoad : function(xhr)
28108 this.maskEl.unmask();
28111 if (xhr.readyState !== 4) {
28112 this.fireEvent('exception', this, xhr);
28116 var response = Roo.decode(xhr.responseText);
28118 if(!response.success){
28119 this.fireEvent('exception', this, xhr);
28123 var response = Roo.decode(xhr.responseText);
28125 this.fireEvent('upload', this, response);
28129 xhrOnError : function()
28132 this.maskEl.unmask();
28135 Roo.log('xhr on error');
28137 var response = Roo.decode(xhr.responseText);
28143 prepare : function(file)
28146 this.maskEl.mask(this.loadingText);
28152 if(typeof(file) === 'string'){
28153 this.loadCanvas(file);
28157 if(!file || !this.urlAPI){
28162 this.cropType = file.type;
28166 if(this.fireEvent('prepare', this, this.file) != false){
28168 var reader = new FileReader();
28170 reader.onload = function (e) {
28171 if (e.target.error) {
28172 Roo.log(e.target.error);
28176 var buffer = e.target.result,
28177 dataView = new DataView(buffer),
28179 maxOffset = dataView.byteLength - 4,
28183 if (dataView.getUint16(0) === 0xffd8) {
28184 while (offset < maxOffset) {
28185 markerBytes = dataView.getUint16(offset);
28187 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28188 markerLength = dataView.getUint16(offset + 2) + 2;
28189 if (offset + markerLength > dataView.byteLength) {
28190 Roo.log('Invalid meta data: Invalid segment size.');
28194 if(markerBytes == 0xffe1){
28195 _this.parseExifData(
28202 offset += markerLength;
28212 var url = _this.urlAPI.createObjectURL(_this.file);
28214 _this.loadCanvas(url);
28219 reader.readAsArrayBuffer(this.file);
28225 parseExifData : function(dataView, offset, length)
28227 var tiffOffset = offset + 10,
28231 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28232 // No Exif data, might be XMP data instead
28236 // Check for the ASCII code for "Exif" (0x45786966):
28237 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28238 // No Exif data, might be XMP data instead
28241 if (tiffOffset + 8 > dataView.byteLength) {
28242 Roo.log('Invalid Exif data: Invalid segment size.');
28245 // Check for the two null bytes:
28246 if (dataView.getUint16(offset + 8) !== 0x0000) {
28247 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28250 // Check the byte alignment:
28251 switch (dataView.getUint16(tiffOffset)) {
28253 littleEndian = true;
28256 littleEndian = false;
28259 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28262 // Check for the TIFF tag marker (0x002A):
28263 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28264 Roo.log('Invalid Exif data: Missing TIFF marker.');
28267 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28268 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28270 this.parseExifTags(
28273 tiffOffset + dirOffset,
28278 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28283 if (dirOffset + 6 > dataView.byteLength) {
28284 Roo.log('Invalid Exif data: Invalid directory offset.');
28287 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28288 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28289 if (dirEndOffset + 4 > dataView.byteLength) {
28290 Roo.log('Invalid Exif data: Invalid directory size.');
28293 for (i = 0; i < tagsNumber; i += 1) {
28297 dirOffset + 2 + 12 * i, // tag offset
28301 // Return the offset to the next directory:
28302 return dataView.getUint32(dirEndOffset, littleEndian);
28305 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28307 var tag = dataView.getUint16(offset, littleEndian);
28309 this.exif[tag] = this.getExifValue(
28313 dataView.getUint16(offset + 2, littleEndian), // tag type
28314 dataView.getUint32(offset + 4, littleEndian), // tag length
28319 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28321 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28330 Roo.log('Invalid Exif data: Invalid tag type.');
28334 tagSize = tagType.size * length;
28335 // Determine if the value is contained in the dataOffset bytes,
28336 // or if the value at the dataOffset is a pointer to the actual data:
28337 dataOffset = tagSize > 4 ?
28338 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28339 if (dataOffset + tagSize > dataView.byteLength) {
28340 Roo.log('Invalid Exif data: Invalid data offset.');
28343 if (length === 1) {
28344 return tagType.getValue(dataView, dataOffset, littleEndian);
28347 for (i = 0; i < length; i += 1) {
28348 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28351 if (tagType.ascii) {
28353 // Concatenate the chars:
28354 for (i = 0; i < values.length; i += 1) {
28356 // Ignore the terminating NULL byte(s):
28357 if (c === '\u0000') {
28369 Roo.apply(Roo.bootstrap.UploadCropbox, {
28371 'Orientation': 0x0112
28375 1: 0, //'top-left',
28377 3: 180, //'bottom-right',
28378 // 4: 'bottom-left',
28380 6: 90, //'right-top',
28381 // 7: 'right-bottom',
28382 8: 270 //'left-bottom'
28386 // byte, 8-bit unsigned int:
28388 getValue: function (dataView, dataOffset) {
28389 return dataView.getUint8(dataOffset);
28393 // ascii, 8-bit byte:
28395 getValue: function (dataView, dataOffset) {
28396 return String.fromCharCode(dataView.getUint8(dataOffset));
28401 // short, 16 bit int:
28403 getValue: function (dataView, dataOffset, littleEndian) {
28404 return dataView.getUint16(dataOffset, littleEndian);
28408 // long, 32 bit int:
28410 getValue: function (dataView, dataOffset, littleEndian) {
28411 return dataView.getUint32(dataOffset, littleEndian);
28415 // rational = two long values, first is numerator, second is denominator:
28417 getValue: function (dataView, dataOffset, littleEndian) {
28418 return dataView.getUint32(dataOffset, littleEndian) /
28419 dataView.getUint32(dataOffset + 4, littleEndian);
28423 // slong, 32 bit signed int:
28425 getValue: function (dataView, dataOffset, littleEndian) {
28426 return dataView.getInt32(dataOffset, littleEndian);
28430 // srational, two slongs, first is numerator, second is denominator:
28432 getValue: function (dataView, dataOffset, littleEndian) {
28433 return dataView.getInt32(dataOffset, littleEndian) /
28434 dataView.getInt32(dataOffset + 4, littleEndian);
28444 cls : 'btn-group roo-upload-cropbox-rotate-left',
28445 action : 'rotate-left',
28449 cls : 'btn btn-default',
28450 html : '<i class="fa fa-undo"></i>'
28456 cls : 'btn-group roo-upload-cropbox-picture',
28457 action : 'picture',
28461 cls : 'btn btn-default',
28462 html : '<i class="fa fa-picture-o"></i>'
28468 cls : 'btn-group roo-upload-cropbox-rotate-right',
28469 action : 'rotate-right',
28473 cls : 'btn btn-default',
28474 html : '<i class="fa fa-repeat"></i>'
28482 cls : 'btn-group roo-upload-cropbox-rotate-left',
28483 action : 'rotate-left',
28487 cls : 'btn btn-default',
28488 html : '<i class="fa fa-undo"></i>'
28494 cls : 'btn-group roo-upload-cropbox-download',
28495 action : 'download',
28499 cls : 'btn btn-default',
28500 html : '<i class="fa fa-download"></i>'
28506 cls : 'btn-group roo-upload-cropbox-crop',
28511 cls : 'btn btn-default',
28512 html : '<i class="fa fa-crop"></i>'
28518 cls : 'btn-group roo-upload-cropbox-trash',
28523 cls : 'btn btn-default',
28524 html : '<i class="fa fa-trash"></i>'
28530 cls : 'btn-group roo-upload-cropbox-rotate-right',
28531 action : 'rotate-right',
28535 cls : 'btn btn-default',
28536 html : '<i class="fa fa-repeat"></i>'
28544 cls : 'btn-group roo-upload-cropbox-rotate-left',
28545 action : 'rotate-left',
28549 cls : 'btn btn-default',
28550 html : '<i class="fa fa-undo"></i>'
28556 cls : 'btn-group roo-upload-cropbox-rotate-right',
28557 action : 'rotate-right',
28561 cls : 'btn btn-default',
28562 html : '<i class="fa fa-repeat"></i>'
28575 * @class Roo.bootstrap.DocumentManager
28576 * @extends Roo.bootstrap.Component
28577 * Bootstrap DocumentManager class
28578 * @cfg {String} paramName default 'imageUpload'
28579 * @cfg {String} toolTipName default 'filename'
28580 * @cfg {String} method default POST
28581 * @cfg {String} url action url
28582 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28583 * @cfg {Boolean} multiple multiple upload default true
28584 * @cfg {Number} thumbSize default 300
28585 * @cfg {String} fieldLabel
28586 * @cfg {Number} labelWidth default 4
28587 * @cfg {String} labelAlign (left|top) default left
28588 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28589 * @cfg {Number} labellg set the width of label (1-12)
28590 * @cfg {Number} labelmd set the width of label (1-12)
28591 * @cfg {Number} labelsm set the width of label (1-12)
28592 * @cfg {Number} labelxs set the width of label (1-12)
28595 * Create a new DocumentManager
28596 * @param {Object} config The config object
28599 Roo.bootstrap.DocumentManager = function(config){
28600 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28603 this.delegates = [];
28608 * Fire when initial the DocumentManager
28609 * @param {Roo.bootstrap.DocumentManager} this
28614 * inspect selected file
28615 * @param {Roo.bootstrap.DocumentManager} this
28616 * @param {File} file
28621 * Fire when xhr load exception
28622 * @param {Roo.bootstrap.DocumentManager} this
28623 * @param {XMLHttpRequest} xhr
28625 "exception" : true,
28627 * @event afterupload
28628 * Fire when xhr load exception
28629 * @param {Roo.bootstrap.DocumentManager} this
28630 * @param {XMLHttpRequest} xhr
28632 "afterupload" : true,
28635 * prepare the form data
28636 * @param {Roo.bootstrap.DocumentManager} this
28637 * @param {Object} formData
28642 * Fire when remove the file
28643 * @param {Roo.bootstrap.DocumentManager} this
28644 * @param {Object} file
28649 * Fire after refresh the file
28650 * @param {Roo.bootstrap.DocumentManager} this
28655 * Fire after click the image
28656 * @param {Roo.bootstrap.DocumentManager} this
28657 * @param {Object} file
28662 * Fire when upload a image and editable set to true
28663 * @param {Roo.bootstrap.DocumentManager} this
28664 * @param {Object} file
28668 * @event beforeselectfile
28669 * Fire before select file
28670 * @param {Roo.bootstrap.DocumentManager} this
28672 "beforeselectfile" : true,
28675 * Fire before process file
28676 * @param {Roo.bootstrap.DocumentManager} this
28677 * @param {Object} file
28681 * @event previewrendered
28682 * Fire when preview rendered
28683 * @param {Roo.bootstrap.DocumentManager} this
28684 * @param {Object} file
28686 "previewrendered" : true
28691 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28700 paramName : 'imageUpload',
28701 toolTipName : 'filename',
28704 labelAlign : 'left',
28714 getAutoCreate : function()
28716 var managerWidget = {
28718 cls : 'roo-document-manager',
28722 cls : 'roo-document-manager-selector',
28727 cls : 'roo-document-manager-uploader',
28731 cls : 'roo-document-manager-upload-btn',
28732 html : '<i class="fa fa-plus"></i>'
28743 cls : 'column col-md-12',
28748 if(this.fieldLabel.length){
28753 cls : 'column col-md-12',
28754 html : this.fieldLabel
28758 cls : 'column col-md-12',
28763 if(this.labelAlign == 'left'){
28768 html : this.fieldLabel
28777 if(this.labelWidth > 12){
28778 content[0].style = "width: " + this.labelWidth + 'px';
28781 if(this.labelWidth < 13 && this.labelmd == 0){
28782 this.labelmd = this.labelWidth;
28785 if(this.labellg > 0){
28786 content[0].cls += ' col-lg-' + this.labellg;
28787 content[1].cls += ' col-lg-' + (12 - this.labellg);
28790 if(this.labelmd > 0){
28791 content[0].cls += ' col-md-' + this.labelmd;
28792 content[1].cls += ' col-md-' + (12 - this.labelmd);
28795 if(this.labelsm > 0){
28796 content[0].cls += ' col-sm-' + this.labelsm;
28797 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28800 if(this.labelxs > 0){
28801 content[0].cls += ' col-xs-' + this.labelxs;
28802 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28810 cls : 'row clearfix',
28818 initEvents : function()
28820 this.managerEl = this.el.select('.roo-document-manager', true).first();
28821 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28823 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28824 this.selectorEl.hide();
28827 this.selectorEl.attr('multiple', 'multiple');
28830 this.selectorEl.on('change', this.onFileSelected, this);
28832 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28833 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28835 this.uploader.on('click', this.onUploaderClick, this);
28837 this.renderProgressDialog();
28841 window.addEventListener("resize", function() { _this.refresh(); } );
28843 this.fireEvent('initial', this);
28846 renderProgressDialog : function()
28850 this.progressDialog = new Roo.bootstrap.Modal({
28851 cls : 'roo-document-manager-progress-dialog',
28852 allow_close : false,
28862 btnclick : function() {
28863 _this.uploadCancel();
28869 this.progressDialog.render(Roo.get(document.body));
28871 this.progress = new Roo.bootstrap.Progress({
28872 cls : 'roo-document-manager-progress',
28877 this.progress.render(this.progressDialog.getChildContainer());
28879 this.progressBar = new Roo.bootstrap.ProgressBar({
28880 cls : 'roo-document-manager-progress-bar',
28883 aria_valuemax : 12,
28887 this.progressBar.render(this.progress.getChildContainer());
28890 onUploaderClick : function(e)
28892 e.preventDefault();
28894 if(this.fireEvent('beforeselectfile', this) != false){
28895 this.selectorEl.dom.click();
28900 onFileSelected : function(e)
28902 e.preventDefault();
28904 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28908 Roo.each(this.selectorEl.dom.files, function(file){
28909 if(this.fireEvent('inspect', this, file) != false){
28910 this.files.push(file);
28920 this.selectorEl.dom.value = '';
28922 if(!this.files || !this.files.length){
28926 if(this.boxes > 0 && this.files.length > this.boxes){
28927 this.files = this.files.slice(0, this.boxes);
28930 this.uploader.show();
28932 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28933 this.uploader.hide();
28942 Roo.each(this.files, function(file){
28944 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28945 var f = this.renderPreview(file);
28950 if(file.type.indexOf('image') != -1){
28951 this.delegates.push(
28953 _this.process(file);
28954 }).createDelegate(this)
28962 _this.process(file);
28963 }).createDelegate(this)
28968 this.files = files;
28970 this.delegates = this.delegates.concat(docs);
28972 if(!this.delegates.length){
28977 this.progressBar.aria_valuemax = this.delegates.length;
28984 arrange : function()
28986 if(!this.delegates.length){
28987 this.progressDialog.hide();
28992 var delegate = this.delegates.shift();
28994 this.progressDialog.show();
28996 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28998 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29003 refresh : function()
29005 this.uploader.show();
29007 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29008 this.uploader.hide();
29011 Roo.isTouch ? this.closable(false) : this.closable(true);
29013 this.fireEvent('refresh', this);
29016 onRemove : function(e, el, o)
29018 e.preventDefault();
29020 this.fireEvent('remove', this, o);
29024 remove : function(o)
29028 Roo.each(this.files, function(file){
29029 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29038 this.files = files;
29045 Roo.each(this.files, function(file){
29050 file.target.remove();
29059 onClick : function(e, el, o)
29061 e.preventDefault();
29063 this.fireEvent('click', this, o);
29067 closable : function(closable)
29069 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29071 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29083 xhrOnLoad : function(xhr)
29085 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29089 if (xhr.readyState !== 4) {
29091 this.fireEvent('exception', this, xhr);
29095 var response = Roo.decode(xhr.responseText);
29097 if(!response.success){
29099 this.fireEvent('exception', this, xhr);
29103 var file = this.renderPreview(response.data);
29105 this.files.push(file);
29109 this.fireEvent('afterupload', this, xhr);
29113 xhrOnError : function(xhr)
29115 Roo.log('xhr on error');
29117 var response = Roo.decode(xhr.responseText);
29124 process : function(file)
29126 if(this.fireEvent('process', this, file) !== false){
29127 if(this.editable && file.type.indexOf('image') != -1){
29128 this.fireEvent('edit', this, file);
29132 this.uploadStart(file, false);
29139 uploadStart : function(file, crop)
29141 this.xhr = new XMLHttpRequest();
29143 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29148 file.xhr = this.xhr;
29150 this.managerEl.createChild({
29152 cls : 'roo-document-manager-loading',
29156 tooltip : file.name,
29157 cls : 'roo-document-manager-thumb',
29158 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29164 this.xhr.open(this.method, this.url, true);
29167 "Accept": "application/json",
29168 "Cache-Control": "no-cache",
29169 "X-Requested-With": "XMLHttpRequest"
29172 for (var headerName in headers) {
29173 var headerValue = headers[headerName];
29175 this.xhr.setRequestHeader(headerName, headerValue);
29181 this.xhr.onload = function()
29183 _this.xhrOnLoad(_this.xhr);
29186 this.xhr.onerror = function()
29188 _this.xhrOnError(_this.xhr);
29191 var formData = new FormData();
29193 formData.append('returnHTML', 'NO');
29196 formData.append('crop', crop);
29199 formData.append(this.paramName, file, file.name);
29206 if(this.fireEvent('prepare', this, formData, options) != false){
29208 if(options.manually){
29212 this.xhr.send(formData);
29216 this.uploadCancel();
29219 uploadCancel : function()
29225 this.delegates = [];
29227 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29234 renderPreview : function(file)
29236 if(typeof(file.target) != 'undefined' && file.target){
29240 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29242 var previewEl = this.managerEl.createChild({
29244 cls : 'roo-document-manager-preview',
29248 tooltip : file[this.toolTipName],
29249 cls : 'roo-document-manager-thumb',
29250 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29255 html : '<i class="fa fa-times-circle"></i>'
29260 var close = previewEl.select('button.close', true).first();
29262 close.on('click', this.onRemove, this, file);
29264 file.target = previewEl;
29266 var image = previewEl.select('img', true).first();
29270 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29272 image.on('click', this.onClick, this, file);
29274 this.fireEvent('previewrendered', this, file);
29280 onPreviewLoad : function(file, image)
29282 if(typeof(file.target) == 'undefined' || !file.target){
29286 var width = image.dom.naturalWidth || image.dom.width;
29287 var height = image.dom.naturalHeight || image.dom.height;
29289 if(width > height){
29290 file.target.addClass('wide');
29294 file.target.addClass('tall');
29299 uploadFromSource : function(file, crop)
29301 this.xhr = new XMLHttpRequest();
29303 this.managerEl.createChild({
29305 cls : 'roo-document-manager-loading',
29309 tooltip : file.name,
29310 cls : 'roo-document-manager-thumb',
29311 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29317 this.xhr.open(this.method, this.url, true);
29320 "Accept": "application/json",
29321 "Cache-Control": "no-cache",
29322 "X-Requested-With": "XMLHttpRequest"
29325 for (var headerName in headers) {
29326 var headerValue = headers[headerName];
29328 this.xhr.setRequestHeader(headerName, headerValue);
29334 this.xhr.onload = function()
29336 _this.xhrOnLoad(_this.xhr);
29339 this.xhr.onerror = function()
29341 _this.xhrOnError(_this.xhr);
29344 var formData = new FormData();
29346 formData.append('returnHTML', 'NO');
29348 formData.append('crop', crop);
29350 if(typeof(file.filename) != 'undefined'){
29351 formData.append('filename', file.filename);
29354 if(typeof(file.mimetype) != 'undefined'){
29355 formData.append('mimetype', file.mimetype);
29360 if(this.fireEvent('prepare', this, formData) != false){
29361 this.xhr.send(formData);
29371 * @class Roo.bootstrap.DocumentViewer
29372 * @extends Roo.bootstrap.Component
29373 * Bootstrap DocumentViewer class
29374 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29375 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29378 * Create a new DocumentViewer
29379 * @param {Object} config The config object
29382 Roo.bootstrap.DocumentViewer = function(config){
29383 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29388 * Fire after initEvent
29389 * @param {Roo.bootstrap.DocumentViewer} this
29395 * @param {Roo.bootstrap.DocumentViewer} this
29400 * Fire after download button
29401 * @param {Roo.bootstrap.DocumentViewer} this
29406 * Fire after trash button
29407 * @param {Roo.bootstrap.DocumentViewer} this
29414 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29416 showDownload : true,
29420 getAutoCreate : function()
29424 cls : 'roo-document-viewer',
29428 cls : 'roo-document-viewer-body',
29432 cls : 'roo-document-viewer-thumb',
29436 cls : 'roo-document-viewer-image'
29444 cls : 'roo-document-viewer-footer',
29447 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29451 cls : 'btn-group roo-document-viewer-download',
29455 cls : 'btn btn-default',
29456 html : '<i class="fa fa-download"></i>'
29462 cls : 'btn-group roo-document-viewer-trash',
29466 cls : 'btn btn-default',
29467 html : '<i class="fa fa-trash"></i>'
29480 initEvents : function()
29482 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29483 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29485 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29486 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29488 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29489 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29491 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29492 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29494 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29495 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29497 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29498 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29500 this.bodyEl.on('click', this.onClick, this);
29501 this.downloadBtn.on('click', this.onDownload, this);
29502 this.trashBtn.on('click', this.onTrash, this);
29504 this.downloadBtn.hide();
29505 this.trashBtn.hide();
29507 if(this.showDownload){
29508 this.downloadBtn.show();
29511 if(this.showTrash){
29512 this.trashBtn.show();
29515 if(!this.showDownload && !this.showTrash) {
29516 this.footerEl.hide();
29521 initial : function()
29523 this.fireEvent('initial', this);
29527 onClick : function(e)
29529 e.preventDefault();
29531 this.fireEvent('click', this);
29534 onDownload : function(e)
29536 e.preventDefault();
29538 this.fireEvent('download', this);
29541 onTrash : function(e)
29543 e.preventDefault();
29545 this.fireEvent('trash', this);
29557 * @class Roo.bootstrap.NavProgressBar
29558 * @extends Roo.bootstrap.Component
29559 * Bootstrap NavProgressBar class
29562 * Create a new nav progress bar
29563 * @param {Object} config The config object
29566 Roo.bootstrap.NavProgressBar = function(config){
29567 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29569 this.bullets = this.bullets || [];
29571 // Roo.bootstrap.NavProgressBar.register(this);
29575 * Fires when the active item changes
29576 * @param {Roo.bootstrap.NavProgressBar} this
29577 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29578 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29585 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29590 getAutoCreate : function()
29592 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29596 cls : 'roo-navigation-bar-group',
29600 cls : 'roo-navigation-top-bar'
29604 cls : 'roo-navigation-bullets-bar',
29608 cls : 'roo-navigation-bar'
29615 cls : 'roo-navigation-bottom-bar'
29625 initEvents: function()
29630 onRender : function(ct, position)
29632 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29634 if(this.bullets.length){
29635 Roo.each(this.bullets, function(b){
29644 addItem : function(cfg)
29646 var item = new Roo.bootstrap.NavProgressItem(cfg);
29648 item.parentId = this.id;
29649 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29652 var top = new Roo.bootstrap.Element({
29654 cls : 'roo-navigation-bar-text'
29657 var bottom = new Roo.bootstrap.Element({
29659 cls : 'roo-navigation-bar-text'
29662 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29663 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29665 var topText = new Roo.bootstrap.Element({
29667 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29670 var bottomText = new Roo.bootstrap.Element({
29672 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29675 topText.onRender(top.el, null);
29676 bottomText.onRender(bottom.el, null);
29679 item.bottomEl = bottom;
29682 this.barItems.push(item);
29687 getActive : function()
29689 var active = false;
29691 Roo.each(this.barItems, function(v){
29693 if (!v.isActive()) {
29705 setActiveItem : function(item)
29709 Roo.each(this.barItems, function(v){
29710 if (v.rid == item.rid) {
29714 if (v.isActive()) {
29715 v.setActive(false);
29720 item.setActive(true);
29722 this.fireEvent('changed', this, item, prev);
29725 getBarItem: function(rid)
29729 Roo.each(this.barItems, function(e) {
29730 if (e.rid != rid) {
29741 indexOfItem : function(item)
29745 Roo.each(this.barItems, function(v, i){
29747 if (v.rid != item.rid) {
29758 setActiveNext : function()
29760 var i = this.indexOfItem(this.getActive());
29762 if (i > this.barItems.length) {
29766 this.setActiveItem(this.barItems[i+1]);
29769 setActivePrev : function()
29771 var i = this.indexOfItem(this.getActive());
29777 this.setActiveItem(this.barItems[i-1]);
29780 format : function()
29782 if(!this.barItems.length){
29786 var width = 100 / this.barItems.length;
29788 Roo.each(this.barItems, function(i){
29789 i.el.setStyle('width', width + '%');
29790 i.topEl.el.setStyle('width', width + '%');
29791 i.bottomEl.el.setStyle('width', width + '%');
29800 * Nav Progress Item
29805 * @class Roo.bootstrap.NavProgressItem
29806 * @extends Roo.bootstrap.Component
29807 * Bootstrap NavProgressItem class
29808 * @cfg {String} rid the reference id
29809 * @cfg {Boolean} active (true|false) Is item active default false
29810 * @cfg {Boolean} disabled (true|false) Is item active default false
29811 * @cfg {String} html
29812 * @cfg {String} position (top|bottom) text position default bottom
29813 * @cfg {String} icon show icon instead of number
29816 * Create a new NavProgressItem
29817 * @param {Object} config The config object
29819 Roo.bootstrap.NavProgressItem = function(config){
29820 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29825 * The raw click event for the entire grid.
29826 * @param {Roo.bootstrap.NavProgressItem} this
29827 * @param {Roo.EventObject} e
29834 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29840 position : 'bottom',
29843 getAutoCreate : function()
29845 var iconCls = 'roo-navigation-bar-item-icon';
29847 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29851 cls: 'roo-navigation-bar-item',
29861 cfg.cls += ' active';
29864 cfg.cls += ' disabled';
29870 disable : function()
29872 this.setDisabled(true);
29875 enable : function()
29877 this.setDisabled(false);
29880 initEvents: function()
29882 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29884 this.iconEl.on('click', this.onClick, this);
29887 onClick : function(e)
29889 e.preventDefault();
29895 if(this.fireEvent('click', this, e) === false){
29899 this.parent().setActiveItem(this);
29902 isActive: function ()
29904 return this.active;
29907 setActive : function(state)
29909 if(this.active == state){
29913 this.active = state;
29916 this.el.addClass('active');
29920 this.el.removeClass('active');
29925 setDisabled : function(state)
29927 if(this.disabled == state){
29931 this.disabled = state;
29934 this.el.addClass('disabled');
29938 this.el.removeClass('disabled');
29941 tooltipEl : function()
29943 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29956 * @class Roo.bootstrap.FieldLabel
29957 * @extends Roo.bootstrap.Component
29958 * Bootstrap FieldLabel class
29959 * @cfg {String} html contents of the element
29960 * @cfg {String} tag tag of the element default label
29961 * @cfg {String} cls class of the element
29962 * @cfg {String} target label target
29963 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29964 * @cfg {String} invalidClass default "text-warning"
29965 * @cfg {String} validClass default "text-success"
29966 * @cfg {String} iconTooltip default "This field is required"
29967 * @cfg {String} indicatorpos (left|right) default left
29970 * Create a new FieldLabel
29971 * @param {Object} config The config object
29974 Roo.bootstrap.FieldLabel = function(config){
29975 Roo.bootstrap.Element.superclass.constructor.call(this, config);
29980 * Fires after the field has been marked as invalid.
29981 * @param {Roo.form.FieldLabel} this
29982 * @param {String} msg The validation message
29987 * Fires after the field has been validated with no errors.
29988 * @param {Roo.form.FieldLabel} this
29994 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30001 invalidClass : 'has-warning',
30002 validClass : 'has-success',
30003 iconTooltip : 'This field is required',
30004 indicatorpos : 'left',
30006 getAutoCreate : function(){
30010 cls : 'roo-bootstrap-field-label ' + this.cls,
30015 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
30016 tooltip : this.iconTooltip
30025 if(this.indicatorpos == 'right'){
30028 cls : 'roo-bootstrap-field-label ' + this.cls,
30037 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
30038 tooltip : this.iconTooltip
30047 initEvents: function()
30049 Roo.bootstrap.Element.superclass.initEvents.call(this);
30051 this.indicator = this.indicatorEl();
30053 if(this.indicator){
30054 this.indicator.removeClass('visible');
30055 this.indicator.addClass('invisible');
30058 Roo.bootstrap.FieldLabel.register(this);
30061 indicatorEl : function()
30063 var indicator = this.el.select('i.roo-required-indicator',true).first();
30074 * Mark this field as valid
30076 markValid : function()
30078 if(this.indicator){
30079 this.indicator.removeClass('visible');
30080 this.indicator.addClass('invisible');
30083 this.el.removeClass(this.invalidClass);
30085 this.el.addClass(this.validClass);
30087 this.fireEvent('valid', this);
30091 * Mark this field as invalid
30092 * @param {String} msg The validation message
30094 markInvalid : function(msg)
30096 if(this.indicator){
30097 this.indicator.removeClass('invisible');
30098 this.indicator.addClass('visible');
30101 this.el.removeClass(this.validClass);
30103 this.el.addClass(this.invalidClass);
30105 this.fireEvent('invalid', this, msg);
30111 Roo.apply(Roo.bootstrap.FieldLabel, {
30116 * register a FieldLabel Group
30117 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30119 register : function(label)
30121 if(this.groups.hasOwnProperty(label.target)){
30125 this.groups[label.target] = label;
30129 * fetch a FieldLabel Group based on the target
30130 * @param {string} target
30131 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30133 get: function(target) {
30134 if (typeof(this.groups[target]) == 'undefined') {
30138 return this.groups[target] ;
30147 * page DateSplitField.
30153 * @class Roo.bootstrap.DateSplitField
30154 * @extends Roo.bootstrap.Component
30155 * Bootstrap DateSplitField class
30156 * @cfg {string} fieldLabel - the label associated
30157 * @cfg {Number} labelWidth set the width of label (0-12)
30158 * @cfg {String} labelAlign (top|left)
30159 * @cfg {Boolean} dayAllowBlank (true|false) default false
30160 * @cfg {Boolean} monthAllowBlank (true|false) default false
30161 * @cfg {Boolean} yearAllowBlank (true|false) default false
30162 * @cfg {string} dayPlaceholder
30163 * @cfg {string} monthPlaceholder
30164 * @cfg {string} yearPlaceholder
30165 * @cfg {string} dayFormat default 'd'
30166 * @cfg {string} monthFormat default 'm'
30167 * @cfg {string} yearFormat default 'Y'
30168 * @cfg {Number} labellg set the width of label (1-12)
30169 * @cfg {Number} labelmd set the width of label (1-12)
30170 * @cfg {Number} labelsm set the width of label (1-12)
30171 * @cfg {Number} labelxs set the width of label (1-12)
30175 * Create a new DateSplitField
30176 * @param {Object} config The config object
30179 Roo.bootstrap.DateSplitField = function(config){
30180 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30186 * getting the data of years
30187 * @param {Roo.bootstrap.DateSplitField} this
30188 * @param {Object} years
30193 * getting the data of days
30194 * @param {Roo.bootstrap.DateSplitField} this
30195 * @param {Object} days
30200 * Fires after the field has been marked as invalid.
30201 * @param {Roo.form.Field} this
30202 * @param {String} msg The validation message
30207 * Fires after the field has been validated with no errors.
30208 * @param {Roo.form.Field} this
30214 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30217 labelAlign : 'top',
30219 dayAllowBlank : false,
30220 monthAllowBlank : false,
30221 yearAllowBlank : false,
30222 dayPlaceholder : '',
30223 monthPlaceholder : '',
30224 yearPlaceholder : '',
30228 isFormField : true,
30234 getAutoCreate : function()
30238 cls : 'row roo-date-split-field-group',
30243 cls : 'form-hidden-field roo-date-split-field-group-value',
30249 var labelCls = 'col-md-12';
30250 var contentCls = 'col-md-4';
30252 if(this.fieldLabel){
30256 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30260 html : this.fieldLabel
30265 if(this.labelAlign == 'left'){
30267 if(this.labelWidth > 12){
30268 label.style = "width: " + this.labelWidth + 'px';
30271 if(this.labelWidth < 13 && this.labelmd == 0){
30272 this.labelmd = this.labelWidth;
30275 if(this.labellg > 0){
30276 labelCls = ' col-lg-' + this.labellg;
30277 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30280 if(this.labelmd > 0){
30281 labelCls = ' col-md-' + this.labelmd;
30282 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30285 if(this.labelsm > 0){
30286 labelCls = ' col-sm-' + this.labelsm;
30287 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30290 if(this.labelxs > 0){
30291 labelCls = ' col-xs-' + this.labelxs;
30292 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30296 label.cls += ' ' + labelCls;
30298 cfg.cn.push(label);
30301 Roo.each(['day', 'month', 'year'], function(t){
30304 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30311 inputEl: function ()
30313 return this.el.select('.roo-date-split-field-group-value', true).first();
30316 onRender : function(ct, position)
30320 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30322 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30324 this.dayField = new Roo.bootstrap.ComboBox({
30325 allowBlank : this.dayAllowBlank,
30326 alwaysQuery : true,
30327 displayField : 'value',
30330 forceSelection : true,
30332 placeholder : this.dayPlaceholder,
30333 selectOnFocus : true,
30334 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30335 triggerAction : 'all',
30337 valueField : 'value',
30338 store : new Roo.data.SimpleStore({
30339 data : (function() {
30341 _this.fireEvent('days', _this, days);
30344 fields : [ 'value' ]
30347 select : function (_self, record, index)
30349 _this.setValue(_this.getValue());
30354 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30356 this.monthField = new Roo.bootstrap.MonthField({
30357 after : '<i class=\"fa fa-calendar\"></i>',
30358 allowBlank : this.monthAllowBlank,
30359 placeholder : this.monthPlaceholder,
30362 render : function (_self)
30364 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30365 e.preventDefault();
30369 select : function (_self, oldvalue, newvalue)
30371 _this.setValue(_this.getValue());
30376 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30378 this.yearField = new Roo.bootstrap.ComboBox({
30379 allowBlank : this.yearAllowBlank,
30380 alwaysQuery : true,
30381 displayField : 'value',
30384 forceSelection : true,
30386 placeholder : this.yearPlaceholder,
30387 selectOnFocus : true,
30388 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30389 triggerAction : 'all',
30391 valueField : 'value',
30392 store : new Roo.data.SimpleStore({
30393 data : (function() {
30395 _this.fireEvent('years', _this, years);
30398 fields : [ 'value' ]
30401 select : function (_self, record, index)
30403 _this.setValue(_this.getValue());
30408 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30411 setValue : function(v, format)
30413 this.inputEl.dom.value = v;
30415 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30417 var d = Date.parseDate(v, f);
30424 this.setDay(d.format(this.dayFormat));
30425 this.setMonth(d.format(this.monthFormat));
30426 this.setYear(d.format(this.yearFormat));
30433 setDay : function(v)
30435 this.dayField.setValue(v);
30436 this.inputEl.dom.value = this.getValue();
30441 setMonth : function(v)
30443 this.monthField.setValue(v, true);
30444 this.inputEl.dom.value = this.getValue();
30449 setYear : function(v)
30451 this.yearField.setValue(v);
30452 this.inputEl.dom.value = this.getValue();
30457 getDay : function()
30459 return this.dayField.getValue();
30462 getMonth : function()
30464 return this.monthField.getValue();
30467 getYear : function()
30469 return this.yearField.getValue();
30472 getValue : function()
30474 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30476 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30486 this.inputEl.dom.value = '';
30491 validate : function()
30493 var d = this.dayField.validate();
30494 var m = this.monthField.validate();
30495 var y = this.yearField.validate();
30500 (!this.dayAllowBlank && !d) ||
30501 (!this.monthAllowBlank && !m) ||
30502 (!this.yearAllowBlank && !y)
30507 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30516 this.markInvalid();
30521 markValid : function()
30524 var label = this.el.select('label', true).first();
30525 var icon = this.el.select('i.fa-star', true).first();
30531 this.fireEvent('valid', this);
30535 * Mark this field as invalid
30536 * @param {String} msg The validation message
30538 markInvalid : function(msg)
30541 var label = this.el.select('label', true).first();
30542 var icon = this.el.select('i.fa-star', true).first();
30544 if(label && !icon){
30545 this.el.select('.roo-date-split-field-label', true).createChild({
30547 cls : 'text-danger fa fa-lg fa-star',
30548 tooltip : 'This field is required',
30549 style : 'margin-right:5px;'
30553 this.fireEvent('invalid', this, msg);
30556 clearInvalid : function()
30558 var label = this.el.select('label', true).first();
30559 var icon = this.el.select('i.fa-star', true).first();
30565 this.fireEvent('valid', this);
30568 getName: function()
30578 * http://masonry.desandro.com
30580 * The idea is to render all the bricks based on vertical width...
30582 * The original code extends 'outlayer' - we might need to use that....
30588 * @class Roo.bootstrap.LayoutMasonry
30589 * @extends Roo.bootstrap.Component
30590 * Bootstrap Layout Masonry class
30593 * Create a new Element
30594 * @param {Object} config The config object
30597 Roo.bootstrap.LayoutMasonry = function(config){
30599 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30603 Roo.bootstrap.LayoutMasonry.register(this);
30609 * Fire after layout the items
30610 * @param {Roo.bootstrap.LayoutMasonry} this
30611 * @param {Roo.EventObject} e
30618 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30621 * @cfg {Boolean} isLayoutInstant = no animation?
30623 isLayoutInstant : false, // needed?
30626 * @cfg {Number} boxWidth width of the columns
30631 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30636 * @cfg {Number} padWidth padding below box..
30641 * @cfg {Number} gutter gutter width..
30646 * @cfg {Number} maxCols maximum number of columns
30652 * @cfg {Boolean} isAutoInitial defalut true
30654 isAutoInitial : true,
30659 * @cfg {Boolean} isHorizontal defalut false
30661 isHorizontal : false,
30663 currentSize : null,
30669 bricks: null, //CompositeElement
30673 _isLayoutInited : false,
30675 // isAlternative : false, // only use for vertical layout...
30678 * @cfg {Number} alternativePadWidth padding below box..
30680 alternativePadWidth : 50,
30682 selectedBrick : [],
30684 getAutoCreate : function(){
30686 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30690 cls: 'blog-masonary-wrapper ' + this.cls,
30692 cls : 'mas-boxes masonary'
30699 getChildContainer: function( )
30701 if (this.boxesEl) {
30702 return this.boxesEl;
30705 this.boxesEl = this.el.select('.mas-boxes').first();
30707 return this.boxesEl;
30711 initEvents : function()
30715 if(this.isAutoInitial){
30716 Roo.log('hook children rendered');
30717 this.on('childrenrendered', function() {
30718 Roo.log('children rendered');
30724 initial : function()
30726 this.selectedBrick = [];
30728 this.currentSize = this.el.getBox(true);
30730 Roo.EventManager.onWindowResize(this.resize, this);
30732 if(!this.isAutoInitial){
30740 //this.layout.defer(500,this);
30744 resize : function()
30746 var cs = this.el.getBox(true);
30749 this.currentSize.width == cs.width &&
30750 this.currentSize.x == cs.x &&
30751 this.currentSize.height == cs.height &&
30752 this.currentSize.y == cs.y
30754 Roo.log("no change in with or X or Y");
30758 this.currentSize = cs;
30764 layout : function()
30766 this._resetLayout();
30768 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30770 this.layoutItems( isInstant );
30772 this._isLayoutInited = true;
30774 this.fireEvent('layout', this);
30778 _resetLayout : function()
30780 if(this.isHorizontal){
30781 this.horizontalMeasureColumns();
30785 this.verticalMeasureColumns();
30789 verticalMeasureColumns : function()
30791 this.getContainerWidth();
30793 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30794 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30798 var boxWidth = this.boxWidth + this.padWidth;
30800 if(this.containerWidth < this.boxWidth){
30801 boxWidth = this.containerWidth
30804 var containerWidth = this.containerWidth;
30806 var cols = Math.floor(containerWidth / boxWidth);
30808 this.cols = Math.max( cols, 1 );
30810 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30812 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30814 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30816 this.colWidth = boxWidth + avail - this.padWidth;
30818 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30819 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30822 horizontalMeasureColumns : function()
30824 this.getContainerWidth();
30826 var boxWidth = this.boxWidth;
30828 if(this.containerWidth < boxWidth){
30829 boxWidth = this.containerWidth;
30832 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30834 this.el.setHeight(boxWidth);
30838 getContainerWidth : function()
30840 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30843 layoutItems : function( isInstant )
30845 Roo.log(this.bricks);
30847 var items = Roo.apply([], this.bricks);
30849 if(this.isHorizontal){
30850 this._horizontalLayoutItems( items , isInstant );
30854 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30855 // this._verticalAlternativeLayoutItems( items , isInstant );
30859 this._verticalLayoutItems( items , isInstant );
30863 _verticalLayoutItems : function ( items , isInstant)
30865 if ( !items || !items.length ) {
30870 ['xs', 'xs', 'xs', 'tall'],
30871 ['xs', 'xs', 'tall'],
30872 ['xs', 'xs', 'sm'],
30873 ['xs', 'xs', 'xs'],
30879 ['sm', 'xs', 'xs'],
30883 ['tall', 'xs', 'xs', 'xs'],
30884 ['tall', 'xs', 'xs'],
30896 Roo.each(items, function(item, k){
30898 switch (item.size) {
30899 // these layouts take up a full box,
30910 boxes.push([item]);
30933 var filterPattern = function(box, length)
30941 var pattern = box.slice(0, length);
30945 Roo.each(pattern, function(i){
30946 format.push(i.size);
30949 Roo.each(standard, function(s){
30951 if(String(s) != String(format)){
30960 if(!match && length == 1){
30965 filterPattern(box, length - 1);
30969 queue.push(pattern);
30971 box = box.slice(length, box.length);
30973 filterPattern(box, 4);
30979 Roo.each(boxes, function(box, k){
30985 if(box.length == 1){
30990 filterPattern(box, 4);
30994 this._processVerticalLayoutQueue( queue, isInstant );
30998 // _verticalAlternativeLayoutItems : function( items , isInstant )
31000 // if ( !items || !items.length ) {
31004 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31008 _horizontalLayoutItems : function ( items , isInstant)
31010 if ( !items || !items.length || items.length < 3) {
31016 var eItems = items.slice(0, 3);
31018 items = items.slice(3, items.length);
31021 ['xs', 'xs', 'xs', 'wide'],
31022 ['xs', 'xs', 'wide'],
31023 ['xs', 'xs', 'sm'],
31024 ['xs', 'xs', 'xs'],
31030 ['sm', 'xs', 'xs'],
31034 ['wide', 'xs', 'xs', 'xs'],
31035 ['wide', 'xs', 'xs'],
31048 Roo.each(items, function(item, k){
31050 switch (item.size) {
31061 boxes.push([item]);
31085 var filterPattern = function(box, length)
31093 var pattern = box.slice(0, length);
31097 Roo.each(pattern, function(i){
31098 format.push(i.size);
31101 Roo.each(standard, function(s){
31103 if(String(s) != String(format)){
31112 if(!match && length == 1){
31117 filterPattern(box, length - 1);
31121 queue.push(pattern);
31123 box = box.slice(length, box.length);
31125 filterPattern(box, 4);
31131 Roo.each(boxes, function(box, k){
31137 if(box.length == 1){
31142 filterPattern(box, 4);
31149 var pos = this.el.getBox(true);
31153 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31155 var hit_end = false;
31157 Roo.each(queue, function(box){
31161 Roo.each(box, function(b){
31163 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31173 Roo.each(box, function(b){
31175 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31178 mx = Math.max(mx, b.x);
31182 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31186 Roo.each(box, function(b){
31188 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31202 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31205 /** Sets position of item in DOM
31206 * @param {Element} item
31207 * @param {Number} x - horizontal position
31208 * @param {Number} y - vertical position
31209 * @param {Boolean} isInstant - disables transitions
31211 _processVerticalLayoutQueue : function( queue, isInstant )
31213 var pos = this.el.getBox(true);
31218 for (var i = 0; i < this.cols; i++){
31222 Roo.each(queue, function(box, k){
31224 var col = k % this.cols;
31226 Roo.each(box, function(b,kk){
31228 b.el.position('absolute');
31230 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31231 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31233 if(b.size == 'md-left' || b.size == 'md-right'){
31234 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31235 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31238 b.el.setWidth(width);
31239 b.el.setHeight(height);
31241 b.el.select('iframe',true).setSize(width,height);
31245 for (var i = 0; i < this.cols; i++){
31247 if(maxY[i] < maxY[col]){
31252 col = Math.min(col, i);
31256 x = pos.x + col * (this.colWidth + this.padWidth);
31260 var positions = [];
31262 switch (box.length){
31264 positions = this.getVerticalOneBoxColPositions(x, y, box);
31267 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31270 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31273 positions = this.getVerticalFourBoxColPositions(x, y, box);
31279 Roo.each(box, function(b,kk){
31281 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31283 var sz = b.el.getSize();
31285 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31293 for (var i = 0; i < this.cols; i++){
31294 mY = Math.max(mY, maxY[i]);
31297 this.el.setHeight(mY - pos.y);
31301 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31303 // var pos = this.el.getBox(true);
31306 // var maxX = pos.right;
31308 // var maxHeight = 0;
31310 // Roo.each(items, function(item, k){
31314 // item.el.position('absolute');
31316 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31318 // item.el.setWidth(width);
31320 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31322 // item.el.setHeight(height);
31325 // item.el.setXY([x, y], isInstant ? false : true);
31327 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31330 // y = y + height + this.alternativePadWidth;
31332 // maxHeight = maxHeight + height + this.alternativePadWidth;
31336 // this.el.setHeight(maxHeight);
31340 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31342 var pos = this.el.getBox(true);
31347 var maxX = pos.right;
31349 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31351 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31353 Roo.each(queue, function(box, k){
31355 Roo.each(box, function(b, kk){
31357 b.el.position('absolute');
31359 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31360 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31362 if(b.size == 'md-left' || b.size == 'md-right'){
31363 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31364 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31367 b.el.setWidth(width);
31368 b.el.setHeight(height);
31376 var positions = [];
31378 switch (box.length){
31380 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31383 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31386 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31389 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31395 Roo.each(box, function(b,kk){
31397 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31399 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31407 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31409 Roo.each(eItems, function(b,k){
31411 b.size = (k == 0) ? 'sm' : 'xs';
31412 b.x = (k == 0) ? 2 : 1;
31413 b.y = (k == 0) ? 2 : 1;
31415 b.el.position('absolute');
31417 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31419 b.el.setWidth(width);
31421 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31423 b.el.setHeight(height);
31427 var positions = [];
31430 x : maxX - this.unitWidth * 2 - this.gutter,
31435 x : maxX - this.unitWidth,
31436 y : minY + (this.unitWidth + this.gutter) * 2
31440 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31444 Roo.each(eItems, function(b,k){
31446 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31452 getVerticalOneBoxColPositions : function(x, y, box)
31456 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31458 if(box[0].size == 'md-left'){
31462 if(box[0].size == 'md-right'){
31467 x : x + (this.unitWidth + this.gutter) * rand,
31474 getVerticalTwoBoxColPositions : function(x, y, box)
31478 if(box[0].size == 'xs'){
31482 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31486 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31500 x : x + (this.unitWidth + this.gutter) * 2,
31501 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31508 getVerticalThreeBoxColPositions : function(x, y, box)
31512 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31520 x : x + (this.unitWidth + this.gutter) * 1,
31525 x : x + (this.unitWidth + this.gutter) * 2,
31533 if(box[0].size == 'xs' && box[1].size == 'xs'){
31542 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31546 x : x + (this.unitWidth + this.gutter) * 1,
31560 x : x + (this.unitWidth + this.gutter) * 2,
31565 x : x + (this.unitWidth + this.gutter) * 2,
31566 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31573 getVerticalFourBoxColPositions : function(x, y, box)
31577 if(box[0].size == 'xs'){
31586 y : y + (this.unitHeight + this.gutter) * 1
31591 y : y + (this.unitHeight + this.gutter) * 2
31595 x : x + (this.unitWidth + this.gutter) * 1,
31609 x : x + (this.unitWidth + this.gutter) * 2,
31614 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31615 y : y + (this.unitHeight + this.gutter) * 1
31619 x : x + (this.unitWidth + this.gutter) * 2,
31620 y : y + (this.unitWidth + this.gutter) * 2
31627 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31631 if(box[0].size == 'md-left'){
31633 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31640 if(box[0].size == 'md-right'){
31642 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31643 y : minY + (this.unitWidth + this.gutter) * 1
31649 var rand = Math.floor(Math.random() * (4 - box[0].y));
31652 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31653 y : minY + (this.unitWidth + this.gutter) * rand
31660 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31664 if(box[0].size == 'xs'){
31667 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31672 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31673 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31681 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31686 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31687 y : minY + (this.unitWidth + this.gutter) * 2
31694 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31698 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31701 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31706 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31707 y : minY + (this.unitWidth + this.gutter) * 1
31711 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31712 y : minY + (this.unitWidth + this.gutter) * 2
31719 if(box[0].size == 'xs' && box[1].size == 'xs'){
31722 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31727 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31732 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31733 y : minY + (this.unitWidth + this.gutter) * 1
31741 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31746 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31747 y : minY + (this.unitWidth + this.gutter) * 2
31751 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31752 y : minY + (this.unitWidth + this.gutter) * 2
31759 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31763 if(box[0].size == 'xs'){
31766 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31771 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31776 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),
31781 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31782 y : minY + (this.unitWidth + this.gutter) * 1
31790 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31795 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31796 y : minY + (this.unitWidth + this.gutter) * 2
31800 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31801 y : minY + (this.unitWidth + this.gutter) * 2
31805 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),
31806 y : minY + (this.unitWidth + this.gutter) * 2
31814 * remove a Masonry Brick
31815 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31817 removeBrick : function(brick_id)
31823 for (var i = 0; i<this.bricks.length; i++) {
31824 if (this.bricks[i].id == brick_id) {
31825 this.bricks.splice(i,1);
31826 this.el.dom.removeChild(Roo.get(brick_id).dom);
31833 * adds a Masonry Brick
31834 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31836 addBrick : function(cfg)
31838 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31839 //this.register(cn);
31840 cn.parentId = this.id;
31841 cn.onRender(this.el, null);
31846 * register a Masonry Brick
31847 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31850 register : function(brick)
31852 this.bricks.push(brick);
31853 brick.masonryId = this.id;
31857 * clear all the Masonry Brick
31859 clearAll : function()
31862 //this.getChildContainer().dom.innerHTML = "";
31863 this.el.dom.innerHTML = '';
31866 getSelected : function()
31868 if (!this.selectedBrick) {
31872 return this.selectedBrick;
31876 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31880 * register a Masonry Layout
31881 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31884 register : function(layout)
31886 this.groups[layout.id] = layout;
31889 * fetch a Masonry Layout based on the masonry layout ID
31890 * @param {string} the masonry layout to add
31891 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31894 get: function(layout_id) {
31895 if (typeof(this.groups[layout_id]) == 'undefined') {
31898 return this.groups[layout_id] ;
31910 * http://masonry.desandro.com
31912 * The idea is to render all the bricks based on vertical width...
31914 * The original code extends 'outlayer' - we might need to use that....
31920 * @class Roo.bootstrap.LayoutMasonryAuto
31921 * @extends Roo.bootstrap.Component
31922 * Bootstrap Layout Masonry class
31925 * Create a new Element
31926 * @param {Object} config The config object
31929 Roo.bootstrap.LayoutMasonryAuto = function(config){
31930 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31933 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31936 * @cfg {Boolean} isFitWidth - resize the width..
31938 isFitWidth : false, // options..
31940 * @cfg {Boolean} isOriginLeft = left align?
31942 isOriginLeft : true,
31944 * @cfg {Boolean} isOriginTop = top align?
31946 isOriginTop : false,
31948 * @cfg {Boolean} isLayoutInstant = no animation?
31950 isLayoutInstant : false, // needed?
31952 * @cfg {Boolean} isResizingContainer = not sure if this is used..
31954 isResizingContainer : true,
31956 * @cfg {Number} columnWidth width of the columns
31962 * @cfg {Number} maxCols maximum number of columns
31967 * @cfg {Number} padHeight padding below box..
31973 * @cfg {Boolean} isAutoInitial defalut true
31976 isAutoInitial : true,
31982 initialColumnWidth : 0,
31983 currentSize : null,
31985 colYs : null, // array.
31992 bricks: null, //CompositeElement
31993 cols : 0, // array?
31994 // element : null, // wrapped now this.el
31995 _isLayoutInited : null,
31998 getAutoCreate : function(){
32002 cls: 'blog-masonary-wrapper ' + this.cls,
32004 cls : 'mas-boxes masonary'
32011 getChildContainer: function( )
32013 if (this.boxesEl) {
32014 return this.boxesEl;
32017 this.boxesEl = this.el.select('.mas-boxes').first();
32019 return this.boxesEl;
32023 initEvents : function()
32027 if(this.isAutoInitial){
32028 Roo.log('hook children rendered');
32029 this.on('childrenrendered', function() {
32030 Roo.log('children rendered');
32037 initial : function()
32039 this.reloadItems();
32041 this.currentSize = this.el.getBox(true);
32043 /// was window resize... - let's see if this works..
32044 Roo.EventManager.onWindowResize(this.resize, this);
32046 if(!this.isAutoInitial){
32051 this.layout.defer(500,this);
32054 reloadItems: function()
32056 this.bricks = this.el.select('.masonry-brick', true);
32058 this.bricks.each(function(b) {
32059 //Roo.log(b.getSize());
32060 if (!b.attr('originalwidth')) {
32061 b.attr('originalwidth', b.getSize().width);
32066 Roo.log(this.bricks.elements.length);
32069 resize : function()
32072 var cs = this.el.getBox(true);
32074 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32075 Roo.log("no change in with or X");
32078 this.currentSize = cs;
32082 layout : function()
32085 this._resetLayout();
32086 //this._manageStamps();
32088 // don't animate first layout
32089 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32090 this.layoutItems( isInstant );
32092 // flag for initalized
32093 this._isLayoutInited = true;
32096 layoutItems : function( isInstant )
32098 //var items = this._getItemsForLayout( this.items );
32099 // original code supports filtering layout items.. we just ignore it..
32101 this._layoutItems( this.bricks , isInstant );
32103 this._postLayout();
32105 _layoutItems : function ( items , isInstant)
32107 //this.fireEvent( 'layout', this, items );
32110 if ( !items || !items.elements.length ) {
32111 // no items, emit event with empty array
32116 items.each(function(item) {
32117 Roo.log("layout item");
32119 // get x/y object from method
32120 var position = this._getItemLayoutPosition( item );
32122 position.item = item;
32123 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32124 queue.push( position );
32127 this._processLayoutQueue( queue );
32129 /** Sets position of item in DOM
32130 * @param {Element} item
32131 * @param {Number} x - horizontal position
32132 * @param {Number} y - vertical position
32133 * @param {Boolean} isInstant - disables transitions
32135 _processLayoutQueue : function( queue )
32137 for ( var i=0, len = queue.length; i < len; i++ ) {
32138 var obj = queue[i];
32139 obj.item.position('absolute');
32140 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32146 * Any logic you want to do after each layout,
32147 * i.e. size the container
32149 _postLayout : function()
32151 this.resizeContainer();
32154 resizeContainer : function()
32156 if ( !this.isResizingContainer ) {
32159 var size = this._getContainerSize();
32161 this.el.setSize(size.width,size.height);
32162 this.boxesEl.setSize(size.width,size.height);
32168 _resetLayout : function()
32170 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32171 this.colWidth = this.el.getWidth();
32172 //this.gutter = this.el.getWidth();
32174 this.measureColumns();
32180 this.colYs.push( 0 );
32186 measureColumns : function()
32188 this.getContainerWidth();
32189 // if columnWidth is 0, default to outerWidth of first item
32190 if ( !this.columnWidth ) {
32191 var firstItem = this.bricks.first();
32192 Roo.log(firstItem);
32193 this.columnWidth = this.containerWidth;
32194 if (firstItem && firstItem.attr('originalwidth') ) {
32195 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32197 // columnWidth fall back to item of first element
32198 Roo.log("set column width?");
32199 this.initialColumnWidth = this.columnWidth ;
32201 // if first elem has no width, default to size of container
32206 if (this.initialColumnWidth) {
32207 this.columnWidth = this.initialColumnWidth;
32212 // column width is fixed at the top - however if container width get's smaller we should
32215 // this bit calcs how man columns..
32217 var columnWidth = this.columnWidth += this.gutter;
32219 // calculate columns
32220 var containerWidth = this.containerWidth + this.gutter;
32222 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32223 // fix rounding errors, typically with gutters
32224 var excess = columnWidth - containerWidth % columnWidth;
32227 // if overshoot is less than a pixel, round up, otherwise floor it
32228 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32229 cols = Math[ mathMethod ]( cols );
32230 this.cols = Math.max( cols, 1 );
32231 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32233 // padding positioning..
32234 var totalColWidth = this.cols * this.columnWidth;
32235 var padavail = this.containerWidth - totalColWidth;
32236 // so for 2 columns - we need 3 'pads'
32238 var padNeeded = (1+this.cols) * this.padWidth;
32240 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32242 this.columnWidth += padExtra
32243 //this.padWidth = Math.floor(padavail / ( this.cols));
32245 // adjust colum width so that padding is fixed??
32247 // we have 3 columns ... total = width * 3
32248 // we have X left over... that should be used by
32250 //if (this.expandC) {
32258 getContainerWidth : function()
32260 /* // container is parent if fit width
32261 var container = this.isFitWidth ? this.element.parentNode : this.element;
32262 // check that this.size and size are there
32263 // IE8 triggers resize on body size change, so they might not be
32265 var size = getSize( container ); //FIXME
32266 this.containerWidth = size && size.innerWidth; //FIXME
32269 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32273 _getItemLayoutPosition : function( item ) // what is item?
32275 // we resize the item to our columnWidth..
32277 item.setWidth(this.columnWidth);
32278 item.autoBoxAdjust = false;
32280 var sz = item.getSize();
32282 // how many columns does this brick span
32283 var remainder = this.containerWidth % this.columnWidth;
32285 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32286 // round if off by 1 pixel, otherwise use ceil
32287 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32288 colSpan = Math.min( colSpan, this.cols );
32290 // normally this should be '1' as we dont' currently allow multi width columns..
32292 var colGroup = this._getColGroup( colSpan );
32293 // get the minimum Y value from the columns
32294 var minimumY = Math.min.apply( Math, colGroup );
32295 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32297 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32299 // position the brick
32301 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32302 y: this.currentSize.y + minimumY + this.padHeight
32306 // apply setHeight to necessary columns
32307 var setHeight = minimumY + sz.height + this.padHeight;
32308 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32310 var setSpan = this.cols + 1 - colGroup.length;
32311 for ( var i = 0; i < setSpan; i++ ) {
32312 this.colYs[ shortColIndex + i ] = setHeight ;
32319 * @param {Number} colSpan - number of columns the element spans
32320 * @returns {Array} colGroup
32322 _getColGroup : function( colSpan )
32324 if ( colSpan < 2 ) {
32325 // if brick spans only one column, use all the column Ys
32330 // how many different places could this brick fit horizontally
32331 var groupCount = this.cols + 1 - colSpan;
32332 // for each group potential horizontal position
32333 for ( var i = 0; i < groupCount; i++ ) {
32334 // make an array of colY values for that one group
32335 var groupColYs = this.colYs.slice( i, i + colSpan );
32336 // and get the max value of the array
32337 colGroup[i] = Math.max.apply( Math, groupColYs );
32342 _manageStamp : function( stamp )
32344 var stampSize = stamp.getSize();
32345 var offset = stamp.getBox();
32346 // get the columns that this stamp affects
32347 var firstX = this.isOriginLeft ? offset.x : offset.right;
32348 var lastX = firstX + stampSize.width;
32349 var firstCol = Math.floor( firstX / this.columnWidth );
32350 firstCol = Math.max( 0, firstCol );
32352 var lastCol = Math.floor( lastX / this.columnWidth );
32353 // lastCol should not go over if multiple of columnWidth #425
32354 lastCol -= lastX % this.columnWidth ? 0 : 1;
32355 lastCol = Math.min( this.cols - 1, lastCol );
32357 // set colYs to bottom of the stamp
32358 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32361 for ( var i = firstCol; i <= lastCol; i++ ) {
32362 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32367 _getContainerSize : function()
32369 this.maxY = Math.max.apply( Math, this.colYs );
32374 if ( this.isFitWidth ) {
32375 size.width = this._getContainerFitWidth();
32381 _getContainerFitWidth : function()
32383 var unusedCols = 0;
32384 // count unused columns
32387 if ( this.colYs[i] !== 0 ) {
32392 // fit container to columns that have been used
32393 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32396 needsResizeLayout : function()
32398 var previousWidth = this.containerWidth;
32399 this.getContainerWidth();
32400 return previousWidth !== this.containerWidth;
32415 * @class Roo.bootstrap.MasonryBrick
32416 * @extends Roo.bootstrap.Component
32417 * Bootstrap MasonryBrick class
32420 * Create a new MasonryBrick
32421 * @param {Object} config The config object
32424 Roo.bootstrap.MasonryBrick = function(config){
32426 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32428 Roo.bootstrap.MasonryBrick.register(this);
32434 * When a MasonryBrick is clcik
32435 * @param {Roo.bootstrap.MasonryBrick} this
32436 * @param {Roo.EventObject} e
32442 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32445 * @cfg {String} title
32449 * @cfg {String} html
32453 * @cfg {String} bgimage
32457 * @cfg {String} videourl
32461 * @cfg {String} cls
32465 * @cfg {String} href
32469 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32474 * @cfg {String} placetitle (center|bottom)
32479 * @cfg {Boolean} isFitContainer defalut true
32481 isFitContainer : true,
32484 * @cfg {Boolean} preventDefault defalut false
32486 preventDefault : false,
32489 * @cfg {Boolean} inverse defalut false
32491 maskInverse : false,
32493 getAutoCreate : function()
32495 if(!this.isFitContainer){
32496 return this.getSplitAutoCreate();
32499 var cls = 'masonry-brick masonry-brick-full';
32501 if(this.href.length){
32502 cls += ' masonry-brick-link';
32505 if(this.bgimage.length){
32506 cls += ' masonry-brick-image';
32509 if(this.maskInverse){
32510 cls += ' mask-inverse';
32513 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32514 cls += ' enable-mask';
32518 cls += ' masonry-' + this.size + '-brick';
32521 if(this.placetitle.length){
32523 switch (this.placetitle) {
32525 cls += ' masonry-center-title';
32528 cls += ' masonry-bottom-title';
32535 if(!this.html.length && !this.bgimage.length){
32536 cls += ' masonry-center-title';
32539 if(!this.html.length && this.bgimage.length){
32540 cls += ' masonry-bottom-title';
32545 cls += ' ' + this.cls;
32549 tag: (this.href.length) ? 'a' : 'div',
32554 cls: 'masonry-brick-mask'
32558 cls: 'masonry-brick-paragraph',
32564 if(this.href.length){
32565 cfg.href = this.href;
32568 var cn = cfg.cn[1].cn;
32570 if(this.title.length){
32573 cls: 'masonry-brick-title',
32578 if(this.html.length){
32581 cls: 'masonry-brick-text',
32586 if (!this.title.length && !this.html.length) {
32587 cfg.cn[1].cls += ' hide';
32590 if(this.bgimage.length){
32593 cls: 'masonry-brick-image-view',
32598 if(this.videourl.length){
32599 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32600 // youtube support only?
32603 cls: 'masonry-brick-image-view',
32606 allowfullscreen : true
32614 getSplitAutoCreate : function()
32616 var cls = 'masonry-brick masonry-brick-split';
32618 if(this.href.length){
32619 cls += ' masonry-brick-link';
32622 if(this.bgimage.length){
32623 cls += ' masonry-brick-image';
32627 cls += ' masonry-' + this.size + '-brick';
32630 switch (this.placetitle) {
32632 cls += ' masonry-center-title';
32635 cls += ' masonry-bottom-title';
32638 if(!this.bgimage.length){
32639 cls += ' masonry-center-title';
32642 if(this.bgimage.length){
32643 cls += ' masonry-bottom-title';
32649 cls += ' ' + this.cls;
32653 tag: (this.href.length) ? 'a' : 'div',
32658 cls: 'masonry-brick-split-head',
32662 cls: 'masonry-brick-paragraph',
32669 cls: 'masonry-brick-split-body',
32675 if(this.href.length){
32676 cfg.href = this.href;
32679 if(this.title.length){
32680 cfg.cn[0].cn[0].cn.push({
32682 cls: 'masonry-brick-title',
32687 if(this.html.length){
32688 cfg.cn[1].cn.push({
32690 cls: 'masonry-brick-text',
32695 if(this.bgimage.length){
32696 cfg.cn[0].cn.push({
32698 cls: 'masonry-brick-image-view',
32703 if(this.videourl.length){
32704 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32705 // youtube support only?
32706 cfg.cn[0].cn.cn.push({
32708 cls: 'masonry-brick-image-view',
32711 allowfullscreen : true
32718 initEvents: function()
32720 switch (this.size) {
32753 this.el.on('touchstart', this.onTouchStart, this);
32754 this.el.on('touchmove', this.onTouchMove, this);
32755 this.el.on('touchend', this.onTouchEnd, this);
32756 this.el.on('contextmenu', this.onContextMenu, this);
32758 this.el.on('mouseenter' ,this.enter, this);
32759 this.el.on('mouseleave', this.leave, this);
32760 this.el.on('click', this.onClick, this);
32763 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32764 this.parent().bricks.push(this);
32769 onClick: function(e, el)
32771 var time = this.endTimer - this.startTimer;
32772 // Roo.log(e.preventDefault());
32775 e.preventDefault();
32780 if(!this.preventDefault){
32784 e.preventDefault();
32786 if (this.activeClass != '') {
32787 this.selectBrick();
32790 this.fireEvent('click', this, e);
32793 enter: function(e, el)
32795 e.preventDefault();
32797 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32801 if(this.bgimage.length && this.html.length){
32802 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32806 leave: function(e, el)
32808 e.preventDefault();
32810 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32814 if(this.bgimage.length && this.html.length){
32815 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32819 onTouchStart: function(e, el)
32821 // e.preventDefault();
32823 this.touchmoved = false;
32825 if(!this.isFitContainer){
32829 if(!this.bgimage.length || !this.html.length){
32833 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32835 this.timer = new Date().getTime();
32839 onTouchMove: function(e, el)
32841 this.touchmoved = true;
32844 onContextMenu : function(e,el)
32846 e.preventDefault();
32847 e.stopPropagation();
32851 onTouchEnd: function(e, el)
32853 // e.preventDefault();
32855 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32862 if(!this.bgimage.length || !this.html.length){
32864 if(this.href.length){
32865 window.location.href = this.href;
32871 if(!this.isFitContainer){
32875 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32877 window.location.href = this.href;
32880 //selection on single brick only
32881 selectBrick : function() {
32883 if (!this.parentId) {
32887 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32888 var index = m.selectedBrick.indexOf(this.id);
32891 m.selectedBrick.splice(index,1);
32892 this.el.removeClass(this.activeClass);
32896 for(var i = 0; i < m.selectedBrick.length; i++) {
32897 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32898 b.el.removeClass(b.activeClass);
32901 m.selectedBrick = [];
32903 m.selectedBrick.push(this.id);
32904 this.el.addClass(this.activeClass);
32908 isSelected : function(){
32909 return this.el.hasClass(this.activeClass);
32914 Roo.apply(Roo.bootstrap.MasonryBrick, {
32917 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32919 * register a Masonry Brick
32920 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32923 register : function(brick)
32925 //this.groups[brick.id] = brick;
32926 this.groups.add(brick.id, brick);
32929 * fetch a masonry brick based on the masonry brick ID
32930 * @param {string} the masonry brick to add
32931 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32934 get: function(brick_id)
32936 // if (typeof(this.groups[brick_id]) == 'undefined') {
32939 // return this.groups[brick_id] ;
32941 if(this.groups.key(brick_id)) {
32942 return this.groups.key(brick_id);
32960 * @class Roo.bootstrap.Brick
32961 * @extends Roo.bootstrap.Component
32962 * Bootstrap Brick class
32965 * Create a new Brick
32966 * @param {Object} config The config object
32969 Roo.bootstrap.Brick = function(config){
32970 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32976 * When a Brick is click
32977 * @param {Roo.bootstrap.Brick} this
32978 * @param {Roo.EventObject} e
32984 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
32987 * @cfg {String} title
32991 * @cfg {String} html
32995 * @cfg {String} bgimage
32999 * @cfg {String} cls
33003 * @cfg {String} href
33007 * @cfg {String} video
33011 * @cfg {Boolean} square
33015 getAutoCreate : function()
33017 var cls = 'roo-brick';
33019 if(this.href.length){
33020 cls += ' roo-brick-link';
33023 if(this.bgimage.length){
33024 cls += ' roo-brick-image';
33027 if(!this.html.length && !this.bgimage.length){
33028 cls += ' roo-brick-center-title';
33031 if(!this.html.length && this.bgimage.length){
33032 cls += ' roo-brick-bottom-title';
33036 cls += ' ' + this.cls;
33040 tag: (this.href.length) ? 'a' : 'div',
33045 cls: 'roo-brick-paragraph',
33051 if(this.href.length){
33052 cfg.href = this.href;
33055 var cn = cfg.cn[0].cn;
33057 if(this.title.length){
33060 cls: 'roo-brick-title',
33065 if(this.html.length){
33068 cls: 'roo-brick-text',
33075 if(this.bgimage.length){
33078 cls: 'roo-brick-image-view',
33086 initEvents: function()
33088 if(this.title.length || this.html.length){
33089 this.el.on('mouseenter' ,this.enter, this);
33090 this.el.on('mouseleave', this.leave, this);
33093 Roo.EventManager.onWindowResize(this.resize, this);
33095 if(this.bgimage.length){
33096 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33097 this.imageEl.on('load', this.onImageLoad, this);
33104 onImageLoad : function()
33109 resize : function()
33111 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33113 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33115 if(this.bgimage.length){
33116 var image = this.el.select('.roo-brick-image-view', true).first();
33118 image.setWidth(paragraph.getWidth());
33121 image.setHeight(paragraph.getWidth());
33124 this.el.setHeight(image.getHeight());
33125 paragraph.setHeight(image.getHeight());
33131 enter: function(e, el)
33133 e.preventDefault();
33135 if(this.bgimage.length){
33136 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33137 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33141 leave: function(e, el)
33143 e.preventDefault();
33145 if(this.bgimage.length){
33146 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33147 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33162 * @class Roo.bootstrap.NumberField
33163 * @extends Roo.bootstrap.Input
33164 * Bootstrap NumberField class
33170 * Create a new NumberField
33171 * @param {Object} config The config object
33174 Roo.bootstrap.NumberField = function(config){
33175 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33178 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33181 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33183 allowDecimals : true,
33185 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33187 decimalSeparator : ".",
33189 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33191 decimalPrecision : 2,
33193 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33195 allowNegative : true,
33198 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33202 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33204 minValue : Number.NEGATIVE_INFINITY,
33206 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33208 maxValue : Number.MAX_VALUE,
33210 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33212 minText : "The minimum value for this field is {0}",
33214 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33216 maxText : "The maximum value for this field is {0}",
33218 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33219 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33221 nanText : "{0} is not a valid number",
33223 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33227 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33229 thousandsDelimiter : false,
33231 * @cfg {String} valueAlign alignment of value
33233 valueAlign : "left",
33235 getAutoCreate : function()
33237 var hiddenInput = {
33241 cls: 'hidden-number-input'
33245 hiddenInput.name = this.name;
33250 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33252 this.name = hiddenInput.name;
33254 if(cfg.cn.length > 0) {
33255 cfg.cn.push(hiddenInput);
33262 initEvents : function()
33264 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33266 var allowed = "0123456789";
33268 if(this.allowDecimals){
33269 allowed += this.decimalSeparator;
33272 if(this.allowNegative){
33276 if(this.thousandsDelimiter) {
33280 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33282 var keyPress = function(e){
33284 var k = e.getKey();
33286 var c = e.getCharCode();
33289 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33290 allowed.indexOf(String.fromCharCode(c)) === -1
33296 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33300 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33305 this.el.on("keypress", keyPress, this);
33308 validateValue : function(value)
33311 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33315 var num = this.parseValue(value);
33318 this.markInvalid(String.format(this.nanText, value));
33322 if(num < this.minValue){
33323 this.markInvalid(String.format(this.minText, this.minValue));
33327 if(num > this.maxValue){
33328 this.markInvalid(String.format(this.maxText, this.maxValue));
33335 getValue : function()
33337 var v = this.hiddenEl().getValue();
33339 return this.fixPrecision(this.parseValue(v));
33342 parseValue : function(value)
33344 if(this.thousandsDelimiter) {
33346 r = new RegExp(",", "g");
33347 value = value.replace(r, "");
33350 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33351 return isNaN(value) ? '' : value;
33354 fixPrecision : function(value)
33356 if(this.thousandsDelimiter) {
33358 r = new RegExp(",", "g");
33359 value = value.replace(r, "");
33362 var nan = isNaN(value);
33364 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33365 return nan ? '' : value;
33367 return parseFloat(value).toFixed(this.decimalPrecision);
33370 setValue : function(v)
33372 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33378 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33380 this.inputEl().dom.value = (v == '') ? '' :
33381 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33383 if(!this.allowZero && v === '0') {
33384 this.hiddenEl().dom.value = '';
33385 this.inputEl().dom.value = '';
33392 decimalPrecisionFcn : function(v)
33394 return Math.floor(v);
33397 beforeBlur : function()
33403 var v = this.parseValue(this.getRawValue());
33410 hiddenEl : function()
33412 return this.el.select('input.hidden-number-input',true).first();
33424 * @class Roo.bootstrap.DocumentSlider
33425 * @extends Roo.bootstrap.Component
33426 * Bootstrap DocumentSlider class
33429 * Create a new DocumentViewer
33430 * @param {Object} config The config object
33433 Roo.bootstrap.DocumentSlider = function(config){
33434 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33441 * Fire after initEvent
33442 * @param {Roo.bootstrap.DocumentSlider} this
33447 * Fire after update
33448 * @param {Roo.bootstrap.DocumentSlider} this
33454 * @param {Roo.bootstrap.DocumentSlider} this
33460 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33466 getAutoCreate : function()
33470 cls : 'roo-document-slider',
33474 cls : 'roo-document-slider-header',
33478 cls : 'roo-document-slider-header-title'
33484 cls : 'roo-document-slider-body',
33488 cls : 'roo-document-slider-prev',
33492 cls : 'fa fa-chevron-left'
33498 cls : 'roo-document-slider-thumb',
33502 cls : 'roo-document-slider-image'
33508 cls : 'roo-document-slider-next',
33512 cls : 'fa fa-chevron-right'
33524 initEvents : function()
33526 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33527 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33529 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33530 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33532 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33533 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33535 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33536 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33538 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33539 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33541 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33542 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33544 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33545 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33547 this.thumbEl.on('click', this.onClick, this);
33549 this.prevIndicator.on('click', this.prev, this);
33551 this.nextIndicator.on('click', this.next, this);
33555 initial : function()
33557 if(this.files.length){
33558 this.indicator = 1;
33562 this.fireEvent('initial', this);
33565 update : function()
33567 this.imageEl.attr('src', this.files[this.indicator - 1]);
33569 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33571 this.prevIndicator.show();
33573 if(this.indicator == 1){
33574 this.prevIndicator.hide();
33577 this.nextIndicator.show();
33579 if(this.indicator == this.files.length){
33580 this.nextIndicator.hide();
33583 this.thumbEl.scrollTo('top');
33585 this.fireEvent('update', this);
33588 onClick : function(e)
33590 e.preventDefault();
33592 this.fireEvent('click', this);
33597 e.preventDefault();
33599 this.indicator = Math.max(1, this.indicator - 1);
33606 e.preventDefault();
33608 this.indicator = Math.min(this.files.length, this.indicator + 1);
33622 * @class Roo.bootstrap.RadioSet
33623 * @extends Roo.bootstrap.Input
33624 * Bootstrap RadioSet class
33625 * @cfg {String} indicatorpos (left|right) default left
33626 * @cfg {Boolean} inline (true|false) inline the element (default true)
33627 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33629 * Create a new RadioSet
33630 * @param {Object} config The config object
33633 Roo.bootstrap.RadioSet = function(config){
33635 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33639 Roo.bootstrap.RadioSet.register(this);
33644 * Fires when the element is checked or unchecked.
33645 * @param {Roo.bootstrap.RadioSet} this This radio
33646 * @param {Roo.bootstrap.Radio} item The checked item
33651 * Fires when the element is click.
33652 * @param {Roo.bootstrap.RadioSet} this This radio set
33653 * @param {Roo.bootstrap.Radio} item The checked item
33654 * @param {Roo.EventObject} e The event object
33661 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33669 indicatorpos : 'left',
33671 getAutoCreate : function()
33675 cls : 'roo-radio-set-label',
33679 html : this.fieldLabel
33684 if(this.indicatorpos == 'left'){
33687 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33688 tooltip : 'This field is required'
33693 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33694 tooltip : 'This field is required'
33700 cls : 'roo-radio-set-items'
33703 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33705 if (align === 'left' && this.fieldLabel.length) {
33708 cls : "roo-radio-set-right",
33714 if(this.labelWidth > 12){
33715 label.style = "width: " + this.labelWidth + 'px';
33718 if(this.labelWidth < 13 && this.labelmd == 0){
33719 this.labelmd = this.labelWidth;
33722 if(this.labellg > 0){
33723 label.cls += ' col-lg-' + this.labellg;
33724 items.cls += ' col-lg-' + (12 - this.labellg);
33727 if(this.labelmd > 0){
33728 label.cls += ' col-md-' + this.labelmd;
33729 items.cls += ' col-md-' + (12 - this.labelmd);
33732 if(this.labelsm > 0){
33733 label.cls += ' col-sm-' + this.labelsm;
33734 items.cls += ' col-sm-' + (12 - this.labelsm);
33737 if(this.labelxs > 0){
33738 label.cls += ' col-xs-' + this.labelxs;
33739 items.cls += ' col-xs-' + (12 - this.labelxs);
33745 cls : 'roo-radio-set',
33749 cls : 'roo-radio-set-input',
33752 value : this.value ? this.value : ''
33759 if(this.weight.length){
33760 cfg.cls += ' roo-radio-' + this.weight;
33764 cfg.cls += ' roo-radio-set-inline';
33768 ['xs','sm','md','lg'].map(function(size){
33769 if (settings[size]) {
33770 cfg.cls += ' col-' + size + '-' + settings[size];
33778 initEvents : function()
33780 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33781 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33783 if(!this.fieldLabel.length){
33784 this.labelEl.hide();
33787 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33788 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33790 this.indicator = this.indicatorEl();
33792 if(this.indicator){
33793 this.indicator.addClass('invisible');
33796 this.originalValue = this.getValue();
33800 inputEl: function ()
33802 return this.el.select('.roo-radio-set-input', true).first();
33805 getChildContainer : function()
33807 return this.itemsEl;
33810 register : function(item)
33812 this.radioes.push(item);
33816 validate : function()
33818 if(this.getVisibilityEl().hasClass('hidden')){
33824 Roo.each(this.radioes, function(i){
33833 if(this.allowBlank) {
33837 if(this.disabled || valid){
33842 this.markInvalid();
33847 markValid : function()
33849 if(this.labelEl.isVisible(true)){
33850 this.indicatorEl().removeClass('visible');
33851 this.indicatorEl().addClass('invisible');
33854 this.el.removeClass([this.invalidClass, this.validClass]);
33855 this.el.addClass(this.validClass);
33857 this.fireEvent('valid', this);
33860 markInvalid : function(msg)
33862 if(this.allowBlank || this.disabled){
33866 if(this.labelEl.isVisible(true)){
33867 this.indicatorEl().removeClass('invisible');
33868 this.indicatorEl().addClass('visible');
33871 this.el.removeClass([this.invalidClass, this.validClass]);
33872 this.el.addClass(this.invalidClass);
33874 this.fireEvent('invalid', this, msg);
33878 setValue : function(v, suppressEvent)
33880 if(this.value === v){
33887 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33890 Roo.each(this.radioes, function(i){
33892 i.el.removeClass('checked');
33895 Roo.each(this.radioes, function(i){
33897 if(i.value === v || i.value.toString() === v.toString()){
33899 i.el.addClass('checked');
33901 if(suppressEvent !== true){
33902 this.fireEvent('check', this, i);
33913 clearInvalid : function(){
33915 if(!this.el || this.preventMark){
33919 this.el.removeClass([this.invalidClass]);
33921 this.fireEvent('valid', this);
33926 Roo.apply(Roo.bootstrap.RadioSet, {
33930 register : function(set)
33932 this.groups[set.name] = set;
33935 get: function(name)
33937 if (typeof(this.groups[name]) == 'undefined') {
33941 return this.groups[name] ;
33947 * Ext JS Library 1.1.1
33948 * Copyright(c) 2006-2007, Ext JS, LLC.
33950 * Originally Released Under LGPL - original licence link has changed is not relivant.
33953 * <script type="text/javascript">
33958 * @class Roo.bootstrap.SplitBar
33959 * @extends Roo.util.Observable
33960 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33964 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33965 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33966 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33967 split.minSize = 100;
33968 split.maxSize = 600;
33969 split.animate = true;
33970 split.on('moved', splitterMoved);
33973 * Create a new SplitBar
33974 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
33975 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
33976 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33977 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
33978 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33979 position of the SplitBar).
33981 Roo.bootstrap.SplitBar = function(cfg){
33986 // dragElement : elm
33987 // resizingElement: el,
33989 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33990 // placement : Roo.bootstrap.SplitBar.LEFT ,
33991 // existingProxy ???
33994 this.el = Roo.get(cfg.dragElement, true);
33995 this.el.dom.unselectable = "on";
33997 this.resizingEl = Roo.get(cfg.resizingElement, true);
34001 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34002 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34005 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34008 * The minimum size of the resizing element. (Defaults to 0)
34014 * The maximum size of the resizing element. (Defaults to 2000)
34017 this.maxSize = 2000;
34020 * Whether to animate the transition to the new size
34023 this.animate = false;
34026 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34029 this.useShim = false;
34034 if(!cfg.existingProxy){
34036 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34038 this.proxy = Roo.get(cfg.existingProxy).dom;
34041 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34044 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34047 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34050 this.dragSpecs = {};
34053 * @private The adapter to use to positon and resize elements
34055 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34056 this.adapter.init(this);
34058 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34060 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34061 this.el.addClass("roo-splitbar-h");
34064 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34065 this.el.addClass("roo-splitbar-v");
34071 * Fires when the splitter is moved (alias for {@link #event-moved})
34072 * @param {Roo.bootstrap.SplitBar} this
34073 * @param {Number} newSize the new width or height
34078 * Fires when the splitter is moved
34079 * @param {Roo.bootstrap.SplitBar} this
34080 * @param {Number} newSize the new width or height
34084 * @event beforeresize
34085 * Fires before the splitter is dragged
34086 * @param {Roo.bootstrap.SplitBar} this
34088 "beforeresize" : true,
34090 "beforeapply" : true
34093 Roo.util.Observable.call(this);
34096 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34097 onStartProxyDrag : function(x, y){
34098 this.fireEvent("beforeresize", this);
34100 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34102 o.enableDisplayMode("block");
34103 // all splitbars share the same overlay
34104 Roo.bootstrap.SplitBar.prototype.overlay = o;
34106 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34107 this.overlay.show();
34108 Roo.get(this.proxy).setDisplayed("block");
34109 var size = this.adapter.getElementSize(this);
34110 this.activeMinSize = this.getMinimumSize();;
34111 this.activeMaxSize = this.getMaximumSize();;
34112 var c1 = size - this.activeMinSize;
34113 var c2 = Math.max(this.activeMaxSize - size, 0);
34114 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34115 this.dd.resetConstraints();
34116 this.dd.setXConstraint(
34117 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34118 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34120 this.dd.setYConstraint(0, 0);
34122 this.dd.resetConstraints();
34123 this.dd.setXConstraint(0, 0);
34124 this.dd.setYConstraint(
34125 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34126 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34129 this.dragSpecs.startSize = size;
34130 this.dragSpecs.startPoint = [x, y];
34131 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34135 * @private Called after the drag operation by the DDProxy
34137 onEndProxyDrag : function(e){
34138 Roo.get(this.proxy).setDisplayed(false);
34139 var endPoint = Roo.lib.Event.getXY(e);
34141 this.overlay.hide();
34144 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34145 newSize = this.dragSpecs.startSize +
34146 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34147 endPoint[0] - this.dragSpecs.startPoint[0] :
34148 this.dragSpecs.startPoint[0] - endPoint[0]
34151 newSize = this.dragSpecs.startSize +
34152 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34153 endPoint[1] - this.dragSpecs.startPoint[1] :
34154 this.dragSpecs.startPoint[1] - endPoint[1]
34157 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34158 if(newSize != this.dragSpecs.startSize){
34159 if(this.fireEvent('beforeapply', this, newSize) !== false){
34160 this.adapter.setElementSize(this, newSize);
34161 this.fireEvent("moved", this, newSize);
34162 this.fireEvent("resize", this, newSize);
34168 * Get the adapter this SplitBar uses
34169 * @return The adapter object
34171 getAdapter : function(){
34172 return this.adapter;
34176 * Set the adapter this SplitBar uses
34177 * @param {Object} adapter A SplitBar adapter object
34179 setAdapter : function(adapter){
34180 this.adapter = adapter;
34181 this.adapter.init(this);
34185 * Gets the minimum size for the resizing element
34186 * @return {Number} The minimum size
34188 getMinimumSize : function(){
34189 return this.minSize;
34193 * Sets the minimum size for the resizing element
34194 * @param {Number} minSize The minimum size
34196 setMinimumSize : function(minSize){
34197 this.minSize = minSize;
34201 * Gets the maximum size for the resizing element
34202 * @return {Number} The maximum size
34204 getMaximumSize : function(){
34205 return this.maxSize;
34209 * Sets the maximum size for the resizing element
34210 * @param {Number} maxSize The maximum size
34212 setMaximumSize : function(maxSize){
34213 this.maxSize = maxSize;
34217 * Sets the initialize size for the resizing element
34218 * @param {Number} size The initial size
34220 setCurrentSize : function(size){
34221 var oldAnimate = this.animate;
34222 this.animate = false;
34223 this.adapter.setElementSize(this, size);
34224 this.animate = oldAnimate;
34228 * Destroy this splitbar.
34229 * @param {Boolean} removeEl True to remove the element
34231 destroy : function(removeEl){
34233 this.shim.remove();
34236 this.proxy.parentNode.removeChild(this.proxy);
34244 * @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.
34246 Roo.bootstrap.SplitBar.createProxy = function(dir){
34247 var proxy = new Roo.Element(document.createElement("div"));
34248 proxy.unselectable();
34249 var cls = 'roo-splitbar-proxy';
34250 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34251 document.body.appendChild(proxy.dom);
34256 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34257 * Default Adapter. It assumes the splitter and resizing element are not positioned
34258 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34260 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34263 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34264 // do nothing for now
34265 init : function(s){
34269 * Called before drag operations to get the current size of the resizing element.
34270 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34272 getElementSize : function(s){
34273 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34274 return s.resizingEl.getWidth();
34276 return s.resizingEl.getHeight();
34281 * Called after drag operations to set the size of the resizing element.
34282 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34283 * @param {Number} newSize The new size to set
34284 * @param {Function} onComplete A function to be invoked when resizing is complete
34286 setElementSize : function(s, newSize, onComplete){
34287 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34289 s.resizingEl.setWidth(newSize);
34291 onComplete(s, newSize);
34294 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34299 s.resizingEl.setHeight(newSize);
34301 onComplete(s, newSize);
34304 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34311 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34312 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34313 * Adapter that moves the splitter element to align with the resized sizing element.
34314 * Used with an absolute positioned SplitBar.
34315 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34316 * document.body, make sure you assign an id to the body element.
34318 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34319 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34320 this.container = Roo.get(container);
34323 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34324 init : function(s){
34325 this.basic.init(s);
34328 getElementSize : function(s){
34329 return this.basic.getElementSize(s);
34332 setElementSize : function(s, newSize, onComplete){
34333 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34336 moveSplitter : function(s){
34337 var yes = Roo.bootstrap.SplitBar;
34338 switch(s.placement){
34340 s.el.setX(s.resizingEl.getRight());
34343 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34346 s.el.setY(s.resizingEl.getBottom());
34349 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34356 * Orientation constant - Create a vertical SplitBar
34360 Roo.bootstrap.SplitBar.VERTICAL = 1;
34363 * Orientation constant - Create a horizontal SplitBar
34367 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34370 * Placement constant - The resizing element is to the left of the splitter element
34374 Roo.bootstrap.SplitBar.LEFT = 1;
34377 * Placement constant - The resizing element is to the right of the splitter element
34381 Roo.bootstrap.SplitBar.RIGHT = 2;
34384 * Placement constant - The resizing element is positioned above the splitter element
34388 Roo.bootstrap.SplitBar.TOP = 3;
34391 * Placement constant - The resizing element is positioned under splitter element
34395 Roo.bootstrap.SplitBar.BOTTOM = 4;
34396 Roo.namespace("Roo.bootstrap.layout");/*
34398 * Ext JS Library 1.1.1
34399 * Copyright(c) 2006-2007, Ext JS, LLC.
34401 * Originally Released Under LGPL - original licence link has changed is not relivant.
34404 * <script type="text/javascript">
34408 * @class Roo.bootstrap.layout.Manager
34409 * @extends Roo.bootstrap.Component
34410 * Base class for layout managers.
34412 Roo.bootstrap.layout.Manager = function(config)
34414 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34420 /** false to disable window resize monitoring @type Boolean */
34421 this.monitorWindowResize = true;
34426 * Fires when a layout is performed.
34427 * @param {Roo.LayoutManager} this
34431 * @event regionresized
34432 * Fires when the user resizes a region.
34433 * @param {Roo.LayoutRegion} region The resized region
34434 * @param {Number} newSize The new size (width for east/west, height for north/south)
34436 "regionresized" : true,
34438 * @event regioncollapsed
34439 * Fires when a region is collapsed.
34440 * @param {Roo.LayoutRegion} region The collapsed region
34442 "regioncollapsed" : true,
34444 * @event regionexpanded
34445 * Fires when a region is expanded.
34446 * @param {Roo.LayoutRegion} region The expanded region
34448 "regionexpanded" : true
34450 this.updating = false;
34453 this.el = Roo.get(config.el);
34459 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34464 monitorWindowResize : true,
34470 onRender : function(ct, position)
34473 this.el = Roo.get(ct);
34476 //this.fireEvent('render',this);
34480 initEvents: function()
34484 // ie scrollbar fix
34485 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34486 document.body.scroll = "no";
34487 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34488 this.el.position('relative');
34490 this.id = this.el.id;
34491 this.el.addClass("roo-layout-container");
34492 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34493 if(this.el.dom != document.body ) {
34494 this.el.on('resize', this.layout,this);
34495 this.el.on('show', this.layout,this);
34501 * Returns true if this layout is currently being updated
34502 * @return {Boolean}
34504 isUpdating : function(){
34505 return this.updating;
34509 * Suspend the LayoutManager from doing auto-layouts while
34510 * making multiple add or remove calls
34512 beginUpdate : function(){
34513 this.updating = true;
34517 * Restore auto-layouts and optionally disable the manager from performing a layout
34518 * @param {Boolean} noLayout true to disable a layout update
34520 endUpdate : function(noLayout){
34521 this.updating = false;
34527 layout: function(){
34531 onRegionResized : function(region, newSize){
34532 this.fireEvent("regionresized", region, newSize);
34536 onRegionCollapsed : function(region){
34537 this.fireEvent("regioncollapsed", region);
34540 onRegionExpanded : function(region){
34541 this.fireEvent("regionexpanded", region);
34545 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34546 * performs box-model adjustments.
34547 * @return {Object} The size as an object {width: (the width), height: (the height)}
34549 getViewSize : function()
34552 if(this.el.dom != document.body){
34553 size = this.el.getSize();
34555 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34557 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34558 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34563 * Returns the Element this layout is bound to.
34564 * @return {Roo.Element}
34566 getEl : function(){
34571 * Returns the specified region.
34572 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34573 * @return {Roo.LayoutRegion}
34575 getRegion : function(target){
34576 return this.regions[target.toLowerCase()];
34579 onWindowResize : function(){
34580 if(this.monitorWindowResize){
34587 * Ext JS Library 1.1.1
34588 * Copyright(c) 2006-2007, Ext JS, LLC.
34590 * Originally Released Under LGPL - original licence link has changed is not relivant.
34593 * <script type="text/javascript">
34596 * @class Roo.bootstrap.layout.Border
34597 * @extends Roo.bootstrap.layout.Manager
34598 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34599 * please see: examples/bootstrap/nested.html<br><br>
34601 <b>The container the layout is rendered into can be either the body element or any other element.
34602 If it is not the body element, the container needs to either be an absolute positioned element,
34603 or you will need to add "position:relative" to the css of the container. You will also need to specify
34604 the container size if it is not the body element.</b>
34607 * Create a new Border
34608 * @param {Object} config Configuration options
34610 Roo.bootstrap.layout.Border = function(config){
34611 config = config || {};
34612 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34616 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34617 if(config[region]){
34618 config[region].region = region;
34619 this.addRegion(config[region]);
34625 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34627 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34629 * Creates and adds a new region if it doesn't already exist.
34630 * @param {String} target The target region key (north, south, east, west or center).
34631 * @param {Object} config The regions config object
34632 * @return {BorderLayoutRegion} The new region
34634 addRegion : function(config)
34636 if(!this.regions[config.region]){
34637 var r = this.factory(config);
34638 this.bindRegion(r);
34640 return this.regions[config.region];
34644 bindRegion : function(r){
34645 this.regions[r.config.region] = r;
34647 r.on("visibilitychange", this.layout, this);
34648 r.on("paneladded", this.layout, this);
34649 r.on("panelremoved", this.layout, this);
34650 r.on("invalidated", this.layout, this);
34651 r.on("resized", this.onRegionResized, this);
34652 r.on("collapsed", this.onRegionCollapsed, this);
34653 r.on("expanded", this.onRegionExpanded, this);
34657 * Performs a layout update.
34659 layout : function()
34661 if(this.updating) {
34665 // render all the rebions if they have not been done alreayd?
34666 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34667 if(this.regions[region] && !this.regions[region].bodyEl){
34668 this.regions[region].onRender(this.el)
34672 var size = this.getViewSize();
34673 var w = size.width;
34674 var h = size.height;
34679 //var x = 0, y = 0;
34681 var rs = this.regions;
34682 var north = rs["north"];
34683 var south = rs["south"];
34684 var west = rs["west"];
34685 var east = rs["east"];
34686 var center = rs["center"];
34687 //if(this.hideOnLayout){ // not supported anymore
34688 //c.el.setStyle("display", "none");
34690 if(north && north.isVisible()){
34691 var b = north.getBox();
34692 var m = north.getMargins();
34693 b.width = w - (m.left+m.right);
34696 centerY = b.height + b.y + m.bottom;
34697 centerH -= centerY;
34698 north.updateBox(this.safeBox(b));
34700 if(south && south.isVisible()){
34701 var b = south.getBox();
34702 var m = south.getMargins();
34703 b.width = w - (m.left+m.right);
34705 var totalHeight = (b.height + m.top + m.bottom);
34706 b.y = h - totalHeight + m.top;
34707 centerH -= totalHeight;
34708 south.updateBox(this.safeBox(b));
34710 if(west && west.isVisible()){
34711 var b = west.getBox();
34712 var m = west.getMargins();
34713 b.height = centerH - (m.top+m.bottom);
34715 b.y = centerY + m.top;
34716 var totalWidth = (b.width + m.left + m.right);
34717 centerX += totalWidth;
34718 centerW -= totalWidth;
34719 west.updateBox(this.safeBox(b));
34721 if(east && east.isVisible()){
34722 var b = east.getBox();
34723 var m = east.getMargins();
34724 b.height = centerH - (m.top+m.bottom);
34725 var totalWidth = (b.width + m.left + m.right);
34726 b.x = w - totalWidth + m.left;
34727 b.y = centerY + m.top;
34728 centerW -= totalWidth;
34729 east.updateBox(this.safeBox(b));
34732 var m = center.getMargins();
34734 x: centerX + m.left,
34735 y: centerY + m.top,
34736 width: centerW - (m.left+m.right),
34737 height: centerH - (m.top+m.bottom)
34739 //if(this.hideOnLayout){
34740 //center.el.setStyle("display", "block");
34742 center.updateBox(this.safeBox(centerBox));
34745 this.fireEvent("layout", this);
34749 safeBox : function(box){
34750 box.width = Math.max(0, box.width);
34751 box.height = Math.max(0, box.height);
34756 * Adds a ContentPanel (or subclass) to this layout.
34757 * @param {String} target The target region key (north, south, east, west or center).
34758 * @param {Roo.ContentPanel} panel The panel to add
34759 * @return {Roo.ContentPanel} The added panel
34761 add : function(target, panel){
34763 target = target.toLowerCase();
34764 return this.regions[target].add(panel);
34768 * Remove a ContentPanel (or subclass) to this layout.
34769 * @param {String} target The target region key (north, south, east, west or center).
34770 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34771 * @return {Roo.ContentPanel} The removed panel
34773 remove : function(target, panel){
34774 target = target.toLowerCase();
34775 return this.regions[target].remove(panel);
34779 * Searches all regions for a panel with the specified id
34780 * @param {String} panelId
34781 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34783 findPanel : function(panelId){
34784 var rs = this.regions;
34785 for(var target in rs){
34786 if(typeof rs[target] != "function"){
34787 var p = rs[target].getPanel(panelId);
34797 * Searches all regions for a panel with the specified id and activates (shows) it.
34798 * @param {String/ContentPanel} panelId The panels id or the panel itself
34799 * @return {Roo.ContentPanel} The shown panel or null
34801 showPanel : function(panelId) {
34802 var rs = this.regions;
34803 for(var target in rs){
34804 var r = rs[target];
34805 if(typeof r != "function"){
34806 if(r.hasPanel(panelId)){
34807 return r.showPanel(panelId);
34815 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34816 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34819 restoreState : function(provider){
34821 provider = Roo.state.Manager;
34823 var sm = new Roo.LayoutStateManager();
34824 sm.init(this, provider);
34830 * Adds a xtype elements to the layout.
34834 xtype : 'ContentPanel',
34841 xtype : 'NestedLayoutPanel',
34847 items : [ ... list of content panels or nested layout panels.. ]
34851 * @param {Object} cfg Xtype definition of item to add.
34853 addxtype : function(cfg)
34855 // basically accepts a pannel...
34856 // can accept a layout region..!?!?
34857 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34860 // theory? children can only be panels??
34862 //if (!cfg.xtype.match(/Panel$/)) {
34867 if (typeof(cfg.region) == 'undefined') {
34868 Roo.log("Failed to add Panel, region was not set");
34872 var region = cfg.region;
34878 xitems = cfg.items;
34885 case 'Content': // ContentPanel (el, cfg)
34886 case 'Scroll': // ContentPanel (el, cfg)
34888 cfg.autoCreate = true;
34889 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34891 // var el = this.el.createChild();
34892 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34895 this.add(region, ret);
34899 case 'TreePanel': // our new panel!
34900 cfg.el = this.el.createChild();
34901 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34902 this.add(region, ret);
34907 // create a new Layout (which is a Border Layout...
34909 var clayout = cfg.layout;
34910 clayout.el = this.el.createChild();
34911 clayout.items = clayout.items || [];
34915 // replace this exitems with the clayout ones..
34916 xitems = clayout.items;
34918 // force background off if it's in center...
34919 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34920 cfg.background = false;
34922 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34925 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34926 //console.log('adding nested layout panel ' + cfg.toSource());
34927 this.add(region, ret);
34928 nb = {}; /// find first...
34933 // needs grid and region
34935 //var el = this.getRegion(region).el.createChild();
34937 *var el = this.el.createChild();
34938 // create the grid first...
34939 cfg.grid.container = el;
34940 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34943 if (region == 'center' && this.active ) {
34944 cfg.background = false;
34947 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34949 this.add(region, ret);
34951 if (cfg.background) {
34952 // render grid on panel activation (if panel background)
34953 ret.on('activate', function(gp) {
34954 if (!gp.grid.rendered) {
34955 // gp.grid.render(el);
34959 // cfg.grid.render(el);
34965 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34966 // it was the old xcomponent building that caused this before.
34967 // espeically if border is the top element in the tree.
34977 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34979 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34980 this.add(region, ret);
34984 throw "Can not add '" + cfg.xtype + "' to Border";
34990 this.beginUpdate();
34994 Roo.each(xitems, function(i) {
34995 region = nb && i.region ? i.region : false;
34997 var add = ret.addxtype(i);
35000 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35001 if (!i.background) {
35002 abn[region] = nb[region] ;
35009 // make the last non-background panel active..
35010 //if (nb) { Roo.log(abn); }
35013 for(var r in abn) {
35014 region = this.getRegion(r);
35016 // tried using nb[r], but it does not work..
35018 region.showPanel(abn[r]);
35029 factory : function(cfg)
35032 var validRegions = Roo.bootstrap.layout.Border.regions;
35034 var target = cfg.region;
35037 var r = Roo.bootstrap.layout;
35041 return new r.North(cfg);
35043 return new r.South(cfg);
35045 return new r.East(cfg);
35047 return new r.West(cfg);
35049 return new r.Center(cfg);
35051 throw 'Layout region "'+target+'" not supported.';
35058 * Ext JS Library 1.1.1
35059 * Copyright(c) 2006-2007, Ext JS, LLC.
35061 * Originally Released Under LGPL - original licence link has changed is not relivant.
35064 * <script type="text/javascript">
35068 * @class Roo.bootstrap.layout.Basic
35069 * @extends Roo.util.Observable
35070 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35071 * and does not have a titlebar, tabs or any other features. All it does is size and position
35072 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35073 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35074 * @cfg {string} region the region that it inhabits..
35075 * @cfg {bool} skipConfig skip config?
35079 Roo.bootstrap.layout.Basic = function(config){
35081 this.mgr = config.mgr;
35083 this.position = config.region;
35085 var skipConfig = config.skipConfig;
35089 * @scope Roo.BasicLayoutRegion
35093 * @event beforeremove
35094 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35095 * @param {Roo.LayoutRegion} this
35096 * @param {Roo.ContentPanel} panel The panel
35097 * @param {Object} e The cancel event object
35099 "beforeremove" : true,
35101 * @event invalidated
35102 * Fires when the layout for this region is changed.
35103 * @param {Roo.LayoutRegion} this
35105 "invalidated" : true,
35107 * @event visibilitychange
35108 * Fires when this region is shown or hidden
35109 * @param {Roo.LayoutRegion} this
35110 * @param {Boolean} visibility true or false
35112 "visibilitychange" : true,
35114 * @event paneladded
35115 * Fires when a panel is added.
35116 * @param {Roo.LayoutRegion} this
35117 * @param {Roo.ContentPanel} panel The panel
35119 "paneladded" : true,
35121 * @event panelremoved
35122 * Fires when a panel is removed.
35123 * @param {Roo.LayoutRegion} this
35124 * @param {Roo.ContentPanel} panel The panel
35126 "panelremoved" : true,
35128 * @event beforecollapse
35129 * Fires when this region before collapse.
35130 * @param {Roo.LayoutRegion} this
35132 "beforecollapse" : true,
35135 * Fires when this region is collapsed.
35136 * @param {Roo.LayoutRegion} this
35138 "collapsed" : true,
35141 * Fires when this region is expanded.
35142 * @param {Roo.LayoutRegion} this
35147 * Fires when this region is slid into view.
35148 * @param {Roo.LayoutRegion} this
35150 "slideshow" : true,
35153 * Fires when this region slides out of view.
35154 * @param {Roo.LayoutRegion} this
35156 "slidehide" : true,
35158 * @event panelactivated
35159 * Fires when a panel is activated.
35160 * @param {Roo.LayoutRegion} this
35161 * @param {Roo.ContentPanel} panel The activated panel
35163 "panelactivated" : true,
35166 * Fires when the user resizes this region.
35167 * @param {Roo.LayoutRegion} this
35168 * @param {Number} newSize The new size (width for east/west, height for north/south)
35172 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35173 this.panels = new Roo.util.MixedCollection();
35174 this.panels.getKey = this.getPanelId.createDelegate(this);
35176 this.activePanel = null;
35177 // ensure listeners are added...
35179 if (config.listeners || config.events) {
35180 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35181 listeners : config.listeners || {},
35182 events : config.events || {}
35186 if(skipConfig !== true){
35187 this.applyConfig(config);
35191 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35193 getPanelId : function(p){
35197 applyConfig : function(config){
35198 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35199 this.config = config;
35204 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35205 * the width, for horizontal (north, south) the height.
35206 * @param {Number} newSize The new width or height
35208 resizeTo : function(newSize){
35209 var el = this.el ? this.el :
35210 (this.activePanel ? this.activePanel.getEl() : null);
35212 switch(this.position){
35215 el.setWidth(newSize);
35216 this.fireEvent("resized", this, newSize);
35220 el.setHeight(newSize);
35221 this.fireEvent("resized", this, newSize);
35227 getBox : function(){
35228 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35231 getMargins : function(){
35232 return this.margins;
35235 updateBox : function(box){
35237 var el = this.activePanel.getEl();
35238 el.dom.style.left = box.x + "px";
35239 el.dom.style.top = box.y + "px";
35240 this.activePanel.setSize(box.width, box.height);
35244 * Returns the container element for this region.
35245 * @return {Roo.Element}
35247 getEl : function(){
35248 return this.activePanel;
35252 * Returns true if this region is currently visible.
35253 * @return {Boolean}
35255 isVisible : function(){
35256 return this.activePanel ? true : false;
35259 setActivePanel : function(panel){
35260 panel = this.getPanel(panel);
35261 if(this.activePanel && this.activePanel != panel){
35262 this.activePanel.setActiveState(false);
35263 this.activePanel.getEl().setLeftTop(-10000,-10000);
35265 this.activePanel = panel;
35266 panel.setActiveState(true);
35268 panel.setSize(this.box.width, this.box.height);
35270 this.fireEvent("panelactivated", this, panel);
35271 this.fireEvent("invalidated");
35275 * Show the specified panel.
35276 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35277 * @return {Roo.ContentPanel} The shown panel or null
35279 showPanel : function(panel){
35280 panel = this.getPanel(panel);
35282 this.setActivePanel(panel);
35288 * Get the active panel for this region.
35289 * @return {Roo.ContentPanel} The active panel or null
35291 getActivePanel : function(){
35292 return this.activePanel;
35296 * Add the passed ContentPanel(s)
35297 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35298 * @return {Roo.ContentPanel} The panel added (if only one was added)
35300 add : function(panel){
35301 if(arguments.length > 1){
35302 for(var i = 0, len = arguments.length; i < len; i++) {
35303 this.add(arguments[i]);
35307 if(this.hasPanel(panel)){
35308 this.showPanel(panel);
35311 var el = panel.getEl();
35312 if(el.dom.parentNode != this.mgr.el.dom){
35313 this.mgr.el.dom.appendChild(el.dom);
35315 if(panel.setRegion){
35316 panel.setRegion(this);
35318 this.panels.add(panel);
35319 el.setStyle("position", "absolute");
35320 if(!panel.background){
35321 this.setActivePanel(panel);
35322 if(this.config.initialSize && this.panels.getCount()==1){
35323 this.resizeTo(this.config.initialSize);
35326 this.fireEvent("paneladded", this, panel);
35331 * Returns true if the panel is in this region.
35332 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35333 * @return {Boolean}
35335 hasPanel : function(panel){
35336 if(typeof panel == "object"){ // must be panel obj
35337 panel = panel.getId();
35339 return this.getPanel(panel) ? true : false;
35343 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35344 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35345 * @param {Boolean} preservePanel Overrides the config preservePanel option
35346 * @return {Roo.ContentPanel} The panel that was removed
35348 remove : function(panel, preservePanel){
35349 panel = this.getPanel(panel);
35354 this.fireEvent("beforeremove", this, panel, e);
35355 if(e.cancel === true){
35358 var panelId = panel.getId();
35359 this.panels.removeKey(panelId);
35364 * Returns the panel specified or null if it's not in this region.
35365 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35366 * @return {Roo.ContentPanel}
35368 getPanel : function(id){
35369 if(typeof id == "object"){ // must be panel obj
35372 return this.panels.get(id);
35376 * Returns this regions position (north/south/east/west/center).
35379 getPosition: function(){
35380 return this.position;
35384 * Ext JS Library 1.1.1
35385 * Copyright(c) 2006-2007, Ext JS, LLC.
35387 * Originally Released Under LGPL - original licence link has changed is not relivant.
35390 * <script type="text/javascript">
35394 * @class Roo.bootstrap.layout.Region
35395 * @extends Roo.bootstrap.layout.Basic
35396 * This class represents a region in a layout manager.
35398 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35399 * @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})
35400 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35401 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35402 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35403 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35404 * @cfg {String} title The title for the region (overrides panel titles)
35405 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35406 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35407 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35408 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35409 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35410 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35411 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35412 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35413 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35414 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35416 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35417 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35418 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35419 * @cfg {Number} width For East/West panels
35420 * @cfg {Number} height For North/South panels
35421 * @cfg {Boolean} split To show the splitter
35422 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35424 * @cfg {string} cls Extra CSS classes to add to region
35426 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35427 * @cfg {string} region the region that it inhabits..
35430 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35431 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35433 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35434 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35435 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35437 Roo.bootstrap.layout.Region = function(config)
35439 this.applyConfig(config);
35441 var mgr = config.mgr;
35442 var pos = config.region;
35443 config.skipConfig = true;
35444 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35447 this.onRender(mgr.el);
35450 this.visible = true;
35451 this.collapsed = false;
35452 this.unrendered_panels = [];
35455 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35457 position: '', // set by wrapper (eg. north/south etc..)
35458 unrendered_panels : null, // unrendered panels.
35459 createBody : function(){
35460 /** This region's body element
35461 * @type Roo.Element */
35462 this.bodyEl = this.el.createChild({
35464 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35468 onRender: function(ctr, pos)
35470 var dh = Roo.DomHelper;
35471 /** This region's container element
35472 * @type Roo.Element */
35473 this.el = dh.append(ctr.dom, {
35475 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35477 /** This region's title element
35478 * @type Roo.Element */
35480 this.titleEl = dh.append(this.el.dom,
35483 unselectable: "on",
35484 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35486 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35487 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35490 this.titleEl.enableDisplayMode();
35491 /** This region's title text element
35492 * @type HTMLElement */
35493 this.titleTextEl = this.titleEl.dom.firstChild;
35494 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35496 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35497 this.closeBtn.enableDisplayMode();
35498 this.closeBtn.on("click", this.closeClicked, this);
35499 this.closeBtn.hide();
35501 this.createBody(this.config);
35502 if(this.config.hideWhenEmpty){
35504 this.on("paneladded", this.validateVisibility, this);
35505 this.on("panelremoved", this.validateVisibility, this);
35507 if(this.autoScroll){
35508 this.bodyEl.setStyle("overflow", "auto");
35510 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35512 //if(c.titlebar !== false){
35513 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35514 this.titleEl.hide();
35516 this.titleEl.show();
35517 if(this.config.title){
35518 this.titleTextEl.innerHTML = this.config.title;
35522 if(this.config.collapsed){
35523 this.collapse(true);
35525 if(this.config.hidden){
35529 if (this.unrendered_panels && this.unrendered_panels.length) {
35530 for (var i =0;i< this.unrendered_panels.length; i++) {
35531 this.add(this.unrendered_panels[i]);
35533 this.unrendered_panels = null;
35539 applyConfig : function(c)
35542 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35543 var dh = Roo.DomHelper;
35544 if(c.titlebar !== false){
35545 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35546 this.collapseBtn.on("click", this.collapse, this);
35547 this.collapseBtn.enableDisplayMode();
35549 if(c.showPin === true || this.showPin){
35550 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35551 this.stickBtn.enableDisplayMode();
35552 this.stickBtn.on("click", this.expand, this);
35553 this.stickBtn.hide();
35558 /** This region's collapsed element
35559 * @type Roo.Element */
35562 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35563 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35566 if(c.floatable !== false){
35567 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35568 this.collapsedEl.on("click", this.collapseClick, this);
35571 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35572 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35573 id: "message", unselectable: "on", style:{"float":"left"}});
35574 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35576 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35577 this.expandBtn.on("click", this.expand, this);
35581 if(this.collapseBtn){
35582 this.collapseBtn.setVisible(c.collapsible == true);
35585 this.cmargins = c.cmargins || this.cmargins ||
35586 (this.position == "west" || this.position == "east" ?
35587 {top: 0, left: 2, right:2, bottom: 0} :
35588 {top: 2, left: 0, right:0, bottom: 2});
35590 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35593 this.bottomTabs = c.tabPosition != "top";
35595 this.autoScroll = c.autoScroll || false;
35600 this.duration = c.duration || .30;
35601 this.slideDuration = c.slideDuration || .45;
35606 * Returns true if this region is currently visible.
35607 * @return {Boolean}
35609 isVisible : function(){
35610 return this.visible;
35614 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35615 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35617 //setCollapsedTitle : function(title){
35618 // title = title || " ";
35619 // if(this.collapsedTitleTextEl){
35620 // this.collapsedTitleTextEl.innerHTML = title;
35624 getBox : function(){
35626 // if(!this.collapsed){
35627 b = this.el.getBox(false, true);
35629 // b = this.collapsedEl.getBox(false, true);
35634 getMargins : function(){
35635 return this.margins;
35636 //return this.collapsed ? this.cmargins : this.margins;
35639 highlight : function(){
35640 this.el.addClass("x-layout-panel-dragover");
35643 unhighlight : function(){
35644 this.el.removeClass("x-layout-panel-dragover");
35647 updateBox : function(box)
35649 if (!this.bodyEl) {
35650 return; // not rendered yet..
35654 if(!this.collapsed){
35655 this.el.dom.style.left = box.x + "px";
35656 this.el.dom.style.top = box.y + "px";
35657 this.updateBody(box.width, box.height);
35659 this.collapsedEl.dom.style.left = box.x + "px";
35660 this.collapsedEl.dom.style.top = box.y + "px";
35661 this.collapsedEl.setSize(box.width, box.height);
35664 this.tabs.autoSizeTabs();
35668 updateBody : function(w, h)
35671 this.el.setWidth(w);
35672 w -= this.el.getBorderWidth("rl");
35673 if(this.config.adjustments){
35674 w += this.config.adjustments[0];
35677 if(h !== null && h > 0){
35678 this.el.setHeight(h);
35679 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35680 h -= this.el.getBorderWidth("tb");
35681 if(this.config.adjustments){
35682 h += this.config.adjustments[1];
35684 this.bodyEl.setHeight(h);
35686 h = this.tabs.syncHeight(h);
35689 if(this.panelSize){
35690 w = w !== null ? w : this.panelSize.width;
35691 h = h !== null ? h : this.panelSize.height;
35693 if(this.activePanel){
35694 var el = this.activePanel.getEl();
35695 w = w !== null ? w : el.getWidth();
35696 h = h !== null ? h : el.getHeight();
35697 this.panelSize = {width: w, height: h};
35698 this.activePanel.setSize(w, h);
35700 if(Roo.isIE && this.tabs){
35701 this.tabs.el.repaint();
35706 * Returns the container element for this region.
35707 * @return {Roo.Element}
35709 getEl : function(){
35714 * Hides this region.
35717 //if(!this.collapsed){
35718 this.el.dom.style.left = "-2000px";
35721 // this.collapsedEl.dom.style.left = "-2000px";
35722 // this.collapsedEl.hide();
35724 this.visible = false;
35725 this.fireEvent("visibilitychange", this, false);
35729 * Shows this region if it was previously hidden.
35732 //if(!this.collapsed){
35735 // this.collapsedEl.show();
35737 this.visible = true;
35738 this.fireEvent("visibilitychange", this, true);
35741 closeClicked : function(){
35742 if(this.activePanel){
35743 this.remove(this.activePanel);
35747 collapseClick : function(e){
35749 e.stopPropagation();
35752 e.stopPropagation();
35758 * Collapses this region.
35759 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35762 collapse : function(skipAnim, skipCheck = false){
35763 if(this.collapsed) {
35767 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35769 this.collapsed = true;
35771 this.split.el.hide();
35773 if(this.config.animate && skipAnim !== true){
35774 this.fireEvent("invalidated", this);
35775 this.animateCollapse();
35777 this.el.setLocation(-20000,-20000);
35779 this.collapsedEl.show();
35780 this.fireEvent("collapsed", this);
35781 this.fireEvent("invalidated", this);
35787 animateCollapse : function(){
35792 * Expands this region if it was previously collapsed.
35793 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35794 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35797 expand : function(e, skipAnim){
35799 e.stopPropagation();
35801 if(!this.collapsed || this.el.hasActiveFx()) {
35805 this.afterSlideIn();
35808 this.collapsed = false;
35809 if(this.config.animate && skipAnim !== true){
35810 this.animateExpand();
35814 this.split.el.show();
35816 this.collapsedEl.setLocation(-2000,-2000);
35817 this.collapsedEl.hide();
35818 this.fireEvent("invalidated", this);
35819 this.fireEvent("expanded", this);
35823 animateExpand : function(){
35827 initTabs : function()
35829 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35831 var ts = new Roo.bootstrap.panel.Tabs({
35832 el: this.bodyEl.dom,
35833 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35834 disableTooltips: this.config.disableTabTips,
35835 toolbar : this.config.toolbar
35838 if(this.config.hideTabs){
35839 ts.stripWrap.setDisplayed(false);
35842 ts.resizeTabs = this.config.resizeTabs === true;
35843 ts.minTabWidth = this.config.minTabWidth || 40;
35844 ts.maxTabWidth = this.config.maxTabWidth || 250;
35845 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35846 ts.monitorResize = false;
35847 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35848 ts.bodyEl.addClass('roo-layout-tabs-body');
35849 this.panels.each(this.initPanelAsTab, this);
35852 initPanelAsTab : function(panel){
35853 var ti = this.tabs.addTab(
35857 this.config.closeOnTab && panel.isClosable(),
35860 if(panel.tabTip !== undefined){
35861 ti.setTooltip(panel.tabTip);
35863 ti.on("activate", function(){
35864 this.setActivePanel(panel);
35867 if(this.config.closeOnTab){
35868 ti.on("beforeclose", function(t, e){
35870 this.remove(panel);
35874 panel.tabItem = ti;
35879 updatePanelTitle : function(panel, title)
35881 if(this.activePanel == panel){
35882 this.updateTitle(title);
35885 var ti = this.tabs.getTab(panel.getEl().id);
35887 if(panel.tabTip !== undefined){
35888 ti.setTooltip(panel.tabTip);
35893 updateTitle : function(title){
35894 if(this.titleTextEl && !this.config.title){
35895 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35899 setActivePanel : function(panel)
35901 panel = this.getPanel(panel);
35902 if(this.activePanel && this.activePanel != panel){
35903 if(this.activePanel.setActiveState(false) === false){
35907 this.activePanel = panel;
35908 panel.setActiveState(true);
35909 if(this.panelSize){
35910 panel.setSize(this.panelSize.width, this.panelSize.height);
35913 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35915 this.updateTitle(panel.getTitle());
35917 this.fireEvent("invalidated", this);
35919 this.fireEvent("panelactivated", this, panel);
35923 * Shows the specified panel.
35924 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35925 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35927 showPanel : function(panel)
35929 panel = this.getPanel(panel);
35932 var tab = this.tabs.getTab(panel.getEl().id);
35933 if(tab.isHidden()){
35934 this.tabs.unhideTab(tab.id);
35938 this.setActivePanel(panel);
35945 * Get the active panel for this region.
35946 * @return {Roo.ContentPanel} The active panel or null
35948 getActivePanel : function(){
35949 return this.activePanel;
35952 validateVisibility : function(){
35953 if(this.panels.getCount() < 1){
35954 this.updateTitle(" ");
35955 this.closeBtn.hide();
35958 if(!this.isVisible()){
35965 * Adds the passed ContentPanel(s) to this region.
35966 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35967 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35969 add : function(panel)
35971 if(arguments.length > 1){
35972 for(var i = 0, len = arguments.length; i < len; i++) {
35973 this.add(arguments[i]);
35978 // if we have not been rendered yet, then we can not really do much of this..
35979 if (!this.bodyEl) {
35980 this.unrendered_panels.push(panel);
35987 if(this.hasPanel(panel)){
35988 this.showPanel(panel);
35991 panel.setRegion(this);
35992 this.panels.add(panel);
35993 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35994 // sinle panel - no tab...?? would it not be better to render it with the tabs,
35995 // and hide them... ???
35996 this.bodyEl.dom.appendChild(panel.getEl().dom);
35997 if(panel.background !== true){
35998 this.setActivePanel(panel);
36000 this.fireEvent("paneladded", this, panel);
36007 this.initPanelAsTab(panel);
36011 if(panel.background !== true){
36012 this.tabs.activate(panel.getEl().id);
36014 this.fireEvent("paneladded", this, panel);
36019 * Hides the tab for the specified panel.
36020 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36022 hidePanel : function(panel){
36023 if(this.tabs && (panel = this.getPanel(panel))){
36024 this.tabs.hideTab(panel.getEl().id);
36029 * Unhides the tab for a previously hidden panel.
36030 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36032 unhidePanel : function(panel){
36033 if(this.tabs && (panel = this.getPanel(panel))){
36034 this.tabs.unhideTab(panel.getEl().id);
36038 clearPanels : function(){
36039 while(this.panels.getCount() > 0){
36040 this.remove(this.panels.first());
36045 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36046 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36047 * @param {Boolean} preservePanel Overrides the config preservePanel option
36048 * @return {Roo.ContentPanel} The panel that was removed
36050 remove : function(panel, preservePanel)
36052 panel = this.getPanel(panel);
36057 this.fireEvent("beforeremove", this, panel, e);
36058 if(e.cancel === true){
36061 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36062 var panelId = panel.getId();
36063 this.panels.removeKey(panelId);
36065 document.body.appendChild(panel.getEl().dom);
36068 this.tabs.removeTab(panel.getEl().id);
36069 }else if (!preservePanel){
36070 this.bodyEl.dom.removeChild(panel.getEl().dom);
36072 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36073 var p = this.panels.first();
36074 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36075 tempEl.appendChild(p.getEl().dom);
36076 this.bodyEl.update("");
36077 this.bodyEl.dom.appendChild(p.getEl().dom);
36079 this.updateTitle(p.getTitle());
36081 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36082 this.setActivePanel(p);
36084 panel.setRegion(null);
36085 if(this.activePanel == panel){
36086 this.activePanel = null;
36088 if(this.config.autoDestroy !== false && preservePanel !== true){
36089 try{panel.destroy();}catch(e){}
36091 this.fireEvent("panelremoved", this, panel);
36096 * Returns the TabPanel component used by this region
36097 * @return {Roo.TabPanel}
36099 getTabs : function(){
36103 createTool : function(parentEl, className){
36104 var btn = Roo.DomHelper.append(parentEl, {
36106 cls: "x-layout-tools-button",
36109 cls: "roo-layout-tools-button-inner " + className,
36113 btn.addClassOnOver("roo-layout-tools-button-over");
36118 * Ext JS Library 1.1.1
36119 * Copyright(c) 2006-2007, Ext JS, LLC.
36121 * Originally Released Under LGPL - original licence link has changed is not relivant.
36124 * <script type="text/javascript">
36130 * @class Roo.SplitLayoutRegion
36131 * @extends Roo.LayoutRegion
36132 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36134 Roo.bootstrap.layout.Split = function(config){
36135 this.cursor = config.cursor;
36136 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36139 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36141 splitTip : "Drag to resize.",
36142 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36143 useSplitTips : false,
36145 applyConfig : function(config){
36146 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36149 onRender : function(ctr,pos) {
36151 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36152 if(!this.config.split){
36157 var splitEl = Roo.DomHelper.append(ctr.dom, {
36159 id: this.el.id + "-split",
36160 cls: "roo-layout-split roo-layout-split-"+this.position,
36163 /** The SplitBar for this region
36164 * @type Roo.SplitBar */
36165 // does not exist yet...
36166 Roo.log([this.position, this.orientation]);
36168 this.split = new Roo.bootstrap.SplitBar({
36169 dragElement : splitEl,
36170 resizingElement: this.el,
36171 orientation : this.orientation
36174 this.split.on("moved", this.onSplitMove, this);
36175 this.split.useShim = this.config.useShim === true;
36176 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36177 if(this.useSplitTips){
36178 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36180 //if(config.collapsible){
36181 // this.split.el.on("dblclick", this.collapse, this);
36184 if(typeof this.config.minSize != "undefined"){
36185 this.split.minSize = this.config.minSize;
36187 if(typeof this.config.maxSize != "undefined"){
36188 this.split.maxSize = this.config.maxSize;
36190 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36191 this.hideSplitter();
36196 getHMaxSize : function(){
36197 var cmax = this.config.maxSize || 10000;
36198 var center = this.mgr.getRegion("center");
36199 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36202 getVMaxSize : function(){
36203 var cmax = this.config.maxSize || 10000;
36204 var center = this.mgr.getRegion("center");
36205 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36208 onSplitMove : function(split, newSize){
36209 this.fireEvent("resized", this, newSize);
36213 * Returns the {@link Roo.SplitBar} for this region.
36214 * @return {Roo.SplitBar}
36216 getSplitBar : function(){
36221 this.hideSplitter();
36222 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36225 hideSplitter : function(){
36227 this.split.el.setLocation(-2000,-2000);
36228 this.split.el.hide();
36234 this.split.el.show();
36236 Roo.bootstrap.layout.Split.superclass.show.call(this);
36239 beforeSlide: function(){
36240 if(Roo.isGecko){// firefox overflow auto bug workaround
36241 this.bodyEl.clip();
36243 this.tabs.bodyEl.clip();
36245 if(this.activePanel){
36246 this.activePanel.getEl().clip();
36248 if(this.activePanel.beforeSlide){
36249 this.activePanel.beforeSlide();
36255 afterSlide : function(){
36256 if(Roo.isGecko){// firefox overflow auto bug workaround
36257 this.bodyEl.unclip();
36259 this.tabs.bodyEl.unclip();
36261 if(this.activePanel){
36262 this.activePanel.getEl().unclip();
36263 if(this.activePanel.afterSlide){
36264 this.activePanel.afterSlide();
36270 initAutoHide : function(){
36271 if(this.autoHide !== false){
36272 if(!this.autoHideHd){
36273 var st = new Roo.util.DelayedTask(this.slideIn, this);
36274 this.autoHideHd = {
36275 "mouseout": function(e){
36276 if(!e.within(this.el, true)){
36280 "mouseover" : function(e){
36286 this.el.on(this.autoHideHd);
36290 clearAutoHide : function(){
36291 if(this.autoHide !== false){
36292 this.el.un("mouseout", this.autoHideHd.mouseout);
36293 this.el.un("mouseover", this.autoHideHd.mouseover);
36297 clearMonitor : function(){
36298 Roo.get(document).un("click", this.slideInIf, this);
36301 // these names are backwards but not changed for compat
36302 slideOut : function(){
36303 if(this.isSlid || this.el.hasActiveFx()){
36306 this.isSlid = true;
36307 if(this.collapseBtn){
36308 this.collapseBtn.hide();
36310 this.closeBtnState = this.closeBtn.getStyle('display');
36311 this.closeBtn.hide();
36313 this.stickBtn.show();
36316 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36317 this.beforeSlide();
36318 this.el.setStyle("z-index", 10001);
36319 this.el.slideIn(this.getSlideAnchor(), {
36320 callback: function(){
36322 this.initAutoHide();
36323 Roo.get(document).on("click", this.slideInIf, this);
36324 this.fireEvent("slideshow", this);
36331 afterSlideIn : function(){
36332 this.clearAutoHide();
36333 this.isSlid = false;
36334 this.clearMonitor();
36335 this.el.setStyle("z-index", "");
36336 if(this.collapseBtn){
36337 this.collapseBtn.show();
36339 this.closeBtn.setStyle('display', this.closeBtnState);
36341 this.stickBtn.hide();
36343 this.fireEvent("slidehide", this);
36346 slideIn : function(cb){
36347 if(!this.isSlid || this.el.hasActiveFx()){
36351 this.isSlid = false;
36352 this.beforeSlide();
36353 this.el.slideOut(this.getSlideAnchor(), {
36354 callback: function(){
36355 this.el.setLeftTop(-10000, -10000);
36357 this.afterSlideIn();
36365 slideInIf : function(e){
36366 if(!e.within(this.el)){
36371 animateCollapse : function(){
36372 this.beforeSlide();
36373 this.el.setStyle("z-index", 20000);
36374 var anchor = this.getSlideAnchor();
36375 this.el.slideOut(anchor, {
36376 callback : function(){
36377 this.el.setStyle("z-index", "");
36378 this.collapsedEl.slideIn(anchor, {duration:.3});
36380 this.el.setLocation(-10000,-10000);
36382 this.fireEvent("collapsed", this);
36389 animateExpand : function(){
36390 this.beforeSlide();
36391 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36392 this.el.setStyle("z-index", 20000);
36393 this.collapsedEl.hide({
36396 this.el.slideIn(this.getSlideAnchor(), {
36397 callback : function(){
36398 this.el.setStyle("z-index", "");
36401 this.split.el.show();
36403 this.fireEvent("invalidated", this);
36404 this.fireEvent("expanded", this);
36432 getAnchor : function(){
36433 return this.anchors[this.position];
36436 getCollapseAnchor : function(){
36437 return this.canchors[this.position];
36440 getSlideAnchor : function(){
36441 return this.sanchors[this.position];
36444 getAlignAdj : function(){
36445 var cm = this.cmargins;
36446 switch(this.position){
36462 getExpandAdj : function(){
36463 var c = this.collapsedEl, cm = this.cmargins;
36464 switch(this.position){
36466 return [-(cm.right+c.getWidth()+cm.left), 0];
36469 return [cm.right+c.getWidth()+cm.left, 0];
36472 return [0, -(cm.top+cm.bottom+c.getHeight())];
36475 return [0, cm.top+cm.bottom+c.getHeight()];
36481 * Ext JS Library 1.1.1
36482 * Copyright(c) 2006-2007, Ext JS, LLC.
36484 * Originally Released Under LGPL - original licence link has changed is not relivant.
36487 * <script type="text/javascript">
36490 * These classes are private internal classes
36492 Roo.bootstrap.layout.Center = function(config){
36493 config.region = "center";
36494 Roo.bootstrap.layout.Region.call(this, config);
36495 this.visible = true;
36496 this.minWidth = config.minWidth || 20;
36497 this.minHeight = config.minHeight || 20;
36500 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36502 // center panel can't be hidden
36506 // center panel can't be hidden
36509 getMinWidth: function(){
36510 return this.minWidth;
36513 getMinHeight: function(){
36514 return this.minHeight;
36527 Roo.bootstrap.layout.North = function(config)
36529 config.region = 'north';
36530 config.cursor = 'n-resize';
36532 Roo.bootstrap.layout.Split.call(this, config);
36536 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36537 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36538 this.split.el.addClass("roo-layout-split-v");
36540 var size = config.initialSize || config.height;
36541 if(typeof size != "undefined"){
36542 this.el.setHeight(size);
36545 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36547 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36551 getBox : function(){
36552 if(this.collapsed){
36553 return this.collapsedEl.getBox();
36555 var box = this.el.getBox();
36557 box.height += this.split.el.getHeight();
36562 updateBox : function(box){
36563 if(this.split && !this.collapsed){
36564 box.height -= this.split.el.getHeight();
36565 this.split.el.setLeft(box.x);
36566 this.split.el.setTop(box.y+box.height);
36567 this.split.el.setWidth(box.width);
36569 if(this.collapsed){
36570 this.updateBody(box.width, null);
36572 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36580 Roo.bootstrap.layout.South = function(config){
36581 config.region = 'south';
36582 config.cursor = 's-resize';
36583 Roo.bootstrap.layout.Split.call(this, config);
36585 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36586 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36587 this.split.el.addClass("roo-layout-split-v");
36589 var size = config.initialSize || config.height;
36590 if(typeof size != "undefined"){
36591 this.el.setHeight(size);
36595 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36596 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36597 getBox : function(){
36598 if(this.collapsed){
36599 return this.collapsedEl.getBox();
36601 var box = this.el.getBox();
36603 var sh = this.split.el.getHeight();
36610 updateBox : function(box){
36611 if(this.split && !this.collapsed){
36612 var sh = this.split.el.getHeight();
36615 this.split.el.setLeft(box.x);
36616 this.split.el.setTop(box.y-sh);
36617 this.split.el.setWidth(box.width);
36619 if(this.collapsed){
36620 this.updateBody(box.width, null);
36622 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36626 Roo.bootstrap.layout.East = function(config){
36627 config.region = "east";
36628 config.cursor = "e-resize";
36629 Roo.bootstrap.layout.Split.call(this, config);
36631 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36632 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36633 this.split.el.addClass("roo-layout-split-h");
36635 var size = config.initialSize || config.width;
36636 if(typeof size != "undefined"){
36637 this.el.setWidth(size);
36640 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36641 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36642 getBox : function(){
36643 if(this.collapsed){
36644 return this.collapsedEl.getBox();
36646 var box = this.el.getBox();
36648 var sw = this.split.el.getWidth();
36655 updateBox : function(box){
36656 if(this.split && !this.collapsed){
36657 var sw = this.split.el.getWidth();
36659 this.split.el.setLeft(box.x);
36660 this.split.el.setTop(box.y);
36661 this.split.el.setHeight(box.height);
36664 if(this.collapsed){
36665 this.updateBody(null, box.height);
36667 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36671 Roo.bootstrap.layout.West = function(config){
36672 config.region = "west";
36673 config.cursor = "w-resize";
36675 Roo.bootstrap.layout.Split.call(this, config);
36677 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36678 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36679 this.split.el.addClass("roo-layout-split-h");
36683 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36684 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36686 onRender: function(ctr, pos)
36688 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36689 var size = this.config.initialSize || this.config.width;
36690 if(typeof size != "undefined"){
36691 this.el.setWidth(size);
36695 getBox : function(){
36696 if(this.collapsed){
36697 return this.collapsedEl.getBox();
36699 var box = this.el.getBox();
36701 box.width += this.split.el.getWidth();
36706 updateBox : function(box){
36707 if(this.split && !this.collapsed){
36708 var sw = this.split.el.getWidth();
36710 this.split.el.setLeft(box.x+box.width);
36711 this.split.el.setTop(box.y);
36712 this.split.el.setHeight(box.height);
36714 if(this.collapsed){
36715 this.updateBody(null, box.height);
36717 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36720 Roo.namespace("Roo.bootstrap.panel");/*
36722 * Ext JS Library 1.1.1
36723 * Copyright(c) 2006-2007, Ext JS, LLC.
36725 * Originally Released Under LGPL - original licence link has changed is not relivant.
36728 * <script type="text/javascript">
36731 * @class Roo.ContentPanel
36732 * @extends Roo.util.Observable
36733 * A basic ContentPanel element.
36734 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36735 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36736 * @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
36737 * @cfg {Boolean} closable True if the panel can be closed/removed
36738 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36739 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36740 * @cfg {Toolbar} toolbar A toolbar for this panel
36741 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36742 * @cfg {String} title The title for this panel
36743 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36744 * @cfg {String} url Calls {@link #setUrl} with this value
36745 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36746 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36747 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36748 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36749 * @cfg {Boolean} badges render the badges
36752 * Create a new ContentPanel.
36753 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36754 * @param {String/Object} config A string to set only the title or a config object
36755 * @param {String} content (optional) Set the HTML content for this panel
36756 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36758 Roo.bootstrap.panel.Content = function( config){
36760 this.tpl = config.tpl || false;
36762 var el = config.el;
36763 var content = config.content;
36765 if(config.autoCreate){ // xtype is available if this is called from factory
36768 this.el = Roo.get(el);
36769 if(!this.el && config && config.autoCreate){
36770 if(typeof config.autoCreate == "object"){
36771 if(!config.autoCreate.id){
36772 config.autoCreate.id = config.id||el;
36774 this.el = Roo.DomHelper.append(document.body,
36775 config.autoCreate, true);
36777 var elcfg = { tag: "div",
36778 cls: "roo-layout-inactive-content",
36782 elcfg.html = config.html;
36786 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36789 this.closable = false;
36790 this.loaded = false;
36791 this.active = false;
36794 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36796 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36798 this.wrapEl = this.el; //this.el.wrap();
36800 if (config.toolbar.items) {
36801 ti = config.toolbar.items ;
36802 delete config.toolbar.items ;
36806 this.toolbar.render(this.wrapEl, 'before');
36807 for(var i =0;i < ti.length;i++) {
36808 // Roo.log(['add child', items[i]]);
36809 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36811 this.toolbar.items = nitems;
36812 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36813 delete config.toolbar;
36817 // xtype created footer. - not sure if will work as we normally have to render first..
36818 if (this.footer && !this.footer.el && this.footer.xtype) {
36819 if (!this.wrapEl) {
36820 this.wrapEl = this.el.wrap();
36823 this.footer.container = this.wrapEl.createChild();
36825 this.footer = Roo.factory(this.footer, Roo);
36830 if(typeof config == "string"){
36831 this.title = config;
36833 Roo.apply(this, config);
36837 this.resizeEl = Roo.get(this.resizeEl, true);
36839 this.resizeEl = this.el;
36841 // handle view.xtype
36849 * Fires when this panel is activated.
36850 * @param {Roo.ContentPanel} this
36854 * @event deactivate
36855 * Fires when this panel is activated.
36856 * @param {Roo.ContentPanel} this
36858 "deactivate" : true,
36862 * Fires when this panel is resized if fitToFrame is true.
36863 * @param {Roo.ContentPanel} this
36864 * @param {Number} width The width after any component adjustments
36865 * @param {Number} height The height after any component adjustments
36871 * Fires when this tab is created
36872 * @param {Roo.ContentPanel} this
36883 if(this.autoScroll){
36884 this.resizeEl.setStyle("overflow", "auto");
36886 // fix randome scrolling
36887 //this.el.on('scroll', function() {
36888 // Roo.log('fix random scolling');
36889 // this.scrollTo('top',0);
36892 content = content || this.content;
36894 this.setContent(content);
36896 if(config && config.url){
36897 this.setUrl(this.url, this.params, this.loadOnce);
36902 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36904 if (this.view && typeof(this.view.xtype) != 'undefined') {
36905 this.view.el = this.el.appendChild(document.createElement("div"));
36906 this.view = Roo.factory(this.view);
36907 this.view.render && this.view.render(false, '');
36911 this.fireEvent('render', this);
36914 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36918 setRegion : function(region){
36919 this.region = region;
36920 this.setActiveClass(region && !this.background);
36924 setActiveClass: function(state)
36927 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36928 this.el.setStyle('position','relative');
36930 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36931 this.el.setStyle('position', 'absolute');
36936 * Returns the toolbar for this Panel if one was configured.
36937 * @return {Roo.Toolbar}
36939 getToolbar : function(){
36940 return this.toolbar;
36943 setActiveState : function(active)
36945 this.active = active;
36946 this.setActiveClass(active);
36948 if(this.fireEvent("deactivate", this) === false){
36953 this.fireEvent("activate", this);
36957 * Updates this panel's element
36958 * @param {String} content The new content
36959 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36961 setContent : function(content, loadScripts){
36962 this.el.update(content, loadScripts);
36965 ignoreResize : function(w, h){
36966 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36969 this.lastSize = {width: w, height: h};
36974 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36975 * @return {Roo.UpdateManager} The UpdateManager
36977 getUpdateManager : function(){
36978 return this.el.getUpdateManager();
36981 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36982 * @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:
36985 url: "your-url.php",
36986 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36987 callback: yourFunction,
36988 scope: yourObject, //(optional scope)
36991 text: "Loading...",
36996 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36997 * 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.
36998 * @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}
36999 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37000 * @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.
37001 * @return {Roo.ContentPanel} this
37004 var um = this.el.getUpdateManager();
37005 um.update.apply(um, arguments);
37011 * 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.
37012 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37013 * @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)
37014 * @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)
37015 * @return {Roo.UpdateManager} The UpdateManager
37017 setUrl : function(url, params, loadOnce){
37018 if(this.refreshDelegate){
37019 this.removeListener("activate", this.refreshDelegate);
37021 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37022 this.on("activate", this.refreshDelegate);
37023 return this.el.getUpdateManager();
37026 _handleRefresh : function(url, params, loadOnce){
37027 if(!loadOnce || !this.loaded){
37028 var updater = this.el.getUpdateManager();
37029 updater.update(url, params, this._setLoaded.createDelegate(this));
37033 _setLoaded : function(){
37034 this.loaded = true;
37038 * Returns this panel's id
37041 getId : function(){
37046 * Returns this panel's element - used by regiosn to add.
37047 * @return {Roo.Element}
37049 getEl : function(){
37050 return this.wrapEl || this.el;
37055 adjustForComponents : function(width, height)
37057 //Roo.log('adjustForComponents ');
37058 if(this.resizeEl != this.el){
37059 width -= this.el.getFrameWidth('lr');
37060 height -= this.el.getFrameWidth('tb');
37063 var te = this.toolbar.getEl();
37064 te.setWidth(width);
37065 height -= te.getHeight();
37068 var te = this.footer.getEl();
37069 te.setWidth(width);
37070 height -= te.getHeight();
37074 if(this.adjustments){
37075 width += this.adjustments[0];
37076 height += this.adjustments[1];
37078 return {"width": width, "height": height};
37081 setSize : function(width, height){
37082 if(this.fitToFrame && !this.ignoreResize(width, height)){
37083 if(this.fitContainer && this.resizeEl != this.el){
37084 this.el.setSize(width, height);
37086 var size = this.adjustForComponents(width, height);
37087 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37088 this.fireEvent('resize', this, size.width, size.height);
37093 * Returns this panel's title
37096 getTitle : function(){
37098 if (typeof(this.title) != 'object') {
37103 for (var k in this.title) {
37104 if (!this.title.hasOwnProperty(k)) {
37108 if (k.indexOf('-') >= 0) {
37109 var s = k.split('-');
37110 for (var i = 0; i<s.length; i++) {
37111 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37114 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37121 * Set this panel's title
37122 * @param {String} title
37124 setTitle : function(title){
37125 this.title = title;
37127 this.region.updatePanelTitle(this, title);
37132 * Returns true is this panel was configured to be closable
37133 * @return {Boolean}
37135 isClosable : function(){
37136 return this.closable;
37139 beforeSlide : function(){
37141 this.resizeEl.clip();
37144 afterSlide : function(){
37146 this.resizeEl.unclip();
37150 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37151 * Will fail silently if the {@link #setUrl} method has not been called.
37152 * This does not activate the panel, just updates its content.
37154 refresh : function(){
37155 if(this.refreshDelegate){
37156 this.loaded = false;
37157 this.refreshDelegate();
37162 * Destroys this panel
37164 destroy : function(){
37165 this.el.removeAllListeners();
37166 var tempEl = document.createElement("span");
37167 tempEl.appendChild(this.el.dom);
37168 tempEl.innerHTML = "";
37174 * form - if the content panel contains a form - this is a reference to it.
37175 * @type {Roo.form.Form}
37179 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37180 * This contains a reference to it.
37186 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37196 * @param {Object} cfg Xtype definition of item to add.
37200 getChildContainer: function () {
37201 return this.getEl();
37206 var ret = new Roo.factory(cfg);
37211 if (cfg.xtype.match(/^Form$/)) {
37214 //if (this.footer) {
37215 // el = this.footer.container.insertSibling(false, 'before');
37217 el = this.el.createChild();
37220 this.form = new Roo.form.Form(cfg);
37223 if ( this.form.allItems.length) {
37224 this.form.render(el.dom);
37228 // should only have one of theses..
37229 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37230 // views.. should not be just added - used named prop 'view''
37232 cfg.el = this.el.appendChild(document.createElement("div"));
37235 var ret = new Roo.factory(cfg);
37237 ret.render && ret.render(false, ''); // render blank..
37247 * @class Roo.bootstrap.panel.Grid
37248 * @extends Roo.bootstrap.panel.Content
37250 * Create a new GridPanel.
37251 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37252 * @param {Object} config A the config object
37258 Roo.bootstrap.panel.Grid = function(config)
37262 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37263 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37265 config.el = this.wrapper;
37266 //this.el = this.wrapper;
37268 if (config.container) {
37269 // ctor'ed from a Border/panel.grid
37272 this.wrapper.setStyle("overflow", "hidden");
37273 this.wrapper.addClass('roo-grid-container');
37278 if(config.toolbar){
37279 var tool_el = this.wrapper.createChild();
37280 this.toolbar = Roo.factory(config.toolbar);
37282 if (config.toolbar.items) {
37283 ti = config.toolbar.items ;
37284 delete config.toolbar.items ;
37288 this.toolbar.render(tool_el);
37289 for(var i =0;i < ti.length;i++) {
37290 // Roo.log(['add child', items[i]]);
37291 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37293 this.toolbar.items = nitems;
37295 delete config.toolbar;
37298 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37299 config.grid.scrollBody = true;;
37300 config.grid.monitorWindowResize = false; // turn off autosizing
37301 config.grid.autoHeight = false;
37302 config.grid.autoWidth = false;
37304 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37306 if (config.background) {
37307 // render grid on panel activation (if panel background)
37308 this.on('activate', function(gp) {
37309 if (!gp.grid.rendered) {
37310 gp.grid.render(this.wrapper);
37311 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37316 this.grid.render(this.wrapper);
37317 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37320 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37321 // ??? needed ??? config.el = this.wrapper;
37326 // xtype created footer. - not sure if will work as we normally have to render first..
37327 if (this.footer && !this.footer.el && this.footer.xtype) {
37329 var ctr = this.grid.getView().getFooterPanel(true);
37330 this.footer.dataSource = this.grid.dataSource;
37331 this.footer = Roo.factory(this.footer, Roo);
37332 this.footer.render(ctr);
37342 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37343 getId : function(){
37344 return this.grid.id;
37348 * Returns the grid for this panel
37349 * @return {Roo.bootstrap.Table}
37351 getGrid : function(){
37355 setSize : function(width, height){
37356 if(!this.ignoreResize(width, height)){
37357 var grid = this.grid;
37358 var size = this.adjustForComponents(width, height);
37359 var gridel = grid.getGridEl();
37360 gridel.setSize(size.width, size.height);
37362 var thd = grid.getGridEl().select('thead',true).first();
37363 var tbd = grid.getGridEl().select('tbody', true).first();
37365 tbd.setSize(width, height - thd.getHeight());
37374 beforeSlide : function(){
37375 this.grid.getView().scroller.clip();
37378 afterSlide : function(){
37379 this.grid.getView().scroller.unclip();
37382 destroy : function(){
37383 this.grid.destroy();
37385 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37390 * @class Roo.bootstrap.panel.Nest
37391 * @extends Roo.bootstrap.panel.Content
37393 * Create a new Panel, that can contain a layout.Border.
37396 * @param {Roo.BorderLayout} layout The layout for this panel
37397 * @param {String/Object} config A string to set only the title or a config object
37399 Roo.bootstrap.panel.Nest = function(config)
37401 // construct with only one argument..
37402 /* FIXME - implement nicer consturctors
37403 if (layout.layout) {
37405 layout = config.layout;
37406 delete config.layout;
37408 if (layout.xtype && !layout.getEl) {
37409 // then layout needs constructing..
37410 layout = Roo.factory(layout, Roo);
37414 config.el = config.layout.getEl();
37416 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37418 config.layout.monitorWindowResize = false; // turn off autosizing
37419 this.layout = config.layout;
37420 this.layout.getEl().addClass("roo-layout-nested-layout");
37427 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37429 setSize : function(width, height){
37430 if(!this.ignoreResize(width, height)){
37431 var size = this.adjustForComponents(width, height);
37432 var el = this.layout.getEl();
37433 if (size.height < 1) {
37434 el.setWidth(size.width);
37436 el.setSize(size.width, size.height);
37438 var touch = el.dom.offsetWidth;
37439 this.layout.layout();
37440 // ie requires a double layout on the first pass
37441 if(Roo.isIE && !this.initialized){
37442 this.initialized = true;
37443 this.layout.layout();
37448 // activate all subpanels if not currently active..
37450 setActiveState : function(active){
37451 this.active = active;
37452 this.setActiveClass(active);
37455 this.fireEvent("deactivate", this);
37459 this.fireEvent("activate", this);
37460 // not sure if this should happen before or after..
37461 if (!this.layout) {
37462 return; // should not happen..
37465 for (var r in this.layout.regions) {
37466 reg = this.layout.getRegion(r);
37467 if (reg.getActivePanel()) {
37468 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37469 reg.setActivePanel(reg.getActivePanel());
37472 if (!reg.panels.length) {
37475 reg.showPanel(reg.getPanel(0));
37484 * Returns the nested BorderLayout for this panel
37485 * @return {Roo.BorderLayout}
37487 getLayout : function(){
37488 return this.layout;
37492 * Adds a xtype elements to the layout of the nested panel
37496 xtype : 'ContentPanel',
37503 xtype : 'NestedLayoutPanel',
37509 items : [ ... list of content panels or nested layout panels.. ]
37513 * @param {Object} cfg Xtype definition of item to add.
37515 addxtype : function(cfg) {
37516 return this.layout.addxtype(cfg);
37521 * Ext JS Library 1.1.1
37522 * Copyright(c) 2006-2007, Ext JS, LLC.
37524 * Originally Released Under LGPL - original licence link has changed is not relivant.
37527 * <script type="text/javascript">
37530 * @class Roo.TabPanel
37531 * @extends Roo.util.Observable
37532 * A lightweight tab container.
37536 // basic tabs 1, built from existing content
37537 var tabs = new Roo.TabPanel("tabs1");
37538 tabs.addTab("script", "View Script");
37539 tabs.addTab("markup", "View Markup");
37540 tabs.activate("script");
37542 // more advanced tabs, built from javascript
37543 var jtabs = new Roo.TabPanel("jtabs");
37544 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37546 // set up the UpdateManager
37547 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37548 var updater = tab2.getUpdateManager();
37549 updater.setDefaultUrl("ajax1.htm");
37550 tab2.on('activate', updater.refresh, updater, true);
37552 // Use setUrl for Ajax loading
37553 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37554 tab3.setUrl("ajax2.htm", null, true);
37557 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37560 jtabs.activate("jtabs-1");
37563 * Create a new TabPanel.
37564 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37565 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37567 Roo.bootstrap.panel.Tabs = function(config){
37569 * The container element for this TabPanel.
37570 * @type Roo.Element
37572 this.el = Roo.get(config.el);
37575 if(typeof config == "boolean"){
37576 this.tabPosition = config ? "bottom" : "top";
37578 Roo.apply(this, config);
37582 if(this.tabPosition == "bottom"){
37583 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37584 this.el.addClass("roo-tabs-bottom");
37586 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37587 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37588 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37590 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37592 if(this.tabPosition != "bottom"){
37593 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37594 * @type Roo.Element
37596 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37597 this.el.addClass("roo-tabs-top");
37601 this.bodyEl.setStyle("position", "relative");
37603 this.active = null;
37604 this.activateDelegate = this.activate.createDelegate(this);
37609 * Fires when the active tab changes
37610 * @param {Roo.TabPanel} this
37611 * @param {Roo.TabPanelItem} activePanel The new active tab
37615 * @event beforetabchange
37616 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37617 * @param {Roo.TabPanel} this
37618 * @param {Object} e Set cancel to true on this object to cancel the tab change
37619 * @param {Roo.TabPanelItem} tab The tab being changed to
37621 "beforetabchange" : true
37624 Roo.EventManager.onWindowResize(this.onResize, this);
37625 this.cpad = this.el.getPadding("lr");
37626 this.hiddenCount = 0;
37629 // toolbar on the tabbar support...
37630 if (this.toolbar) {
37631 alert("no toolbar support yet");
37632 this.toolbar = false;
37634 var tcfg = this.toolbar;
37635 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37636 this.toolbar = new Roo.Toolbar(tcfg);
37637 if (Roo.isSafari) {
37638 var tbl = tcfg.container.child('table', true);
37639 tbl.setAttribute('width', '100%');
37647 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37650 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37652 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37654 tabPosition : "top",
37656 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37658 currentTabWidth : 0,
37660 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37664 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37668 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37670 preferredTabWidth : 175,
37672 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37674 resizeTabs : false,
37676 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37678 monitorResize : true,
37680 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37685 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37686 * @param {String} id The id of the div to use <b>or create</b>
37687 * @param {String} text The text for the tab
37688 * @param {String} content (optional) Content to put in the TabPanelItem body
37689 * @param {Boolean} closable (optional) True to create a close icon on the tab
37690 * @return {Roo.TabPanelItem} The created TabPanelItem
37692 addTab : function(id, text, content, closable, tpl)
37694 var item = new Roo.bootstrap.panel.TabItem({
37698 closable : closable,
37701 this.addTabItem(item);
37703 item.setContent(content);
37709 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37710 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37711 * @return {Roo.TabPanelItem}
37713 getTab : function(id){
37714 return this.items[id];
37718 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37719 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37721 hideTab : function(id){
37722 var t = this.items[id];
37725 this.hiddenCount++;
37726 this.autoSizeTabs();
37731 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37732 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37734 unhideTab : function(id){
37735 var t = this.items[id];
37737 t.setHidden(false);
37738 this.hiddenCount--;
37739 this.autoSizeTabs();
37744 * Adds an existing {@link Roo.TabPanelItem}.
37745 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37747 addTabItem : function(item){
37748 this.items[item.id] = item;
37749 this.items.push(item);
37750 // if(this.resizeTabs){
37751 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37752 // this.autoSizeTabs();
37754 // item.autoSize();
37759 * Removes a {@link Roo.TabPanelItem}.
37760 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37762 removeTab : function(id){
37763 var items = this.items;
37764 var tab = items[id];
37765 if(!tab) { return; }
37766 var index = items.indexOf(tab);
37767 if(this.active == tab && items.length > 1){
37768 var newTab = this.getNextAvailable(index);
37773 this.stripEl.dom.removeChild(tab.pnode.dom);
37774 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37775 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37777 items.splice(index, 1);
37778 delete this.items[tab.id];
37779 tab.fireEvent("close", tab);
37780 tab.purgeListeners();
37781 this.autoSizeTabs();
37784 getNextAvailable : function(start){
37785 var items = this.items;
37787 // look for a next tab that will slide over to
37788 // replace the one being removed
37789 while(index < items.length){
37790 var item = items[++index];
37791 if(item && !item.isHidden()){
37795 // if one isn't found select the previous tab (on the left)
37798 var item = items[--index];
37799 if(item && !item.isHidden()){
37807 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37808 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37810 disableTab : function(id){
37811 var tab = this.items[id];
37812 if(tab && this.active != tab){
37818 * Enables a {@link Roo.TabPanelItem} that is disabled.
37819 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37821 enableTab : function(id){
37822 var tab = this.items[id];
37827 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37828 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37829 * @return {Roo.TabPanelItem} The TabPanelItem.
37831 activate : function(id){
37832 var tab = this.items[id];
37836 if(tab == this.active || tab.disabled){
37840 this.fireEvent("beforetabchange", this, e, tab);
37841 if(e.cancel !== true && !tab.disabled){
37843 this.active.hide();
37845 this.active = this.items[id];
37846 this.active.show();
37847 this.fireEvent("tabchange", this, this.active);
37853 * Gets the active {@link Roo.TabPanelItem}.
37854 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37856 getActiveTab : function(){
37857 return this.active;
37861 * Updates the tab body element to fit the height of the container element
37862 * for overflow scrolling
37863 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37865 syncHeight : function(targetHeight){
37866 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37867 var bm = this.bodyEl.getMargins();
37868 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37869 this.bodyEl.setHeight(newHeight);
37873 onResize : function(){
37874 if(this.monitorResize){
37875 this.autoSizeTabs();
37880 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37882 beginUpdate : function(){
37883 this.updating = true;
37887 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37889 endUpdate : function(){
37890 this.updating = false;
37891 this.autoSizeTabs();
37895 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37897 autoSizeTabs : function(){
37898 var count = this.items.length;
37899 var vcount = count - this.hiddenCount;
37900 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37903 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37904 var availWidth = Math.floor(w / vcount);
37905 var b = this.stripBody;
37906 if(b.getWidth() > w){
37907 var tabs = this.items;
37908 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37909 if(availWidth < this.minTabWidth){
37910 /*if(!this.sleft){ // incomplete scrolling code
37911 this.createScrollButtons();
37914 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37917 if(this.currentTabWidth < this.preferredTabWidth){
37918 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37924 * Returns the number of tabs in this TabPanel.
37927 getCount : function(){
37928 return this.items.length;
37932 * Resizes all the tabs to the passed width
37933 * @param {Number} The new width
37935 setTabWidth : function(width){
37936 this.currentTabWidth = width;
37937 for(var i = 0, len = this.items.length; i < len; i++) {
37938 if(!this.items[i].isHidden()) {
37939 this.items[i].setWidth(width);
37945 * Destroys this TabPanel
37946 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37948 destroy : function(removeEl){
37949 Roo.EventManager.removeResizeListener(this.onResize, this);
37950 for(var i = 0, len = this.items.length; i < len; i++){
37951 this.items[i].purgeListeners();
37953 if(removeEl === true){
37954 this.el.update("");
37959 createStrip : function(container)
37961 var strip = document.createElement("nav");
37962 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37963 container.appendChild(strip);
37967 createStripList : function(strip)
37969 // div wrapper for retard IE
37970 // returns the "tr" element.
37971 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37972 //'<div class="x-tabs-strip-wrap">'+
37973 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37974 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37975 return strip.firstChild; //.firstChild.firstChild.firstChild;
37977 createBody : function(container)
37979 var body = document.createElement("div");
37980 Roo.id(body, "tab-body");
37981 //Roo.fly(body).addClass("x-tabs-body");
37982 Roo.fly(body).addClass("tab-content");
37983 container.appendChild(body);
37986 createItemBody :function(bodyEl, id){
37987 var body = Roo.getDom(id);
37989 body = document.createElement("div");
37992 //Roo.fly(body).addClass("x-tabs-item-body");
37993 Roo.fly(body).addClass("tab-pane");
37994 bodyEl.insertBefore(body, bodyEl.firstChild);
37998 createStripElements : function(stripEl, text, closable, tpl)
38000 var td = document.createElement("li"); // was td..
38003 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38006 stripEl.appendChild(td);
38008 td.className = "x-tabs-closable";
38009 if(!this.closeTpl){
38010 this.closeTpl = new Roo.Template(
38011 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38012 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38013 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38016 var el = this.closeTpl.overwrite(td, {"text": text});
38017 var close = el.getElementsByTagName("div")[0];
38018 var inner = el.getElementsByTagName("em")[0];
38019 return {"el": el, "close": close, "inner": inner};
38022 // not sure what this is..
38023 // if(!this.tabTpl){
38024 //this.tabTpl = new Roo.Template(
38025 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38026 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38028 // this.tabTpl = new Roo.Template(
38029 // '<a href="#">' +
38030 // '<span unselectable="on"' +
38031 // (this.disableTooltips ? '' : ' title="{text}"') +
38032 // ' >{text}</span></a>'
38038 var template = tpl || this.tabTpl || false;
38042 template = new Roo.Template(
38044 '<span unselectable="on"' +
38045 (this.disableTooltips ? '' : ' title="{text}"') +
38046 ' >{text}</span></a>'
38050 switch (typeof(template)) {
38054 template = new Roo.Template(template);
38060 var el = template.overwrite(td, {"text": text});
38062 var inner = el.getElementsByTagName("span")[0];
38064 return {"el": el, "inner": inner};
38072 * @class Roo.TabPanelItem
38073 * @extends Roo.util.Observable
38074 * Represents an individual item (tab plus body) in a TabPanel.
38075 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38076 * @param {String} id The id of this TabPanelItem
38077 * @param {String} text The text for the tab of this TabPanelItem
38078 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38080 Roo.bootstrap.panel.TabItem = function(config){
38082 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38083 * @type Roo.TabPanel
38085 this.tabPanel = config.panel;
38087 * The id for this TabPanelItem
38090 this.id = config.id;
38092 this.disabled = false;
38094 this.text = config.text;
38096 this.loaded = false;
38097 this.closable = config.closable;
38100 * The body element for this TabPanelItem.
38101 * @type Roo.Element
38103 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38104 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38105 this.bodyEl.setStyle("display", "block");
38106 this.bodyEl.setStyle("zoom", "1");
38107 //this.hideAction();
38109 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38111 this.el = Roo.get(els.el);
38112 this.inner = Roo.get(els.inner, true);
38113 this.textEl = Roo.get(this.el.dom.firstChild, true);
38114 this.pnode = Roo.get(els.el.parentNode, true);
38115 // this.el.on("mousedown", this.onTabMouseDown, this);
38116 this.el.on("click", this.onTabClick, this);
38118 if(config.closable){
38119 var c = Roo.get(els.close, true);
38120 c.dom.title = this.closeText;
38121 c.addClassOnOver("close-over");
38122 c.on("click", this.closeClick, this);
38128 * Fires when this tab becomes the active tab.
38129 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38130 * @param {Roo.TabPanelItem} this
38134 * @event beforeclose
38135 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38136 * @param {Roo.TabPanelItem} this
38137 * @param {Object} e Set cancel to true on this object to cancel the close.
38139 "beforeclose": true,
38142 * Fires when this tab is closed.
38143 * @param {Roo.TabPanelItem} this
38147 * @event deactivate
38148 * Fires when this tab is no longer the active tab.
38149 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38150 * @param {Roo.TabPanelItem} this
38152 "deactivate" : true
38154 this.hidden = false;
38156 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38159 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38161 purgeListeners : function(){
38162 Roo.util.Observable.prototype.purgeListeners.call(this);
38163 this.el.removeAllListeners();
38166 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38169 this.pnode.addClass("active");
38172 this.tabPanel.stripWrap.repaint();
38174 this.fireEvent("activate", this.tabPanel, this);
38178 * Returns true if this tab is the active tab.
38179 * @return {Boolean}
38181 isActive : function(){
38182 return this.tabPanel.getActiveTab() == this;
38186 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38189 this.pnode.removeClass("active");
38191 this.fireEvent("deactivate", this.tabPanel, this);
38194 hideAction : function(){
38195 this.bodyEl.hide();
38196 this.bodyEl.setStyle("position", "absolute");
38197 this.bodyEl.setLeft("-20000px");
38198 this.bodyEl.setTop("-20000px");
38201 showAction : function(){
38202 this.bodyEl.setStyle("position", "relative");
38203 this.bodyEl.setTop("");
38204 this.bodyEl.setLeft("");
38205 this.bodyEl.show();
38209 * Set the tooltip for the tab.
38210 * @param {String} tooltip The tab's tooltip
38212 setTooltip : function(text){
38213 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38214 this.textEl.dom.qtip = text;
38215 this.textEl.dom.removeAttribute('title');
38217 this.textEl.dom.title = text;
38221 onTabClick : function(e){
38222 e.preventDefault();
38223 this.tabPanel.activate(this.id);
38226 onTabMouseDown : function(e){
38227 e.preventDefault();
38228 this.tabPanel.activate(this.id);
38231 getWidth : function(){
38232 return this.inner.getWidth();
38235 setWidth : function(width){
38236 var iwidth = width - this.pnode.getPadding("lr");
38237 this.inner.setWidth(iwidth);
38238 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38239 this.pnode.setWidth(width);
38243 * Show or hide the tab
38244 * @param {Boolean} hidden True to hide or false to show.
38246 setHidden : function(hidden){
38247 this.hidden = hidden;
38248 this.pnode.setStyle("display", hidden ? "none" : "");
38252 * Returns true if this tab is "hidden"
38253 * @return {Boolean}
38255 isHidden : function(){
38256 return this.hidden;
38260 * Returns the text for this tab
38263 getText : function(){
38267 autoSize : function(){
38268 //this.el.beginMeasure();
38269 this.textEl.setWidth(1);
38271 * #2804 [new] Tabs in Roojs
38272 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38274 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38275 //this.el.endMeasure();
38279 * Sets the text for the tab (Note: this also sets the tooltip text)
38280 * @param {String} text The tab's text and tooltip
38282 setText : function(text){
38284 this.textEl.update(text);
38285 this.setTooltip(text);
38286 //if(!this.tabPanel.resizeTabs){
38287 // this.autoSize();
38291 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38293 activate : function(){
38294 this.tabPanel.activate(this.id);
38298 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38300 disable : function(){
38301 if(this.tabPanel.active != this){
38302 this.disabled = true;
38303 this.pnode.addClass("disabled");
38308 * Enables this TabPanelItem if it was previously disabled.
38310 enable : function(){
38311 this.disabled = false;
38312 this.pnode.removeClass("disabled");
38316 * Sets the content for this TabPanelItem.
38317 * @param {String} content The content
38318 * @param {Boolean} loadScripts true to look for and load scripts
38320 setContent : function(content, loadScripts){
38321 this.bodyEl.update(content, loadScripts);
38325 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38326 * @return {Roo.UpdateManager} The UpdateManager
38328 getUpdateManager : function(){
38329 return this.bodyEl.getUpdateManager();
38333 * Set a URL to be used to load the content for this TabPanelItem.
38334 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38335 * @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)
38336 * @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)
38337 * @return {Roo.UpdateManager} The UpdateManager
38339 setUrl : function(url, params, loadOnce){
38340 if(this.refreshDelegate){
38341 this.un('activate', this.refreshDelegate);
38343 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38344 this.on("activate", this.refreshDelegate);
38345 return this.bodyEl.getUpdateManager();
38349 _handleRefresh : function(url, params, loadOnce){
38350 if(!loadOnce || !this.loaded){
38351 var updater = this.bodyEl.getUpdateManager();
38352 updater.update(url, params, this._setLoaded.createDelegate(this));
38357 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38358 * Will fail silently if the setUrl method has not been called.
38359 * This does not activate the panel, just updates its content.
38361 refresh : function(){
38362 if(this.refreshDelegate){
38363 this.loaded = false;
38364 this.refreshDelegate();
38369 _setLoaded : function(){
38370 this.loaded = true;
38374 closeClick : function(e){
38377 this.fireEvent("beforeclose", this, o);
38378 if(o.cancel !== true){
38379 this.tabPanel.removeTab(this.id);
38383 * The text displayed in the tooltip for the close icon.
38386 closeText : "Close this tab"
38389 * This script refer to:
38390 * Title: International Telephone Input
38391 * Author: Jack O'Connor
38392 * Code version: v12.1.12
38393 * Availability: https://github.com/jackocnr/intl-tel-input.git
38396 Roo.bootstrap.PhoneInputData = function() {
38399 "Afghanistan (افغانستان)",
38404 "Albania (Shqipëri)",
38409 "Algeria (الجزائر)",
38434 "Antigua and Barbuda",
38444 "Armenia (Հայաստան)",
38460 "Austria (Österreich)",
38465 "Azerbaijan (Azərbaycan)",
38475 "Bahrain (البحرين)",
38480 "Bangladesh (বাংলাদেশ)",
38490 "Belarus (Беларусь)",
38495 "Belgium (België)",
38525 "Bosnia and Herzegovina (Босна и Херцеговина)",
38540 "British Indian Ocean Territory",
38545 "British Virgin Islands",
38555 "Bulgaria (България)",
38565 "Burundi (Uburundi)",
38570 "Cambodia (កម្ពុជា)",
38575 "Cameroon (Cameroun)",
38584 ["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"]
38587 "Cape Verde (Kabu Verdi)",
38592 "Caribbean Netherlands",
38603 "Central African Republic (République centrafricaine)",
38623 "Christmas Island",
38629 "Cocos (Keeling) Islands",
38640 "Comoros (جزر القمر)",
38645 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38650 "Congo (Republic) (Congo-Brazzaville)",
38670 "Croatia (Hrvatska)",
38691 "Czech Republic (Česká republika)",
38696 "Denmark (Danmark)",
38711 "Dominican Republic (República Dominicana)",
38715 ["809", "829", "849"]
38733 "Equatorial Guinea (Guinea Ecuatorial)",
38753 "Falkland Islands (Islas Malvinas)",
38758 "Faroe Islands (Føroyar)",
38779 "French Guiana (Guyane française)",
38784 "French Polynesia (Polynésie française)",
38799 "Georgia (საქართველო)",
38804 "Germany (Deutschland)",
38824 "Greenland (Kalaallit Nunaat)",
38861 "Guinea-Bissau (Guiné Bissau)",
38886 "Hungary (Magyarország)",
38891 "Iceland (Ísland)",
38911 "Iraq (العراق)",
38927 "Israel (ישראל)",
38954 "Jordan (الأردن)",
38959 "Kazakhstan (Казахстан)",
38980 "Kuwait (الكويت)",
38985 "Kyrgyzstan (Кыргызстан)",
38995 "Latvia (Latvija)",
39000 "Lebanon (لبنان)",
39015 "Libya (ليبيا)",
39025 "Lithuania (Lietuva)",
39040 "Macedonia (FYROM) (Македонија)",
39045 "Madagascar (Madagasikara)",
39075 "Marshall Islands",
39085 "Mauritania (موريتانيا)",
39090 "Mauritius (Moris)",
39111 "Moldova (Republica Moldova)",
39121 "Mongolia (Монгол)",
39126 "Montenegro (Crna Gora)",
39136 "Morocco (المغرب)",
39142 "Mozambique (Moçambique)",
39147 "Myanmar (Burma) (မြန်မာ)",
39152 "Namibia (Namibië)",
39167 "Netherlands (Nederland)",
39172 "New Caledonia (Nouvelle-Calédonie)",
39207 "North Korea (조선 민주주의 인민 공화국)",
39212 "Northern Mariana Islands",
39228 "Pakistan (پاکستان)",
39238 "Palestine (فلسطين)",
39248 "Papua New Guinea",
39290 "Réunion (La Réunion)",
39296 "Romania (România)",
39312 "Saint Barthélemy",
39323 "Saint Kitts and Nevis",
39333 "Saint Martin (Saint-Martin (partie française))",
39339 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39344 "Saint Vincent and the Grenadines",
39359 "São Tomé and Príncipe (São Tomé e Príncipe)",
39364 "Saudi Arabia (المملكة العربية السعودية)",
39369 "Senegal (Sénégal)",
39399 "Slovakia (Slovensko)",
39404 "Slovenia (Slovenija)",
39414 "Somalia (Soomaaliya)",
39424 "South Korea (대한민국)",
39429 "South Sudan (جنوب السودان)",
39439 "Sri Lanka (ශ්රී ලංකාව)",
39444 "Sudan (السودان)",
39454 "Svalbard and Jan Mayen",
39465 "Sweden (Sverige)",
39470 "Switzerland (Schweiz)",
39475 "Syria (سوريا)",
39520 "Trinidad and Tobago",
39525 "Tunisia (تونس)",
39530 "Turkey (Türkiye)",
39540 "Turks and Caicos Islands",
39550 "U.S. Virgin Islands",
39560 "Ukraine (Україна)",
39565 "United Arab Emirates (الإمارات العربية المتحدة)",
39587 "Uzbekistan (Oʻzbekiston)",
39597 "Vatican City (Città del Vaticano)",
39608 "Vietnam (Việt Nam)",
39613 "Wallis and Futuna (Wallis-et-Futuna)",
39618 "Western Sahara (الصحراء الغربية)",
39624 "Yemen (اليمن)",
39648 * This script refer to:
39649 * Title: International Telephone Input
39650 * Author: Jack O'Connor
39651 * Code version: v12.1.12
39652 * Availability: https://github.com/jackocnr/intl-tel-input.git
39656 * @class Roo.bootstrap.PhoneInput
39657 * @extends Roo.bootstrap.TriggerField
39658 * An input with International dial-code selection
39660 * @cfg {String} defaultDialCode default '+852'
39661 * @cfg {Array} preferedCountries default []
39664 * Create a new PhoneInput.
39665 * @param {Object} config Configuration options
39668 Roo.bootstrap.PhoneInput = function(config) {
39669 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39672 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39674 listWidth: undefined,
39676 selectedClass: 'active',
39678 invalidClass : "has-warning",
39680 validClass: 'has-success',
39682 allowed: '0123456789',
39685 * @cfg {String} defaultDialCode The default dial code when initializing the input
39687 defaultDialCode: '+852',
39690 * @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
39692 preferedCountries: false,
39694 getAutoCreate : function()
39696 var data = Roo.bootstrap.PhoneInputData();
39697 var align = this.labelAlign || this.parentLabelAlign();
39700 this.allCountries = [];
39701 this.dialCodeMapping = [];
39703 for (var i = 0; i < data.length; i++) {
39705 this.allCountries[i] = {
39709 priority: c[3] || 0,
39710 areaCodes: c[4] || null
39712 this.dialCodeMapping[c[2]] = {
39715 priority: c[3] || 0,
39716 areaCodes: c[4] || null
39728 cls : 'form-control tel-input',
39729 autocomplete: 'new-password'
39732 var hiddenInput = {
39735 cls: 'hidden-tel-input'
39739 hiddenInput.name = this.name;
39742 if (this.disabled) {
39743 input.disabled = true;
39746 var flag_container = {
39763 cls: this.hasFeedback ? 'has-feedback' : '',
39769 cls: 'dial-code-holder',
39776 cls: 'roo-select2-container input-group',
39783 if (this.fieldLabel.length) {
39786 tooltip: 'This field is required'
39792 cls: 'control-label',
39798 html: this.fieldLabel
39801 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39807 if(this.indicatorpos == 'right') {
39808 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39815 if(align == 'left') {
39823 if(this.labelWidth > 12){
39824 label.style = "width: " + this.labelWidth + 'px';
39826 if(this.labelWidth < 13 && this.labelmd == 0){
39827 this.labelmd = this.labelWidth;
39829 if(this.labellg > 0){
39830 label.cls += ' col-lg-' + this.labellg;
39831 input.cls += ' col-lg-' + (12 - this.labellg);
39833 if(this.labelmd > 0){
39834 label.cls += ' col-md-' + this.labelmd;
39835 container.cls += ' col-md-' + (12 - this.labelmd);
39837 if(this.labelsm > 0){
39838 label.cls += ' col-sm-' + this.labelsm;
39839 container.cls += ' col-sm-' + (12 - this.labelsm);
39841 if(this.labelxs > 0){
39842 label.cls += ' col-xs-' + this.labelxs;
39843 container.cls += ' col-xs-' + (12 - this.labelxs);
39853 var settings = this;
39855 ['xs','sm','md','lg'].map(function(size){
39856 if (settings[size]) {
39857 cfg.cls += ' col-' + size + '-' + settings[size];
39861 this.store = new Roo.data.Store({
39862 proxy : new Roo.data.MemoryProxy({}),
39863 reader : new Roo.data.JsonReader({
39874 'name' : 'dialCode',
39878 'name' : 'priority',
39882 'name' : 'areaCodes',
39889 if(!this.preferedCountries) {
39890 this.preferedCountries = [
39897 var p = this.preferedCountries.reverse();
39900 for (var i = 0; i < p.length; i++) {
39901 for (var j = 0; j < this.allCountries.length; j++) {
39902 if(this.allCountries[j].iso2 == p[i]) {
39903 var t = this.allCountries[j];
39904 this.allCountries.splice(j,1);
39905 this.allCountries.unshift(t);
39911 this.store.proxy.data = {
39913 data: this.allCountries
39919 initEvents : function()
39922 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39924 this.indicator = this.indicatorEl();
39925 this.flag = this.flagEl();
39926 this.dialCodeHolder = this.dialCodeHolderEl();
39928 this.trigger = this.el.select('div.flag-box',true).first();
39929 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39934 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39935 _this.list.setWidth(lw);
39938 this.list.on('mouseover', this.onViewOver, this);
39939 this.list.on('mousemove', this.onViewMove, this);
39940 this.inputEl().on("keyup", this.onKeyUp, this);
39942 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39944 this.view = new Roo.View(this.list, this.tpl, {
39945 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39948 this.view.on('click', this.onViewClick, this);
39949 this.setValue(this.defaultDialCode);
39952 onTriggerClick : function(e)
39954 Roo.log('trigger click');
39959 if(this.isExpanded()){
39961 this.hasFocus = false;
39963 this.store.load({});
39964 this.hasFocus = true;
39969 isExpanded : function()
39971 return this.list.isVisible();
39974 collapse : function()
39976 if(!this.isExpanded()){
39980 Roo.get(document).un('mousedown', this.collapseIf, this);
39981 Roo.get(document).un('mousewheel', this.collapseIf, this);
39982 this.fireEvent('collapse', this);
39986 expand : function()
39990 if(this.isExpanded() || !this.hasFocus){
39994 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39995 this.list.setWidth(lw);
39998 this.restrictHeight();
40000 Roo.get(document).on('mousedown', this.collapseIf, this);
40001 Roo.get(document).on('mousewheel', this.collapseIf, this);
40003 this.fireEvent('expand', this);
40006 restrictHeight : function()
40008 this.list.alignTo(this.inputEl(), this.listAlign);
40009 this.list.alignTo(this.inputEl(), this.listAlign);
40012 onViewOver : function(e, t)
40014 if(this.inKeyMode){
40017 var item = this.view.findItemFromChild(t);
40020 var index = this.view.indexOf(item);
40021 this.select(index, false);
40026 onViewClick : function(view, doFocus, el, e)
40028 var index = this.view.getSelectedIndexes()[0];
40030 var r = this.store.getAt(index);
40033 this.onSelect(r, index);
40035 if(doFocus !== false && !this.blockFocus){
40036 this.inputEl().focus();
40040 onViewMove : function(e, t)
40042 this.inKeyMode = false;
40045 select : function(index, scrollIntoView)
40047 this.selectedIndex = index;
40048 this.view.select(index);
40049 if(scrollIntoView !== false){
40050 var el = this.view.getNode(index);
40052 this.list.scrollChildIntoView(el, false);
40057 createList : function()
40059 this.list = Roo.get(document.body).createChild({
40061 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40062 style: 'display:none'
40065 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40068 collapseIf : function(e)
40070 var in_combo = e.within(this.el);
40071 var in_list = e.within(this.list);
40072 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40074 if (in_combo || in_list || is_list) {
40080 onSelect : function(record, index)
40082 if(this.fireEvent('beforeselect', this, record, index) !== false){
40084 this.setFlagClass(record.data.iso2);
40085 this.setDialCode(record.data.dialCode);
40086 this.hasFocus = false;
40088 this.fireEvent('select', this, record, index);
40092 flagEl : function()
40094 var flag = this.el.select('div.flag',true).first();
40101 dialCodeHolderEl : function()
40103 var d = this.el.select('input.dial-code-holder',true).first();
40110 setDialCode : function(v)
40112 this.dialCodeHolder.dom.value = '+'+v;
40115 setFlagClass : function(n)
40117 this.flag.dom.className = 'flag '+n;
40120 getValue : function()
40122 var v = this.inputEl().getValue();
40123 if(this.dialCodeHolder) {
40124 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40129 setValue : function(v)
40131 var d = this.getDialCode(v);
40133 //invalid dial code
40134 if(v.length == 0 || !d || d.length == 0) {
40136 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40137 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40143 this.setFlagClass(this.dialCodeMapping[d].iso2);
40144 this.setDialCode(d);
40145 this.inputEl().dom.value = v.replace('+'+d,'');
40146 this.hiddenEl().dom.value = this.getValue();
40151 getDialCode : function(v)
40155 if (v.length == 0) {
40156 return this.dialCodeHolder.dom.value;
40160 if (v.charAt(0) != "+") {
40163 var numericChars = "";
40164 for (var i = 1; i < v.length; i++) {
40165 var c = v.charAt(i);
40168 if (this.dialCodeMapping[numericChars]) {
40169 dialCode = v.substr(1, i);
40171 if (numericChars.length == 4) {
40181 this.setValue(this.defaultDialCode);
40185 hiddenEl : function()
40187 return this.el.select('input.hidden-tel-input',true).first();
40190 onKeyUp : function(e){
40192 var k = e.getKey();
40193 var c = e.getCharCode();
40196 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40197 this.allowed.indexOf(String.fromCharCode(c)) === -1
40202 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40205 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40209 this.setValue(this.getValue());
40214 * @class Roo.bootstrap.MoneyField
40215 * @extends Roo.bootstrap.ComboBox
40216 * Bootstrap MoneyField class
40219 * Create a new MoneyField.
40220 * @param {Object} config Configuration options
40223 Roo.bootstrap.MoneyField = function(config) {
40225 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40229 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40232 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40234 allowDecimals : true,
40236 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40238 decimalSeparator : ".",
40240 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40242 decimalPrecision : 0,
40244 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40246 allowNegative : true,
40248 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40250 minValue : Number.NEGATIVE_INFINITY,
40252 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40254 maxValue : Number.MAX_VALUE,
40256 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40258 minText : "The minimum value for this field is {0}",
40260 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40262 maxText : "The maximum value for this field is {0}",
40264 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40265 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40267 nanText : "{0} is not a valid number",
40269 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40273 * @cfg {String} defaults currency of the MoneyField
40274 * value should be in lkey
40276 defaultCurrency : false,
40278 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40280 thousandsDelimiter : false,
40290 getAutoCreate : function()
40292 var align = this.labelAlign || this.parentLabelAlign();
40304 cls : 'form-control roo-money-amount-input',
40305 autocomplete: 'new-password'
40308 var hiddenInput = {
40312 cls: 'hidden-number-input'
40316 hiddenInput.name = this.name;
40319 if (this.disabled) {
40320 input.disabled = true;
40323 var clg = 12 - this.inputlg;
40324 var cmd = 12 - this.inputmd;
40325 var csm = 12 - this.inputsm;
40326 var cxs = 12 - this.inputxs;
40330 cls : 'row roo-money-field',
40334 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40338 cls: 'roo-select2-container input-group',
40342 cls : 'form-control roo-money-currency-input',
40343 autocomplete: 'new-password',
40345 name : this.currencyName
40349 cls : 'input-group-addon',
40363 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40367 cls: this.hasFeedback ? 'has-feedback' : '',
40378 if (this.fieldLabel.length) {
40381 tooltip: 'This field is required'
40387 cls: 'control-label',
40393 html: this.fieldLabel
40396 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40402 if(this.indicatorpos == 'right') {
40403 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40410 if(align == 'left') {
40418 if(this.labelWidth > 12){
40419 label.style = "width: " + this.labelWidth + 'px';
40421 if(this.labelWidth < 13 && this.labelmd == 0){
40422 this.labelmd = this.labelWidth;
40424 if(this.labellg > 0){
40425 label.cls += ' col-lg-' + this.labellg;
40426 input.cls += ' col-lg-' + (12 - this.labellg);
40428 if(this.labelmd > 0){
40429 label.cls += ' col-md-' + this.labelmd;
40430 container.cls += ' col-md-' + (12 - this.labelmd);
40432 if(this.labelsm > 0){
40433 label.cls += ' col-sm-' + this.labelsm;
40434 container.cls += ' col-sm-' + (12 - this.labelsm);
40436 if(this.labelxs > 0){
40437 label.cls += ' col-xs-' + this.labelxs;
40438 container.cls += ' col-xs-' + (12 - this.labelxs);
40449 var settings = this;
40451 ['xs','sm','md','lg'].map(function(size){
40452 if (settings[size]) {
40453 cfg.cls += ' col-' + size + '-' + settings[size];
40460 initEvents : function()
40462 this.indicator = this.indicatorEl();
40464 this.initCurrencyEvent();
40466 this.initNumberEvent();
40469 initCurrencyEvent : function()
40472 throw "can not find store for combo";
40475 this.store = Roo.factory(this.store, Roo.data);
40476 this.store.parent = this;
40480 this.triggerEl = this.el.select('.input-group-addon', true).first();
40482 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40487 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40488 _this.list.setWidth(lw);
40491 this.list.on('mouseover', this.onViewOver, this);
40492 this.list.on('mousemove', this.onViewMove, this);
40493 this.list.on('scroll', this.onViewScroll, this);
40496 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40499 this.view = new Roo.View(this.list, this.tpl, {
40500 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40503 this.view.on('click', this.onViewClick, this);
40505 this.store.on('beforeload', this.onBeforeLoad, this);
40506 this.store.on('load', this.onLoad, this);
40507 this.store.on('loadexception', this.onLoadException, this);
40509 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40510 "up" : function(e){
40511 this.inKeyMode = true;
40515 "down" : function(e){
40516 if(!this.isExpanded()){
40517 this.onTriggerClick();
40519 this.inKeyMode = true;
40524 "enter" : function(e){
40527 if(this.fireEvent("specialkey", this, e)){
40528 this.onViewClick(false);
40534 "esc" : function(e){
40538 "tab" : function(e){
40541 if(this.fireEvent("specialkey", this, e)){
40542 this.onViewClick(false);
40550 doRelay : function(foo, bar, hname){
40551 if(hname == 'down' || this.scope.isExpanded()){
40552 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40560 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40564 initNumberEvent : function(e)
40566 this.inputEl().on("keydown" , this.fireKey, this);
40567 this.inputEl().on("focus", this.onFocus, this);
40568 this.inputEl().on("blur", this.onBlur, this);
40570 this.inputEl().relayEvent('keyup', this);
40572 if(this.indicator){
40573 this.indicator.addClass('invisible');
40576 this.originalValue = this.getValue();
40578 if(this.validationEvent == 'keyup'){
40579 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40580 this.inputEl().on('keyup', this.filterValidation, this);
40582 else if(this.validationEvent !== false){
40583 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40586 if(this.selectOnFocus){
40587 this.on("focus", this.preFocus, this);
40590 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40591 this.inputEl().on("keypress", this.filterKeys, this);
40593 this.inputEl().relayEvent('keypress', this);
40596 var allowed = "0123456789";
40598 if(this.allowDecimals){
40599 allowed += this.decimalSeparator;
40602 if(this.allowNegative){
40606 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40608 var keyPress = function(e){
40610 var k = e.getKey();
40612 var c = e.getCharCode();
40615 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40616 allowed.indexOf(String.fromCharCode(c)) === -1
40622 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40626 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40631 this.inputEl().on("keypress", keyPress, this);
40635 onTriggerClick : function(e)
40642 this.loadNext = false;
40644 if(this.isExpanded()){
40649 this.hasFocus = true;
40651 if(this.triggerAction == 'all') {
40652 this.doQuery(this.allQuery, true);
40656 this.doQuery(this.getRawValue());
40659 getCurrency : function()
40661 var v = this.currencyEl().getValue();
40666 restrictHeight : function()
40668 this.list.alignTo(this.currencyEl(), this.listAlign);
40669 this.list.alignTo(this.currencyEl(), this.listAlign);
40672 onViewClick : function(view, doFocus, el, e)
40674 var index = this.view.getSelectedIndexes()[0];
40676 var r = this.store.getAt(index);
40679 this.onSelect(r, index);
40683 onSelect : function(record, index){
40685 if(this.fireEvent('beforeselect', this, record, index) !== false){
40687 this.setFromCurrencyData(index > -1 ? record.data : false);
40691 this.fireEvent('select', this, record, index);
40695 setFromCurrencyData : function(o)
40699 this.lastCurrency = o;
40701 if (this.currencyField) {
40702 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40704 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40707 this.lastSelectionText = currency;
40709 //setting default currency
40710 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40711 this.setCurrency(this.defaultCurrency);
40715 this.setCurrency(currency);
40718 setFromData : function(o)
40722 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40724 this.setFromCurrencyData(c);
40729 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40731 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40734 this.setValue(value);
40738 setCurrency : function(v)
40740 this.currencyValue = v;
40743 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40748 setValue : function(v)
40750 v = this.fixPrecision(v);
40752 v = String(v).replace(".", this.decimalSeparator);
40754 this.value = (v === null || v === undefined) ? '' : v;
40758 this.inputEl().dom.value = this.hiddenEl().dom.value = this.value;
40760 if(this.allowDecimals && this.decimalPrecision != -1 && !isNaN(this.value)){
40761 this.inputEl().dom.value = Roo.util.Format.number(v, this.decimalPrecision,
40762 this.thousandsDelimiter || ','
40766 if(this.allowBlank && !v) {
40767 this.inputEl().dom.value = '';
40774 getRawValue : function()
40776 var v = this.inputEl().getValue();
40781 getValue : function()
40783 return this.fixPrecision(this.parseValue(this.getRawValue()));
40786 parseValue : function(value)
40788 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40789 return isNaN(value) ? '' : value;
40792 fixPrecision : function(value)
40794 var nan = isNaN(value);
40796 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40797 return nan ? '' : value;
40800 return parseFloat(value).toFixed(this.decimalPrecision);
40803 decimalPrecisionFcn : function(v)
40805 return Math.floor(v);
40808 validateValue : function(value)
40810 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40814 var num = this.parseValue(value);
40817 this.markInvalid(String.format(this.nanText, value));
40821 if(num < this.minValue){
40822 this.markInvalid(String.format(this.minText, this.minValue));
40826 if(num > this.maxValue){
40827 this.markInvalid(String.format(this.maxText, this.maxValue));
40834 validate : function()
40836 if(this.disabled || this.allowBlank){
40841 var currency = this.getCurrency();
40843 if(this.validateValue(this.getRawValue()) && currency.length){
40848 this.markInvalid();
40852 getName: function()
40857 beforeBlur : function()
40863 var v = this.parseValue(this.getRawValue());
40870 onBlur : function()
40874 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40875 //this.el.removeClass(this.focusClass);
40878 this.hasFocus = false;
40880 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40884 var v = this.getValue();
40886 if(String(v) !== String(this.startValue)){
40887 this.fireEvent('change', this, v, this.startValue);
40890 this.fireEvent("blur", this);
40893 inputEl : function()
40895 return this.el.select('.roo-money-amount-input', true).first();
40898 currencyEl : function()
40900 return this.el.select('.roo-money-currency-input', true).first();
40903 hiddenEl : function()
40905 return this.el.select('input.hidden-number-input',true).first();