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)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
105 cfg.id = this.id || Roo.id();
107 // fill in the extra attributes
108 if (this.xattr && typeof(this.xattr) =='object') {
109 for (var i in this.xattr) {
110 cfg[i] = this.xattr[i];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
165 //Roo.log(['addxtype', cn]);
167 cn.parentType = this.xtype; //??
168 cn.parentId = this.id;
170 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171 if (typeof(cn.container_method) == 'string') {
172 cntr = cn.container_method;
176 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
178 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
180 var build_from_html = Roo.XComponent.build_from_html;
182 var is_body = (tree.xtype == 'Body') ;
184 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186 var self_cntr_el = Roo.get(this[cntr](false));
188 // do not try and build conditional elements
189 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
193 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195 return this.addxtypeChild(tree,cntr, is_body);
198 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
201 return this.addxtypeChild(Roo.apply({}, tree),cntr);
204 Roo.log('skipping render');
210 if (!build_from_html) {
214 // this i think handles overlaying multiple children of the same type
215 // with the sam eelement.. - which might be buggy..
217 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
223 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
227 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
234 addxtypeChild : function (tree, cntr, is_body)
236 Roo.debug && Roo.log('addxtypeChild:' + cntr);
238 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
241 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242 (typeof(tree['flexy:foreach']) != 'undefined');
246 skip_children = false;
247 // render the element if it's not BODY.
250 cn = Roo.factory(tree);
252 cn.parentType = this.xtype; //??
253 cn.parentId = this.id;
255 var build_from_html = Roo.XComponent.build_from_html;
258 // does the container contain child eleemnts with 'xtype' attributes.
259 // that match this xtype..
260 // note - when we render we create these as well..
261 // so we should check to see if body has xtype set.
262 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
264 var self_cntr_el = Roo.get(this[cntr](false));
265 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
267 //Roo.log(Roo.XComponent.build_from_html);
268 //Roo.log("got echild:");
271 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272 // and are not displayed -this causes this to use up the wrong element when matching.
273 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
276 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
283 //echild.dom.removeAttribute('xtype');
285 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286 Roo.debug && Roo.log(self_cntr_el);
287 Roo.debug && Roo.log(echild);
288 Roo.debug && Roo.log(cn);
294 // if object has flexy:if - then it may or may not be rendered.
295 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
296 // skip a flexy if element.
297 Roo.debug && Roo.log('skipping render');
298 Roo.debug && Roo.log(tree);
300 Roo.debug && Roo.log('skipping all children');
301 skip_children = true;
306 // actually if flexy:foreach is found, we really want to create
307 // multiple copies here...
309 //Roo.log(this[cntr]());
310 // some elements do not have render methods.. like the layouts...
312 if(this[cntr](true) === false){
317 cn.render && cn.render(this[cntr](true));
320 // then add the element..
328 if (typeof (tree.menu) != 'undefined') {
329 tree.menu.parentType = cn.xtype;
330 tree.menu.triggerEl = cn.el;
331 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
335 if (!tree.items || !tree.items.length) {
337 //Roo.log(["no children", this]);
342 var items = tree.items;
345 //Roo.log(items.length);
347 if (!skip_children) {
348 for(var i =0;i < items.length;i++) {
349 // Roo.log(['add child', items[i]]);
350 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
356 //Roo.log("fire childrenrendered");
358 cn.fireEvent('childrenrendered', this);
363 * Show a component - removes 'hidden' class
368 this.el.removeClass('hidden');
372 * Hide a component - adds 'hidden' class
376 if (this.el && !this.el.hasClass('hidden')) {
377 this.el.addClass('hidden');
390 * @class Roo.bootstrap.Body
391 * @extends Roo.bootstrap.Component
392 * Bootstrap Body class
396 * @param {Object} config The config object
399 Roo.bootstrap.Body = function(config){
401 config = config || {};
403 Roo.bootstrap.Body.superclass.constructor.call(this, config);
404 this.el = Roo.get(config.el ? config.el : document.body );
405 if (this.cls && this.cls.length) {
406 Roo.get(document.body).addClass(this.cls);
410 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
412 is_body : true,// just to make sure it's constructed?
417 onRender : function(ct, position)
419 /* Roo.log("Roo.bootstrap.Body - onRender");
420 if (this.cls && this.cls.length) {
421 Roo.get(document.body).addClass(this.cls);
440 * @class Roo.bootstrap.ButtonGroup
441 * @extends Roo.bootstrap.Component
442 * Bootstrap ButtonGroup class
443 * @cfg {String} size lg | sm | xs (default empty normal)
444 * @cfg {String} align vertical | justified (default none)
445 * @cfg {String} direction up | down (default down)
446 * @cfg {Boolean} toolbar false | true
447 * @cfg {Boolean} btn true | false
452 * @param {Object} config The config object
455 Roo.bootstrap.ButtonGroup = function(config){
456 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
459 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
467 getAutoCreate : function(){
473 cfg.html = this.html || cfg.html;
484 if (['vertical','justified'].indexOf(this.align)!==-1) {
485 cfg.cls = 'btn-group-' + this.align;
487 if (this.align == 'justified') {
488 console.log(this.items);
492 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
493 cfg.cls += ' btn-group-' + this.size;
496 if (this.direction == 'up') {
497 cfg.cls += ' dropup' ;
513 * @class Roo.bootstrap.Button
514 * @extends Roo.bootstrap.Component
515 * Bootstrap Button class
516 * @cfg {String} html The button content
517 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
518 * @cfg {String} size ( lg | sm | xs)
519 * @cfg {String} tag ( a | input | submit)
520 * @cfg {String} href empty or href
521 * @cfg {Boolean} disabled default false;
522 * @cfg {Boolean} isClose default false;
523 * @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)
524 * @cfg {String} badge text for badge
525 * @cfg {String} theme default
526 * @cfg {Boolean} inverse
527 * @cfg {Boolean} toggle
528 * @cfg {String} ontext text for on toggle state
529 * @cfg {String} offtext text for off toggle state
530 * @cfg {Boolean} defaulton
531 * @cfg {Boolean} preventDefault default true
532 * @cfg {Boolean} removeClass remove the standard class..
533 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
536 * Create a new button
537 * @param {Object} config The config object
541 Roo.bootstrap.Button = function(config){
542 Roo.bootstrap.Button.superclass.constructor.call(this, config);
543 this.weightClass = ["btn-default",
555 * When a butotn is pressed
556 * @param {Roo.bootstrap.Button} this
557 * @param {Roo.EventObject} e
562 * After the button has been toggles
563 * @param {Roo.EventObject} e
564 * @param {boolean} pressed (also available as button.pressed)
570 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
588 preventDefault: true,
597 getAutoCreate : function(){
605 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
606 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
611 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
613 if (this.toggle == true) {
616 cls: 'slider-frame roo-button',
621 'data-off-text':'OFF',
622 cls: 'slider-button',
628 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
629 cfg.cls += ' '+this.weight;
638 cfg["aria-hidden"] = true;
640 cfg.html = "×";
646 if (this.theme==='default') {
647 cfg.cls = 'btn roo-button';
649 //if (this.parentType != 'Navbar') {
650 this.weight = this.weight.length ? this.weight : 'default';
652 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
654 cfg.cls += ' btn-' + this.weight;
656 } else if (this.theme==='glow') {
659 cfg.cls = 'btn-glow roo-button';
661 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
663 cfg.cls += ' ' + this.weight;
669 this.cls += ' inverse';
674 cfg.cls += ' active';
678 cfg.disabled = 'disabled';
682 Roo.log('changing to ul' );
684 this.glyphicon = 'caret';
687 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
689 //gsRoo.log(this.parentType);
690 if (this.parentType === 'Navbar' && !this.parent().bar) {
691 Roo.log('changing to li?');
700 href : this.href || '#'
703 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
704 cfg.cls += ' dropdown';
711 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
713 if (this.glyphicon) {
714 cfg.html = ' ' + cfg.html;
719 cls: 'glyphicon glyphicon-' + this.glyphicon
729 // cfg.cls='btn roo-button';
733 var value = cfg.html;
738 cls: 'glyphicon glyphicon-' + this.glyphicon,
757 cfg.cls += ' dropdown';
758 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
761 if (cfg.tag !== 'a' && this.href !== '') {
762 throw "Tag must be a to set href.";
763 } else if (this.href.length > 0) {
764 cfg.href = this.href;
767 if(this.removeClass){
772 cfg.target = this.target;
777 initEvents: function() {
778 // Roo.log('init events?');
779 // Roo.log(this.el.dom);
782 if (typeof (this.menu) != 'undefined') {
783 this.menu.parentType = this.xtype;
784 this.menu.triggerEl = this.el;
785 this.addxtype(Roo.apply({}, this.menu));
789 if (this.el.hasClass('roo-button')) {
790 this.el.on('click', this.onClick, this);
792 this.el.select('.roo-button').on('click', this.onClick, this);
795 if(this.removeClass){
796 this.el.on('click', this.onClick, this);
799 this.el.enableDisplayMode();
802 onClick : function(e)
809 Roo.log('button on click ');
810 if(this.preventDefault){
813 if (this.pressed === true || this.pressed === false) {
814 this.pressed = !this.pressed;
815 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
816 this.fireEvent('toggle', this, e, this.pressed);
820 this.fireEvent('click', this, e);
824 * Enables this button
828 this.disabled = false;
829 this.el.removeClass('disabled');
833 * Disable this button
837 this.disabled = true;
838 this.el.addClass('disabled');
841 * sets the active state on/off,
842 * @param {Boolean} state (optional) Force a particular state
844 setActive : function(v) {
846 this.el[v ? 'addClass' : 'removeClass']('active');
849 * toggles the current active state
851 toggleActive : function()
853 var active = this.el.hasClass('active');
854 this.setActive(!active);
858 setText : function(str)
860 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
864 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
875 setWeight : function(str)
877 this.el.removeClass(this.weightClass);
878 this.el.addClass('btn-' + str);
892 * @class Roo.bootstrap.Column
893 * @extends Roo.bootstrap.Component
894 * Bootstrap Column class
895 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
896 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
897 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
898 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
899 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
900 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
901 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
902 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
905 * @cfg {Boolean} hidden (true|false) hide the element
906 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
907 * @cfg {String} fa (ban|check|...) font awesome icon
908 * @cfg {Number} fasize (1|2|....) font awsome size
910 * @cfg {String} icon (info-sign|check|...) glyphicon name
912 * @cfg {String} html content of column.
915 * Create a new Column
916 * @param {Object} config The config object
919 Roo.bootstrap.Column = function(config){
920 Roo.bootstrap.Column.superclass.constructor.call(this, config);
923 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
941 getAutoCreate : function(){
942 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
950 ['xs','sm','md','lg'].map(function(size){
951 //Roo.log( size + ':' + settings[size]);
953 if (settings[size+'off'] !== false) {
954 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
957 if (settings[size] === false) {
961 if (!settings[size]) { // 0 = hidden
962 cfg.cls += ' hidden-' + size;
965 cfg.cls += ' col-' + size + '-' + settings[size];
970 cfg.cls += ' hidden';
973 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
974 cfg.cls +=' alert alert-' + this.alert;
978 if (this.html.length) {
979 cfg.html = this.html;
983 if (this.fasize > 1) {
984 fasize = ' fa-' + this.fasize + 'x';
986 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
991 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1010 * @class Roo.bootstrap.Container
1011 * @extends Roo.bootstrap.Component
1012 * Bootstrap Container class
1013 * @cfg {Boolean} jumbotron is it a jumbotron element
1014 * @cfg {String} html content of element
1015 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1016 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1017 * @cfg {String} header content of header (for panel)
1018 * @cfg {String} footer content of footer (for panel)
1019 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1020 * @cfg {String} tag (header|aside|section) type of HTML tag.
1021 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1022 * @cfg {String} fa font awesome icon
1023 * @cfg {String} icon (info-sign|check|...) glyphicon name
1024 * @cfg {Boolean} hidden (true|false) hide the element
1025 * @cfg {Boolean} expandable (true|false) default false
1026 * @cfg {Boolean} expanded (true|false) default true
1027 * @cfg {String} rheader contet on the right of header
1028 * @cfg {Boolean} clickable (true|false) default false
1032 * Create a new Container
1033 * @param {Object} config The config object
1036 Roo.bootstrap.Container = function(config){
1037 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1043 * After the panel has been expand
1045 * @param {Roo.bootstrap.Container} this
1050 * After the panel has been collapsed
1052 * @param {Roo.bootstrap.Container} this
1057 * When a element is chick
1058 * @param {Roo.bootstrap.Container} this
1059 * @param {Roo.EventObject} e
1065 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1083 getChildContainer : function() {
1089 if (this.panel.length) {
1090 return this.el.select('.panel-body',true).first();
1097 getAutoCreate : function(){
1100 tag : this.tag || 'div',
1104 if (this.jumbotron) {
1105 cfg.cls = 'jumbotron';
1110 // - this is applied by the parent..
1112 // cfg.cls = this.cls + '';
1115 if (this.sticky.length) {
1117 var bd = Roo.get(document.body);
1118 if (!bd.hasClass('bootstrap-sticky')) {
1119 bd.addClass('bootstrap-sticky');
1120 Roo.select('html',true).setStyle('height', '100%');
1123 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1127 if (this.well.length) {
1128 switch (this.well) {
1131 cfg.cls +=' well well-' +this.well;
1140 cfg.cls += ' hidden';
1144 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1145 cfg.cls +=' alert alert-' + this.alert;
1150 if (this.panel.length) {
1151 cfg.cls += ' panel panel-' + this.panel;
1153 if (this.header.length) {
1157 if(this.expandable){
1159 cfg.cls = cfg.cls + ' expandable';
1163 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1171 cls : 'panel-title',
1172 html : (this.expandable ? ' ' : '') + this.header
1176 cls: 'panel-header-right',
1182 cls : 'panel-heading',
1183 style : this.expandable ? 'cursor: pointer' : '',
1191 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1196 if (this.footer.length) {
1198 cls : 'panel-footer',
1207 body.html = this.html || cfg.html;
1208 // prefix with the icons..
1210 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1213 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1218 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1219 cfg.cls = 'container';
1225 initEvents: function()
1227 if(this.expandable){
1228 var headerEl = this.headerEl();
1231 headerEl.on('click', this.onToggleClick, this);
1236 this.el.on('click', this.onClick, this);
1241 onToggleClick : function()
1243 var headerEl = this.headerEl();
1259 if(this.fireEvent('expand', this)) {
1261 this.expanded = true;
1263 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1265 this.el.select('.panel-body',true).first().removeClass('hide');
1267 var toggleEl = this.toggleEl();
1273 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1278 collapse : function()
1280 if(this.fireEvent('collapse', this)) {
1282 this.expanded = false;
1284 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1285 this.el.select('.panel-body',true).first().addClass('hide');
1287 var toggleEl = this.toggleEl();
1293 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1297 toggleEl : function()
1299 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1303 return this.el.select('.panel-heading .fa',true).first();
1306 headerEl : function()
1308 if(!this.el || !this.panel.length || !this.header.length){
1312 return this.el.select('.panel-heading',true).first()
1317 if(!this.el || !this.panel.length){
1321 return this.el.select('.panel-body',true).first()
1324 titleEl : function()
1326 if(!this.el || !this.panel.length || !this.header.length){
1330 return this.el.select('.panel-title',true).first();
1333 setTitle : function(v)
1335 var titleEl = this.titleEl();
1341 titleEl.dom.innerHTML = v;
1344 getTitle : function()
1347 var titleEl = this.titleEl();
1353 return titleEl.dom.innerHTML;
1356 setRightTitle : function(v)
1358 var t = this.el.select('.panel-header-right',true).first();
1364 t.dom.innerHTML = v;
1367 onClick : function(e)
1371 this.fireEvent('click', this, e);
1385 * @class Roo.bootstrap.Img
1386 * @extends Roo.bootstrap.Component
1387 * Bootstrap Img class
1388 * @cfg {Boolean} imgResponsive false | true
1389 * @cfg {String} border rounded | circle | thumbnail
1390 * @cfg {String} src image source
1391 * @cfg {String} alt image alternative text
1392 * @cfg {String} href a tag href
1393 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1394 * @cfg {String} xsUrl xs image source
1395 * @cfg {String} smUrl sm image source
1396 * @cfg {String} mdUrl md image source
1397 * @cfg {String} lgUrl lg image source
1400 * Create a new Input
1401 * @param {Object} config The config object
1404 Roo.bootstrap.Img = function(config){
1405 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1411 * The img click event for the img.
1412 * @param {Roo.EventObject} e
1418 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1420 imgResponsive: true,
1430 getAutoCreate : function()
1432 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1433 return this.createSingleImg();
1438 cls: 'roo-image-responsive-group',
1443 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1445 if(!_this[size + 'Url']){
1451 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1452 html: _this.html || cfg.html,
1453 src: _this[size + 'Url']
1456 img.cls += ' roo-image-responsive-' + size;
1458 var s = ['xs', 'sm', 'md', 'lg'];
1460 s.splice(s.indexOf(size), 1);
1462 Roo.each(s, function(ss){
1463 img.cls += ' hidden-' + ss;
1466 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1467 cfg.cls += ' img-' + _this.border;
1471 cfg.alt = _this.alt;
1484 a.target = _this.target;
1488 cfg.cn.push((_this.href) ? a : img);
1495 createSingleImg : function()
1499 cls: (this.imgResponsive) ? 'img-responsive' : '',
1501 src : 'about:blank' // just incase src get's set to undefined?!?
1504 cfg.html = this.html || cfg.html;
1506 cfg.src = this.src || cfg.src;
1508 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1509 cfg.cls += ' img-' + this.border;
1526 a.target = this.target;
1531 return (this.href) ? a : cfg;
1534 initEvents: function()
1537 this.el.on('click', this.onClick, this);
1542 onClick : function(e)
1544 Roo.log('img onclick');
1545 this.fireEvent('click', this, e);
1548 * Sets the url of the image - used to update it
1549 * @param {String} url the url of the image
1552 setSrc : function(url)
1556 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1557 this.el.dom.src = url;
1561 this.el.select('img', true).first().dom.src = url;
1577 * @class Roo.bootstrap.Link
1578 * @extends Roo.bootstrap.Component
1579 * Bootstrap Link Class
1580 * @cfg {String} alt image alternative text
1581 * @cfg {String} href a tag href
1582 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1583 * @cfg {String} html the content of the link.
1584 * @cfg {String} anchor name for the anchor link
1585 * @cfg {String} fa - favicon
1587 * @cfg {Boolean} preventDefault (true | false) default false
1591 * Create a new Input
1592 * @param {Object} config The config object
1595 Roo.bootstrap.Link = function(config){
1596 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1602 * The img click event for the img.
1603 * @param {Roo.EventObject} e
1609 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1613 preventDefault: false,
1619 getAutoCreate : function()
1621 var html = this.html || '';
1623 if (this.fa !== false) {
1624 html = '<i class="fa fa-' + this.fa + '"></i>';
1629 // anchor's do not require html/href...
1630 if (this.anchor === false) {
1632 cfg.href = this.href || '#';
1634 cfg.name = this.anchor;
1635 if (this.html !== false || this.fa !== false) {
1638 if (this.href !== false) {
1639 cfg.href = this.href;
1643 if(this.alt !== false){
1648 if(this.target !== false) {
1649 cfg.target = this.target;
1655 initEvents: function() {
1657 if(!this.href || this.preventDefault){
1658 this.el.on('click', this.onClick, this);
1662 onClick : function(e)
1664 if(this.preventDefault){
1667 //Roo.log('img onclick');
1668 this.fireEvent('click', this, e);
1681 * @class Roo.bootstrap.Header
1682 * @extends Roo.bootstrap.Component
1683 * Bootstrap Header class
1684 * @cfg {String} html content of header
1685 * @cfg {Number} level (1|2|3|4|5|6) default 1
1688 * Create a new Header
1689 * @param {Object} config The config object
1693 Roo.bootstrap.Header = function(config){
1694 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1697 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1705 getAutoCreate : function(){
1710 tag: 'h' + (1 *this.level),
1711 html: this.html || ''
1723 * Ext JS Library 1.1.1
1724 * Copyright(c) 2006-2007, Ext JS, LLC.
1726 * Originally Released Under LGPL - original licence link has changed is not relivant.
1729 * <script type="text/javascript">
1733 * @class Roo.bootstrap.MenuMgr
1734 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1737 Roo.bootstrap.MenuMgr = function(){
1738 var menus, active, groups = {}, attached = false, lastShow = new Date();
1740 // private - called when first menu is created
1743 active = new Roo.util.MixedCollection();
1744 Roo.get(document).addKeyListener(27, function(){
1745 if(active.length > 0){
1753 if(active && active.length > 0){
1754 var c = active.clone();
1764 if(active.length < 1){
1765 Roo.get(document).un("mouseup", onMouseDown);
1773 var last = active.last();
1774 lastShow = new Date();
1777 Roo.get(document).on("mouseup", onMouseDown);
1782 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1783 m.parentMenu.activeChild = m;
1784 }else if(last && last.isVisible()){
1785 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1790 function onBeforeHide(m){
1792 m.activeChild.hide();
1794 if(m.autoHideTimer){
1795 clearTimeout(m.autoHideTimer);
1796 delete m.autoHideTimer;
1801 function onBeforeShow(m){
1802 var pm = m.parentMenu;
1803 if(!pm && !m.allowOtherMenus){
1805 }else if(pm && pm.activeChild && active != m){
1806 pm.activeChild.hide();
1810 // private this should really trigger on mouseup..
1811 function onMouseDown(e){
1812 Roo.log("on Mouse Up");
1814 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1815 Roo.log("MenuManager hideAll");
1824 function onBeforeCheck(mi, state){
1826 var g = groups[mi.group];
1827 for(var i = 0, l = g.length; i < l; i++){
1829 g[i].setChecked(false);
1838 * Hides all menus that are currently visible
1840 hideAll : function(){
1845 register : function(menu){
1849 menus[menu.id] = menu;
1850 menu.on("beforehide", onBeforeHide);
1851 menu.on("hide", onHide);
1852 menu.on("beforeshow", onBeforeShow);
1853 menu.on("show", onShow);
1855 if(g && menu.events["checkchange"]){
1859 groups[g].push(menu);
1860 menu.on("checkchange", onCheck);
1865 * Returns a {@link Roo.menu.Menu} object
1866 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1867 * be used to generate and return a new Menu instance.
1869 get : function(menu){
1870 if(typeof menu == "string"){ // menu id
1872 }else if(menu.events){ // menu instance
1875 /*else if(typeof menu.length == 'number'){ // array of menu items?
1876 return new Roo.bootstrap.Menu({items:menu});
1877 }else{ // otherwise, must be a config
1878 return new Roo.bootstrap.Menu(menu);
1885 unregister : function(menu){
1886 delete menus[menu.id];
1887 menu.un("beforehide", onBeforeHide);
1888 menu.un("hide", onHide);
1889 menu.un("beforeshow", onBeforeShow);
1890 menu.un("show", onShow);
1892 if(g && menu.events["checkchange"]){
1893 groups[g].remove(menu);
1894 menu.un("checkchange", onCheck);
1899 registerCheckable : function(menuItem){
1900 var g = menuItem.group;
1905 groups[g].push(menuItem);
1906 menuItem.on("beforecheckchange", onBeforeCheck);
1911 unregisterCheckable : function(menuItem){
1912 var g = menuItem.group;
1914 groups[g].remove(menuItem);
1915 menuItem.un("beforecheckchange", onBeforeCheck);
1927 * @class Roo.bootstrap.Menu
1928 * @extends Roo.bootstrap.Component
1929 * Bootstrap Menu class - container for MenuItems
1930 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1931 * @cfg {bool} hidden if the menu should be hidden when rendered.
1932 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1933 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1937 * @param {Object} config The config object
1941 Roo.bootstrap.Menu = function(config){
1942 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1943 if (this.registerMenu && this.type != 'treeview') {
1944 Roo.bootstrap.MenuMgr.register(this);
1949 * Fires before this menu is displayed
1950 * @param {Roo.menu.Menu} this
1955 * Fires before this menu is hidden
1956 * @param {Roo.menu.Menu} this
1961 * Fires after this menu is displayed
1962 * @param {Roo.menu.Menu} this
1967 * Fires after this menu is hidden
1968 * @param {Roo.menu.Menu} this
1973 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1974 * @param {Roo.menu.Menu} this
1975 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1976 * @param {Roo.EventObject} e
1981 * Fires when the mouse is hovering over this menu
1982 * @param {Roo.menu.Menu} this
1983 * @param {Roo.EventObject} e
1984 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1989 * Fires when the mouse exits this menu
1990 * @param {Roo.menu.Menu} this
1991 * @param {Roo.EventObject} e
1992 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1997 * Fires when a menu item contained in this menu is clicked
1998 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1999 * @param {Roo.EventObject} e
2003 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2006 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2010 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2013 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2015 registerMenu : true,
2017 menuItems :false, // stores the menu items..
2027 getChildContainer : function() {
2031 getAutoCreate : function(){
2033 //if (['right'].indexOf(this.align)!==-1) {
2034 // cfg.cn[1].cls += ' pull-right'
2040 cls : 'dropdown-menu' ,
2041 style : 'z-index:1000'
2045 if (this.type === 'submenu') {
2046 cfg.cls = 'submenu active';
2048 if (this.type === 'treeview') {
2049 cfg.cls = 'treeview-menu';
2054 initEvents : function() {
2056 // Roo.log("ADD event");
2057 // Roo.log(this.triggerEl.dom);
2059 this.triggerEl.on('click', this.onTriggerClick, this);
2061 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2063 this.triggerEl.addClass('dropdown-toggle');
2066 this.el.on('touchstart' , this.onTouch, this);
2068 this.el.on('click' , this.onClick, this);
2070 this.el.on("mouseover", this.onMouseOver, this);
2071 this.el.on("mouseout", this.onMouseOut, this);
2075 findTargetItem : function(e)
2077 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2081 //Roo.log(t); Roo.log(t.id);
2083 //Roo.log(this.menuitems);
2084 return this.menuitems.get(t.id);
2086 //return this.items.get(t.menuItemId);
2092 onTouch : function(e)
2094 Roo.log("menu.onTouch");
2095 //e.stopEvent(); this make the user popdown broken
2099 onClick : function(e)
2101 Roo.log("menu.onClick");
2103 var t = this.findTargetItem(e);
2104 if(!t || t.isContainer){
2109 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2110 if(t == this.activeItem && t.shouldDeactivate(e)){
2111 this.activeItem.deactivate();
2112 delete this.activeItem;
2116 this.setActiveItem(t, true);
2124 Roo.log('pass click event');
2128 this.fireEvent("click", this, t, e);
2132 if(!t.href.length || t.href == '#'){
2133 (function() { _this.hide(); }).defer(100);
2138 onMouseOver : function(e){
2139 var t = this.findTargetItem(e);
2142 // if(t.canActivate && !t.disabled){
2143 // this.setActiveItem(t, true);
2147 this.fireEvent("mouseover", this, e, t);
2149 isVisible : function(){
2150 return !this.hidden;
2152 onMouseOut : function(e){
2153 var t = this.findTargetItem(e);
2156 // if(t == this.activeItem && t.shouldDeactivate(e)){
2157 // this.activeItem.deactivate();
2158 // delete this.activeItem;
2161 this.fireEvent("mouseout", this, e, t);
2166 * Displays this menu relative to another element
2167 * @param {String/HTMLElement/Roo.Element} element The element to align to
2168 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2169 * the element (defaults to this.defaultAlign)
2170 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2172 show : function(el, pos, parentMenu){
2173 this.parentMenu = parentMenu;
2177 this.fireEvent("beforeshow", this);
2178 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2181 * Displays this menu at a specific xy position
2182 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2183 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2185 showAt : function(xy, parentMenu, /* private: */_e){
2186 this.parentMenu = parentMenu;
2191 this.fireEvent("beforeshow", this);
2192 //xy = this.el.adjustForConstraints(xy);
2196 this.hideMenuItems();
2197 this.hidden = false;
2198 this.triggerEl.addClass('open');
2200 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2201 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2204 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2209 this.fireEvent("show", this);
2215 this.doFocus.defer(50, this);
2219 doFocus : function(){
2221 this.focusEl.focus();
2226 * Hides this menu and optionally all parent menus
2227 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2229 hide : function(deep)
2232 this.hideMenuItems();
2233 if(this.el && this.isVisible()){
2234 this.fireEvent("beforehide", this);
2235 if(this.activeItem){
2236 this.activeItem.deactivate();
2237 this.activeItem = null;
2239 this.triggerEl.removeClass('open');;
2241 this.fireEvent("hide", this);
2243 if(deep === true && this.parentMenu){
2244 this.parentMenu.hide(true);
2248 onTriggerClick : function(e)
2250 Roo.log('trigger click');
2252 var target = e.getTarget();
2254 Roo.log(target.nodeName.toLowerCase());
2256 if(target.nodeName.toLowerCase() === 'i'){
2262 onTriggerPress : function(e)
2264 Roo.log('trigger press');
2265 //Roo.log(e.getTarget());
2266 // Roo.log(this.triggerEl.dom);
2268 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2269 var pel = Roo.get(e.getTarget());
2270 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2271 Roo.log('is treeview or dropdown?');
2275 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2279 if (this.isVisible()) {
2284 this.show(this.triggerEl, false, false);
2287 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2294 hideMenuItems : function()
2296 Roo.log("hide Menu Items");
2300 //$(backdrop).remove()
2301 this.el.select('.open',true).each(function(aa) {
2303 aa.removeClass('open');
2304 //var parent = getParent($(this))
2305 //var relatedTarget = { relatedTarget: this }
2307 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2308 //if (e.isDefaultPrevented()) return
2309 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2312 addxtypeChild : function (tree, cntr) {
2313 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2315 this.menuitems.add(comp);
2336 * @class Roo.bootstrap.MenuItem
2337 * @extends Roo.bootstrap.Component
2338 * Bootstrap MenuItem class
2339 * @cfg {String} html the menu label
2340 * @cfg {String} href the link
2341 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2342 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2343 * @cfg {Boolean} active used on sidebars to highlight active itesm
2344 * @cfg {String} fa favicon to show on left of menu item.
2345 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2349 * Create a new MenuItem
2350 * @param {Object} config The config object
2354 Roo.bootstrap.MenuItem = function(config){
2355 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2360 * The raw click event for the entire grid.
2361 * @param {Roo.bootstrap.MenuItem} this
2362 * @param {Roo.EventObject} e
2368 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2372 preventDefault: false,
2373 isContainer : false,
2377 getAutoCreate : function(){
2379 if(this.isContainer){
2382 cls: 'dropdown-menu-item'
2396 if (this.fa !== false) {
2399 cls : 'fa fa-' + this.fa
2408 cls: 'dropdown-menu-item',
2411 if (this.parent().type == 'treeview') {
2412 cfg.cls = 'treeview-menu';
2415 cfg.cls += ' active';
2420 anc.href = this.href || cfg.cn[0].href ;
2421 ctag.html = this.html || cfg.cn[0].html ;
2425 initEvents: function()
2427 if (this.parent().type == 'treeview') {
2428 this.el.select('a').on('click', this.onClick, this);
2432 this.menu.parentType = this.xtype;
2433 this.menu.triggerEl = this.el;
2434 this.menu = this.addxtype(Roo.apply({}, this.menu));
2438 onClick : function(e)
2440 Roo.log('item on click ');
2442 if(this.preventDefault){
2445 //this.parent().hideMenuItems();
2447 this.fireEvent('click', this, e);
2466 * @class Roo.bootstrap.MenuSeparator
2467 * @extends Roo.bootstrap.Component
2468 * Bootstrap MenuSeparator class
2471 * Create a new MenuItem
2472 * @param {Object} config The config object
2476 Roo.bootstrap.MenuSeparator = function(config){
2477 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2480 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2482 getAutoCreate : function(){
2501 * @class Roo.bootstrap.Modal
2502 * @extends Roo.bootstrap.Component
2503 * Bootstrap Modal class
2504 * @cfg {String} title Title of dialog
2505 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2506 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2507 * @cfg {Boolean} specificTitle default false
2508 * @cfg {Array} buttons Array of buttons or standard button set..
2509 * @cfg {String} buttonPosition (left|right|center) default right
2510 * @cfg {Boolean} animate default true
2511 * @cfg {Boolean} allow_close default true
2512 * @cfg {Boolean} fitwindow default false
2513 * @cfg {String} size (sm|lg) default empty
2517 * Create a new Modal Dialog
2518 * @param {Object} config The config object
2521 Roo.bootstrap.Modal = function(config){
2522 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2527 * The raw btnclick event for the button
2528 * @param {Roo.EventObject} e
2533 * Fire when dialog resize
2534 * @param {Roo.bootstrap.Modal} this
2535 * @param {Roo.EventObject} e
2539 this.buttons = this.buttons || [];
2542 this.tmpl = Roo.factory(this.tmpl);
2547 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2549 title : 'test dialog',
2559 specificTitle: false,
2561 buttonPosition: 'right',
2580 onRender : function(ct, position)
2582 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2585 var cfg = Roo.apply({}, this.getAutoCreate());
2588 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2590 //if (!cfg.name.length) {
2594 cfg.cls += ' ' + this.cls;
2597 cfg.style = this.style;
2599 this.el = Roo.get(document.body).createChild(cfg, position);
2601 //var type = this.el.dom.type;
2604 if(this.tabIndex !== undefined){
2605 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2608 this.dialogEl = this.el.select('.modal-dialog',true).first();
2609 this.bodyEl = this.el.select('.modal-body',true).first();
2610 this.closeEl = this.el.select('.modal-header .close', true).first();
2611 this.headerEl = this.el.select('.modal-header',true).first();
2612 this.titleEl = this.el.select('.modal-title',true).first();
2613 this.footerEl = this.el.select('.modal-footer',true).first();
2615 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2616 this.maskEl.enableDisplayMode("block");
2618 //this.el.addClass("x-dlg-modal");
2620 if (this.buttons.length) {
2621 Roo.each(this.buttons, function(bb) {
2622 var b = Roo.apply({}, bb);
2623 b.xns = b.xns || Roo.bootstrap;
2624 b.xtype = b.xtype || 'Button';
2625 if (typeof(b.listeners) == 'undefined') {
2626 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2629 var btn = Roo.factory(b);
2631 btn.render(this.el.select('.modal-footer div').first());
2635 // render the children.
2638 if(typeof(this.items) != 'undefined'){
2639 var items = this.items;
2642 for(var i =0;i < items.length;i++) {
2643 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2647 this.items = nitems;
2649 // where are these used - they used to be body/close/footer
2653 //this.el.addClass([this.fieldClass, this.cls]);
2657 getAutoCreate : function(){
2662 html : this.html || ''
2667 cls : 'modal-title',
2671 if(this.specificTitle){
2677 if (this.allow_close) {
2689 if(this.size.length){
2690 size = 'modal-' + this.size;
2695 style : 'display: none',
2698 cls: "modal-dialog " + size,
2701 cls : "modal-content",
2704 cls : 'modal-header',
2709 cls : 'modal-footer',
2713 cls: 'btn-' + this.buttonPosition
2730 modal.cls += ' fade';
2736 getChildContainer : function() {
2741 getButtonContainer : function() {
2742 return this.el.select('.modal-footer div',true).first();
2745 initEvents : function()
2747 if (this.allow_close) {
2748 this.closeEl.on('click', this.hide, this);
2750 Roo.EventManager.onWindowResize(this.resize, this, true);
2757 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2758 if (this.fitwindow) {
2759 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2760 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2765 setSize : function(w,h)
2775 if (!this.rendered) {
2779 this.el.setStyle('display', 'block');
2781 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2784 this.el.addClass('in');
2787 this.el.addClass('in');
2791 // not sure how we can show data in here..
2793 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2796 Roo.get(document.body).addClass("x-body-masked");
2798 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2799 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2804 this.fireEvent('show', this);
2806 // set zindex here - otherwise it appears to be ignored...
2807 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2810 this.items.forEach( function(e) {
2811 e.layout ? e.layout() : false;
2819 if(this.fireEvent("beforehide", this) !== false){
2821 Roo.get(document.body).removeClass("x-body-masked");
2822 this.el.removeClass('in');
2823 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2825 if(this.animate){ // why
2827 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2829 this.el.setStyle('display', 'none');
2831 this.fireEvent('hide', this);
2835 addButton : function(str, cb)
2839 var b = Roo.apply({}, { html : str } );
2840 b.xns = b.xns || Roo.bootstrap;
2841 b.xtype = b.xtype || 'Button';
2842 if (typeof(b.listeners) == 'undefined') {
2843 b.listeners = { click : cb.createDelegate(this) };
2846 var btn = Roo.factory(b);
2848 btn.render(this.el.select('.modal-footer div').first());
2854 setDefaultButton : function(btn)
2856 //this.el.select('.modal-footer').()
2860 resizeTo: function(w,h)
2864 this.dialogEl.setWidth(w);
2865 if (this.diff === false) {
2866 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2869 this.bodyEl.setHeight(h-this.diff);
2871 this.fireEvent('resize', this);
2874 setContentSize : function(w, h)
2878 onButtonClick: function(btn,e)
2881 this.fireEvent('btnclick', btn.name, e);
2884 * Set the title of the Dialog
2885 * @param {String} str new Title
2887 setTitle: function(str) {
2888 this.titleEl.dom.innerHTML = str;
2891 * Set the body of the Dialog
2892 * @param {String} str new Title
2894 setBody: function(str) {
2895 this.bodyEl.dom.innerHTML = str;
2898 * Set the body of the Dialog using the template
2899 * @param {Obj} data - apply this data to the template and replace the body contents.
2901 applyBody: function(obj)
2904 Roo.log("Error - using apply Body without a template");
2907 this.tmpl.overwrite(this.bodyEl, obj);
2913 Roo.apply(Roo.bootstrap.Modal, {
2915 * Button config that displays a single OK button
2924 * Button config that displays Yes and No buttons
2940 * Button config that displays OK and Cancel buttons
2955 * Button config that displays Yes, No and Cancel buttons
2979 * messagebox - can be used as a replace
2983 * @class Roo.MessageBox
2984 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2988 Roo.Msg.alert('Status', 'Changes saved successfully.');
2990 // Prompt for user data:
2991 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2993 // process text value...
2997 // Show a dialog using config options:
2999 title:'Save Changes?',
3000 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3001 buttons: Roo.Msg.YESNOCANCEL,
3008 Roo.bootstrap.MessageBox = function(){
3009 var dlg, opt, mask, waitTimer;
3010 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3011 var buttons, activeTextEl, bwidth;
3015 var handleButton = function(button){
3017 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3021 var handleHide = function(){
3023 dlg.el.removeClass(opt.cls);
3026 // Roo.TaskMgr.stop(waitTimer);
3027 // waitTimer = null;
3032 var updateButtons = function(b){
3035 buttons["ok"].hide();
3036 buttons["cancel"].hide();
3037 buttons["yes"].hide();
3038 buttons["no"].hide();
3039 //dlg.footer.dom.style.display = 'none';
3042 dlg.footerEl.dom.style.display = '';
3043 for(var k in buttons){
3044 if(typeof buttons[k] != "function"){
3047 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3048 width += buttons[k].el.getWidth()+15;
3058 var handleEsc = function(d, k, e){
3059 if(opt && opt.closable !== false){
3069 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3070 * @return {Roo.BasicDialog} The BasicDialog element
3072 getDialog : function(){
3074 dlg = new Roo.bootstrap.Modal( {
3077 //constraintoviewport:false,
3079 //collapsible : false,
3084 //buttonAlign:"center",
3085 closeClick : function(){
3086 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3089 handleButton("cancel");
3094 dlg.on("hide", handleHide);
3096 //dlg.addKeyListener(27, handleEsc);
3098 this.buttons = buttons;
3099 var bt = this.buttonText;
3100 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3101 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3102 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3103 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3105 bodyEl = dlg.bodyEl.createChild({
3107 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3108 '<textarea class="roo-mb-textarea"></textarea>' +
3109 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3111 msgEl = bodyEl.dom.firstChild;
3112 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3113 textboxEl.enableDisplayMode();
3114 textboxEl.addKeyListener([10,13], function(){
3115 if(dlg.isVisible() && opt && opt.buttons){
3118 }else if(opt.buttons.yes){
3119 handleButton("yes");
3123 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3124 textareaEl.enableDisplayMode();
3125 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3126 progressEl.enableDisplayMode();
3128 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3129 var pf = progressEl.dom.firstChild;
3131 pp = Roo.get(pf.firstChild);
3132 pp.setHeight(pf.offsetHeight);
3140 * Updates the message box body text
3141 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3142 * the XHTML-compliant non-breaking space character '&#160;')
3143 * @return {Roo.MessageBox} This message box
3145 updateText : function(text)
3147 if(!dlg.isVisible() && !opt.width){
3148 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3149 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3151 msgEl.innerHTML = text || ' ';
3153 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3154 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3156 Math.min(opt.width || cw , this.maxWidth),
3157 Math.max(opt.minWidth || this.minWidth, bwidth)
3160 activeTextEl.setWidth(w);
3162 if(dlg.isVisible()){
3163 dlg.fixedcenter = false;
3165 // to big, make it scroll. = But as usual stupid IE does not support
3168 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3169 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3170 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3172 bodyEl.dom.style.height = '';
3173 bodyEl.dom.style.overflowY = '';
3176 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3178 bodyEl.dom.style.overflowX = '';
3181 dlg.setContentSize(w, bodyEl.getHeight());
3182 if(dlg.isVisible()){
3183 dlg.fixedcenter = true;
3189 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3190 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3191 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3192 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3193 * @return {Roo.MessageBox} This message box
3195 updateProgress : function(value, text){
3197 this.updateText(text);
3200 if (pp) { // weird bug on my firefox - for some reason this is not defined
3201 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3202 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3208 * Returns true if the message box is currently displayed
3209 * @return {Boolean} True if the message box is visible, else false
3211 isVisible : function(){
3212 return dlg && dlg.isVisible();
3216 * Hides the message box if it is displayed
3219 if(this.isVisible()){
3225 * Displays a new message box, or reinitializes an existing message box, based on the config options
3226 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3227 * The following config object properties are supported:
3229 Property Type Description
3230 ---------- --------------- ------------------------------------------------------------------------------------
3231 animEl String/Element An id or Element from which the message box should animate as it opens and
3232 closes (defaults to undefined)
3233 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3234 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3235 closable Boolean False to hide the top-right close button (defaults to true). Note that
3236 progress and wait dialogs will ignore this property and always hide the
3237 close button as they can only be closed programmatically.
3238 cls String A custom CSS class to apply to the message box element
3239 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3240 displayed (defaults to 75)
3241 fn Function A callback function to execute after closing the dialog. The arguments to the
3242 function will be btn (the name of the button that was clicked, if applicable,
3243 e.g. "ok"), and text (the value of the active text field, if applicable).
3244 Progress and wait dialogs will ignore this option since they do not respond to
3245 user actions and can only be closed programmatically, so any required function
3246 should be called by the same code after it closes the dialog.
3247 icon String A CSS class that provides a background image to be used as an icon for
3248 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3249 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3250 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3251 modal Boolean False to allow user interaction with the page while the message box is
3252 displayed (defaults to true)
3253 msg String A string that will replace the existing message box body text (defaults
3254 to the XHTML-compliant non-breaking space character ' ')
3255 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3256 progress Boolean True to display a progress bar (defaults to false)
3257 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3258 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3259 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3260 title String The title text
3261 value String The string value to set into the active textbox element if displayed
3262 wait Boolean True to display a progress bar (defaults to false)
3263 width Number The width of the dialog in pixels
3270 msg: 'Please enter your address:',
3272 buttons: Roo.MessageBox.OKCANCEL,
3275 animEl: 'addAddressBtn'
3278 * @param {Object} config Configuration options
3279 * @return {Roo.MessageBox} This message box
3281 show : function(options)
3284 // this causes nightmares if you show one dialog after another
3285 // especially on callbacks..
3287 if(this.isVisible()){
3290 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3291 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3292 Roo.log("New Dialog Message:" + options.msg )
3293 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3294 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3297 var d = this.getDialog();
3299 d.setTitle(opt.title || " ");
3300 d.closeEl.setDisplayed(opt.closable !== false);
3301 activeTextEl = textboxEl;
3302 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3307 textareaEl.setHeight(typeof opt.multiline == "number" ?
3308 opt.multiline : this.defaultTextHeight);
3309 activeTextEl = textareaEl;
3318 progressEl.setDisplayed(opt.progress === true);
3319 this.updateProgress(0);
3320 activeTextEl.dom.value = opt.value || "";
3322 dlg.setDefaultButton(activeTextEl);
3324 var bs = opt.buttons;
3328 }else if(bs && bs.yes){
3329 db = buttons["yes"];
3331 dlg.setDefaultButton(db);
3333 bwidth = updateButtons(opt.buttons);
3334 this.updateText(opt.msg);
3336 d.el.addClass(opt.cls);
3338 d.proxyDrag = opt.proxyDrag === true;
3339 d.modal = opt.modal !== false;
3340 d.mask = opt.modal !== false ? mask : false;
3342 // force it to the end of the z-index stack so it gets a cursor in FF
3343 document.body.appendChild(dlg.el.dom);
3344 d.animateTarget = null;
3345 d.show(options.animEl);
3351 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3352 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3353 * and closing the message box when the process is complete.
3354 * @param {String} title The title bar text
3355 * @param {String} msg The message box body text
3356 * @return {Roo.MessageBox} This message box
3358 progress : function(title, msg){
3365 minWidth: this.minProgressWidth,
3372 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3373 * If a callback function is passed it will be called after the user clicks the button, and the
3374 * id of the button that was clicked will be passed as the only parameter to the callback
3375 * (could also be the top-right close button).
3376 * @param {String} title The title bar text
3377 * @param {String} msg The message box body text
3378 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3379 * @param {Object} scope (optional) The scope of the callback function
3380 * @return {Roo.MessageBox} This message box
3382 alert : function(title, msg, fn, scope)
3397 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3398 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3399 * You are responsible for closing the message box when the process is complete.
3400 * @param {String} msg The message box body text
3401 * @param {String} title (optional) The title bar text
3402 * @return {Roo.MessageBox} This message box
3404 wait : function(msg, title){
3415 waitTimer = Roo.TaskMgr.start({
3417 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3425 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3426 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3427 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3428 * @param {String} title The title bar text
3429 * @param {String} msg The message box body text
3430 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3431 * @param {Object} scope (optional) The scope of the callback function
3432 * @return {Roo.MessageBox} This message box
3434 confirm : function(title, msg, fn, scope){
3438 buttons: this.YESNO,
3447 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3448 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3449 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3450 * (could also be the top-right close button) and the text that was entered will be passed as the two
3451 * parameters to the callback.
3452 * @param {String} title The title bar text
3453 * @param {String} msg The message box body text
3454 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3455 * @param {Object} scope (optional) The scope of the callback function
3456 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3457 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3458 * @return {Roo.MessageBox} This message box
3460 prompt : function(title, msg, fn, scope, multiline){
3464 buttons: this.OKCANCEL,
3469 multiline: multiline,
3476 * Button config that displays a single OK button
3481 * Button config that displays Yes and No buttons
3484 YESNO : {yes:true, no:true},
3486 * Button config that displays OK and Cancel buttons
3489 OKCANCEL : {ok:true, cancel:true},
3491 * Button config that displays Yes, No and Cancel buttons
3494 YESNOCANCEL : {yes:true, no:true, cancel:true},
3497 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3500 defaultTextHeight : 75,
3502 * The maximum width in pixels of the message box (defaults to 600)
3507 * The minimum width in pixels of the message box (defaults to 100)
3512 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3513 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3516 minProgressWidth : 250,
3518 * An object containing the default button text strings that can be overriden for localized language support.
3519 * Supported properties are: ok, cancel, yes and no.
3520 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3533 * Shorthand for {@link Roo.MessageBox}
3535 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3536 Roo.Msg = Roo.Msg || Roo.MessageBox;
3545 * @class Roo.bootstrap.Navbar
3546 * @extends Roo.bootstrap.Component
3547 * Bootstrap Navbar class
3550 * Create a new Navbar
3551 * @param {Object} config The config object
3555 Roo.bootstrap.Navbar = function(config){
3556 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3560 * @event beforetoggle
3561 * Fire before toggle the menu
3562 * @param {Roo.EventObject} e
3564 "beforetoggle" : true
3568 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3577 getAutoCreate : function(){
3580 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3584 initEvents :function ()
3586 //Roo.log(this.el.select('.navbar-toggle',true));
3587 this.el.select('.navbar-toggle',true).on('click', function() {
3588 if(this.fireEvent('beforetoggle', this) !== false){
3589 this.el.select('.navbar-collapse',true).toggleClass('in');
3599 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3601 var size = this.el.getSize();
3602 this.maskEl.setSize(size.width, size.height);
3603 this.maskEl.enableDisplayMode("block");
3612 getChildContainer : function()
3614 if (this.el.select('.collapse').getCount()) {
3615 return this.el.select('.collapse',true).first();
3648 * @class Roo.bootstrap.NavSimplebar
3649 * @extends Roo.bootstrap.Navbar
3650 * Bootstrap Sidebar class
3652 * @cfg {Boolean} inverse is inverted color
3654 * @cfg {String} type (nav | pills | tabs)
3655 * @cfg {Boolean} arrangement stacked | justified
3656 * @cfg {String} align (left | right) alignment
3658 * @cfg {Boolean} main (true|false) main nav bar? default false
3659 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3661 * @cfg {String} tag (header|footer|nav|div) default is nav
3667 * Create a new Sidebar
3668 * @param {Object} config The config object
3672 Roo.bootstrap.NavSimplebar = function(config){
3673 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3676 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3692 getAutoCreate : function(){
3696 tag : this.tag || 'div',
3709 this.type = this.type || 'nav';
3710 if (['tabs','pills'].indexOf(this.type)!==-1) {
3711 cfg.cn[0].cls += ' nav-' + this.type
3715 if (this.type!=='nav') {
3716 Roo.log('nav type must be nav/tabs/pills')
3718 cfg.cn[0].cls += ' navbar-nav'
3724 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3725 cfg.cn[0].cls += ' nav-' + this.arrangement;
3729 if (this.align === 'right') {
3730 cfg.cn[0].cls += ' navbar-right';
3734 cfg.cls += ' navbar-inverse';
3761 * @class Roo.bootstrap.NavHeaderbar
3762 * @extends Roo.bootstrap.NavSimplebar
3763 * Bootstrap Sidebar class
3765 * @cfg {String} brand what is brand
3766 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3767 * @cfg {String} brand_href href of the brand
3768 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3769 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3770 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3771 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3774 * Create a new Sidebar
3775 * @param {Object} config The config object
3779 Roo.bootstrap.NavHeaderbar = function(config){
3780 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3784 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3791 desktopCenter : false,
3794 getAutoCreate : function(){
3797 tag: this.nav || 'nav',
3804 if (this.desktopCenter) {
3805 cn.push({cls : 'container', cn : []});
3812 cls: 'navbar-header',
3817 cls: 'navbar-toggle',
3818 'data-toggle': 'collapse',
3823 html: 'Toggle navigation'
3845 cls: 'collapse navbar-collapse',
3849 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3851 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3852 cfg.cls += ' navbar-' + this.position;
3854 // tag can override this..
3856 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3859 if (this.brand !== '') {
3862 href: this.brand_href ? this.brand_href : '#',
3863 cls: 'navbar-brand',
3871 cfg.cls += ' main-nav';
3879 getHeaderChildContainer : function()
3881 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3882 return this.el.select('.navbar-header',true).first();
3885 return this.getChildContainer();
3889 initEvents : function()
3891 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3893 if (this.autohide) {
3898 Roo.get(document).on('scroll',function(e) {
3899 var ns = Roo.get(document).getScroll().top;
3900 var os = prevScroll;
3904 ft.removeClass('slideDown');
3905 ft.addClass('slideUp');
3908 ft.removeClass('slideUp');
3909 ft.addClass('slideDown');
3930 * @class Roo.bootstrap.NavSidebar
3931 * @extends Roo.bootstrap.Navbar
3932 * Bootstrap Sidebar class
3935 * Create a new Sidebar
3936 * @param {Object} config The config object
3940 Roo.bootstrap.NavSidebar = function(config){
3941 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3944 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3946 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3948 getAutoCreate : function(){
3953 cls: 'sidebar sidebar-nav'
3975 * @class Roo.bootstrap.NavGroup
3976 * @extends Roo.bootstrap.Component
3977 * Bootstrap NavGroup class
3978 * @cfg {String} align (left|right)
3979 * @cfg {Boolean} inverse
3980 * @cfg {String} type (nav|pills|tab) default nav
3981 * @cfg {String} navId - reference Id for navbar.
3985 * Create a new nav group
3986 * @param {Object} config The config object
3989 Roo.bootstrap.NavGroup = function(config){
3990 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3993 Roo.bootstrap.NavGroup.register(this);
3997 * Fires when the active item changes
3998 * @param {Roo.bootstrap.NavGroup} this
3999 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4000 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4007 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4018 getAutoCreate : function()
4020 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4027 if (['tabs','pills'].indexOf(this.type)!==-1) {
4028 cfg.cls += ' nav-' + this.type
4030 if (this.type!=='nav') {
4031 Roo.log('nav type must be nav/tabs/pills')
4033 cfg.cls += ' navbar-nav'
4036 if (this.parent() && this.parent().sidebar) {
4039 cls: 'dashboard-menu sidebar-menu'
4045 if (this.form === true) {
4051 if (this.align === 'right') {
4052 cfg.cls += ' navbar-right';
4054 cfg.cls += ' navbar-left';
4058 if (this.align === 'right') {
4059 cfg.cls += ' navbar-right';
4063 cfg.cls += ' navbar-inverse';
4071 * sets the active Navigation item
4072 * @param {Roo.bootstrap.NavItem} the new current navitem
4074 setActiveItem : function(item)
4077 Roo.each(this.navItems, function(v){
4082 v.setActive(false, true);
4089 item.setActive(true, true);
4090 this.fireEvent('changed', this, item, prev);
4095 * gets the active Navigation item
4096 * @return {Roo.bootstrap.NavItem} the current navitem
4098 getActive : function()
4102 Roo.each(this.navItems, function(v){
4113 indexOfNav : function()
4117 Roo.each(this.navItems, function(v,i){
4128 * adds a Navigation item
4129 * @param {Roo.bootstrap.NavItem} the navitem to add
4131 addItem : function(cfg)
4133 var cn = new Roo.bootstrap.NavItem(cfg);
4135 cn.parentId = this.id;
4136 cn.onRender(this.el, null);
4140 * register a Navigation item
4141 * @param {Roo.bootstrap.NavItem} the navitem to add
4143 register : function(item)
4145 this.navItems.push( item);
4146 item.navId = this.navId;
4151 * clear all the Navigation item
4154 clearAll : function()
4157 this.el.dom.innerHTML = '';
4160 getNavItem: function(tabId)
4163 Roo.each(this.navItems, function(e) {
4164 if (e.tabId == tabId) {
4174 setActiveNext : function()
4176 var i = this.indexOfNav(this.getActive());
4177 if (i > this.navItems.length) {
4180 this.setActiveItem(this.navItems[i+1]);
4182 setActivePrev : function()
4184 var i = this.indexOfNav(this.getActive());
4188 this.setActiveItem(this.navItems[i-1]);
4190 clearWasActive : function(except) {
4191 Roo.each(this.navItems, function(e) {
4192 if (e.tabId != except.tabId && e.was_active) {
4193 e.was_active = false;
4200 getWasActive : function ()
4203 Roo.each(this.navItems, function(e) {
4218 Roo.apply(Roo.bootstrap.NavGroup, {
4222 * register a Navigation Group
4223 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4225 register : function(navgrp)
4227 this.groups[navgrp.navId] = navgrp;
4231 * fetch a Navigation Group based on the navigation ID
4232 * @param {string} the navgroup to add
4233 * @returns {Roo.bootstrap.NavGroup} the navgroup
4235 get: function(navId) {
4236 if (typeof(this.groups[navId]) == 'undefined') {
4238 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4240 return this.groups[navId] ;
4255 * @class Roo.bootstrap.NavItem
4256 * @extends Roo.bootstrap.Component
4257 * Bootstrap Navbar.NavItem class
4258 * @cfg {String} href link to
4259 * @cfg {String} html content of button
4260 * @cfg {String} badge text inside badge
4261 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4262 * @cfg {String} glyphicon name of glyphicon
4263 * @cfg {String} icon name of font awesome icon
4264 * @cfg {Boolean} active Is item active
4265 * @cfg {Boolean} disabled Is item disabled
4267 * @cfg {Boolean} preventDefault (true | false) default false
4268 * @cfg {String} tabId the tab that this item activates.
4269 * @cfg {String} tagtype (a|span) render as a href or span?
4270 * @cfg {Boolean} animateRef (true|false) link to element default false
4273 * Create a new Navbar Item
4274 * @param {Object} config The config object
4276 Roo.bootstrap.NavItem = function(config){
4277 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4282 * The raw click event for the entire grid.
4283 * @param {Roo.EventObject} e
4288 * Fires when the active item active state changes
4289 * @param {Roo.bootstrap.NavItem} this
4290 * @param {boolean} state the new state
4296 * Fires when scroll to element
4297 * @param {Roo.bootstrap.NavItem} this
4298 * @param {Object} options
4299 * @param {Roo.EventObject} e
4307 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4315 preventDefault : false,
4322 getAutoCreate : function(){
4331 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4333 if (this.disabled) {
4334 cfg.cls += ' disabled';
4337 if (this.href || this.html || this.glyphicon || this.icon) {
4341 href : this.href || "#",
4342 html: this.html || ''
4347 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4350 if(this.glyphicon) {
4351 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4356 cfg.cn[0].html += " <span class='caret'></span>";
4360 if (this.badge !== '') {
4362 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4370 initEvents: function()
4372 if (typeof (this.menu) != 'undefined') {
4373 this.menu.parentType = this.xtype;
4374 this.menu.triggerEl = this.el;
4375 this.menu = this.addxtype(Roo.apply({}, this.menu));
4378 this.el.select('a',true).on('click', this.onClick, this);
4380 if(this.tagtype == 'span'){
4381 this.el.select('span',true).on('click', this.onClick, this);
4384 // at this point parent should be available..
4385 this.parent().register(this);
4388 onClick : function(e)
4390 if (e.getTarget('.dropdown-menu-item')) {
4391 // did you click on a menu itemm.... - then don't trigger onclick..
4396 this.preventDefault ||
4399 Roo.log("NavItem - prevent Default?");
4403 if (this.disabled) {
4407 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4408 if (tg && tg.transition) {
4409 Roo.log("waiting for the transitionend");
4415 //Roo.log("fire event clicked");
4416 if(this.fireEvent('click', this, e) === false){
4420 if(this.tagtype == 'span'){
4424 //Roo.log(this.href);
4425 var ael = this.el.select('a',true).first();
4428 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4429 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4430 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4431 return; // ignore... - it's a 'hash' to another page.
4433 Roo.log("NavItem - prevent Default?");
4435 this.scrollToElement(e);
4439 var p = this.parent();
4441 if (['tabs','pills'].indexOf(p.type)!==-1) {
4442 if (typeof(p.setActiveItem) !== 'undefined') {
4443 p.setActiveItem(this);
4447 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4448 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4449 // remove the collapsed menu expand...
4450 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4454 isActive: function () {
4457 setActive : function(state, fire, is_was_active)
4459 if (this.active && !state && this.navId) {
4460 this.was_active = true;
4461 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4463 nv.clearWasActive(this);
4467 this.active = state;
4470 this.el.removeClass('active');
4471 } else if (!this.el.hasClass('active')) {
4472 this.el.addClass('active');
4475 this.fireEvent('changed', this, state);
4478 // show a panel if it's registered and related..
4480 if (!this.navId || !this.tabId || !state || is_was_active) {
4484 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4488 var pan = tg.getPanelByName(this.tabId);
4492 // if we can not flip to new panel - go back to old nav highlight..
4493 if (false == tg.showPanel(pan)) {
4494 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4496 var onav = nv.getWasActive();
4498 onav.setActive(true, false, true);
4507 // this should not be here...
4508 setDisabled : function(state)
4510 this.disabled = state;
4512 this.el.removeClass('disabled');
4513 } else if (!this.el.hasClass('disabled')) {
4514 this.el.addClass('disabled');
4520 * Fetch the element to display the tooltip on.
4521 * @return {Roo.Element} defaults to this.el
4523 tooltipEl : function()
4525 return this.el.select('' + this.tagtype + '', true).first();
4528 scrollToElement : function(e)
4530 var c = document.body;
4533 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4535 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4536 c = document.documentElement;
4539 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4545 var o = target.calcOffsetsTo(c);
4552 this.fireEvent('scrollto', this, options, e);
4554 Roo.get(c).scrollTo('top', options.value, true);
4567 * <span> icon </span>
4568 * <span> text </span>
4569 * <span>badge </span>
4573 * @class Roo.bootstrap.NavSidebarItem
4574 * @extends Roo.bootstrap.NavItem
4575 * Bootstrap Navbar.NavSidebarItem class
4576 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4577 * {Boolean} open is the menu open
4578 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4579 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4580 * {String} buttonSize (sm|md|lg)the extra classes for the button
4581 * {Boolean} showArrow show arrow next to the text (default true)
4583 * Create a new Navbar Button
4584 * @param {Object} config The config object
4586 Roo.bootstrap.NavSidebarItem = function(config){
4587 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4592 * The raw click event for the entire grid.
4593 * @param {Roo.EventObject} e
4598 * Fires when the active item active state changes
4599 * @param {Roo.bootstrap.NavSidebarItem} this
4600 * @param {boolean} state the new state
4608 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4610 badgeWeight : 'default',
4616 buttonWeight : 'default',
4622 getAutoCreate : function(){
4627 href : this.href || '#',
4633 if(this.buttonView){
4636 href : this.href || '#',
4637 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4650 cfg.cls += ' active';
4653 if (this.disabled) {
4654 cfg.cls += ' disabled';
4657 cfg.cls += ' open x-open';
4660 if (this.glyphicon || this.icon) {
4661 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4662 a.cn.push({ tag : 'i', cls : c }) ;
4665 if(!this.buttonView){
4668 html : this.html || ''
4675 if (this.badge !== '') {
4676 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4682 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4685 a.cls += ' dropdown-toggle treeview' ;
4691 initEvents : function()
4693 if (typeof (this.menu) != 'undefined') {
4694 this.menu.parentType = this.xtype;
4695 this.menu.triggerEl = this.el;
4696 this.menu = this.addxtype(Roo.apply({}, this.menu));
4699 this.el.on('click', this.onClick, this);
4701 if(this.badge !== ''){
4702 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4707 onClick : function(e)
4714 if(this.preventDefault){
4718 this.fireEvent('click', this);
4721 disable : function()
4723 this.setDisabled(true);
4728 this.setDisabled(false);
4731 setDisabled : function(state)
4733 if(this.disabled == state){
4737 this.disabled = state;
4740 this.el.addClass('disabled');
4744 this.el.removeClass('disabled');
4749 setActive : function(state)
4751 if(this.active == state){
4755 this.active = state;
4758 this.el.addClass('active');
4762 this.el.removeClass('active');
4767 isActive: function ()
4772 setBadge : function(str)
4778 this.badgeEl.dom.innerHTML = str;
4795 * @class Roo.bootstrap.Row
4796 * @extends Roo.bootstrap.Component
4797 * Bootstrap Row class (contains columns...)
4801 * @param {Object} config The config object
4804 Roo.bootstrap.Row = function(config){
4805 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4808 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4810 getAutoCreate : function(){
4829 * @class Roo.bootstrap.Element
4830 * @extends Roo.bootstrap.Component
4831 * Bootstrap Element class
4832 * @cfg {String} html contents of the element
4833 * @cfg {String} tag tag of the element
4834 * @cfg {String} cls class of the element
4835 * @cfg {Boolean} preventDefault (true|false) default false
4836 * @cfg {Boolean} clickable (true|false) default false
4839 * Create a new Element
4840 * @param {Object} config The config object
4843 Roo.bootstrap.Element = function(config){
4844 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4850 * When a element is chick
4851 * @param {Roo.bootstrap.Element} this
4852 * @param {Roo.EventObject} e
4858 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4863 preventDefault: false,
4866 getAutoCreate : function(){
4877 initEvents: function()
4879 Roo.bootstrap.Element.superclass.initEvents.call(this);
4882 this.el.on('click', this.onClick, this);
4887 onClick : function(e)
4889 if(this.preventDefault){
4893 this.fireEvent('click', this, e);
4896 getValue : function()
4898 return this.el.dom.innerHTML;
4901 setValue : function(value)
4903 this.el.dom.innerHTML = value;
4918 * @class Roo.bootstrap.Pagination
4919 * @extends Roo.bootstrap.Component
4920 * Bootstrap Pagination class
4921 * @cfg {String} size xs | sm | md | lg
4922 * @cfg {Boolean} inverse false | true
4925 * Create a new Pagination
4926 * @param {Object} config The config object
4929 Roo.bootstrap.Pagination = function(config){
4930 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4933 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4939 getAutoCreate : function(){
4945 cfg.cls += ' inverse';
4951 cfg.cls += " " + this.cls;
4969 * @class Roo.bootstrap.PaginationItem
4970 * @extends Roo.bootstrap.Component
4971 * Bootstrap PaginationItem class
4972 * @cfg {String} html text
4973 * @cfg {String} href the link
4974 * @cfg {Boolean} preventDefault (true | false) default true
4975 * @cfg {Boolean} active (true | false) default false
4976 * @cfg {Boolean} disabled default false
4980 * Create a new PaginationItem
4981 * @param {Object} config The config object
4985 Roo.bootstrap.PaginationItem = function(config){
4986 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4991 * The raw click event for the entire grid.
4992 * @param {Roo.EventObject} e
4998 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5002 preventDefault: true,
5007 getAutoCreate : function(){
5013 href : this.href ? this.href : '#',
5014 html : this.html ? this.html : ''
5024 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5028 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5034 initEvents: function() {
5036 this.el.on('click', this.onClick, this);
5039 onClick : function(e)
5041 Roo.log('PaginationItem on click ');
5042 if(this.preventDefault){
5050 this.fireEvent('click', this, e);
5066 * @class Roo.bootstrap.Slider
5067 * @extends Roo.bootstrap.Component
5068 * Bootstrap Slider class
5071 * Create a new Slider
5072 * @param {Object} config The config object
5075 Roo.bootstrap.Slider = function(config){
5076 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5079 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5081 getAutoCreate : function(){
5085 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5089 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5101 * Ext JS Library 1.1.1
5102 * Copyright(c) 2006-2007, Ext JS, LLC.
5104 * Originally Released Under LGPL - original licence link has changed is not relivant.
5107 * <script type="text/javascript">
5112 * @class Roo.grid.ColumnModel
5113 * @extends Roo.util.Observable
5114 * This is the default implementation of a ColumnModel used by the Grid. It defines
5115 * the columns in the grid.
5118 var colModel = new Roo.grid.ColumnModel([
5119 {header: "Ticker", width: 60, sortable: true, locked: true},
5120 {header: "Company Name", width: 150, sortable: true},
5121 {header: "Market Cap.", width: 100, sortable: true},
5122 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5123 {header: "Employees", width: 100, sortable: true, resizable: false}
5128 * The config options listed for this class are options which may appear in each
5129 * individual column definition.
5130 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5132 * @param {Object} config An Array of column config objects. See this class's
5133 * config objects for details.
5135 Roo.grid.ColumnModel = function(config){
5137 * The config passed into the constructor
5139 this.config = config;
5142 // if no id, create one
5143 // if the column does not have a dataIndex mapping,
5144 // map it to the order it is in the config
5145 for(var i = 0, len = config.length; i < len; i++){
5147 if(typeof c.dataIndex == "undefined"){
5150 if(typeof c.renderer == "string"){
5151 c.renderer = Roo.util.Format[c.renderer];
5153 if(typeof c.id == "undefined"){
5156 if(c.editor && c.editor.xtype){
5157 c.editor = Roo.factory(c.editor, Roo.grid);
5159 if(c.editor && c.editor.isFormField){
5160 c.editor = new Roo.grid.GridEditor(c.editor);
5162 this.lookup[c.id] = c;
5166 * The width of columns which have no width specified (defaults to 100)
5169 this.defaultWidth = 100;
5172 * Default sortable of columns which have no sortable specified (defaults to false)
5175 this.defaultSortable = false;
5179 * @event widthchange
5180 * Fires when the width of a column changes.
5181 * @param {ColumnModel} this
5182 * @param {Number} columnIndex The column index
5183 * @param {Number} newWidth The new width
5185 "widthchange": true,
5187 * @event headerchange
5188 * Fires when the text of a header changes.
5189 * @param {ColumnModel} this
5190 * @param {Number} columnIndex The column index
5191 * @param {Number} newText The new header text
5193 "headerchange": true,
5195 * @event hiddenchange
5196 * Fires when a column is hidden or "unhidden".
5197 * @param {ColumnModel} this
5198 * @param {Number} columnIndex The column index
5199 * @param {Boolean} hidden true if hidden, false otherwise
5201 "hiddenchange": true,
5203 * @event columnmoved
5204 * Fires when a column is moved.
5205 * @param {ColumnModel} this
5206 * @param {Number} oldIndex
5207 * @param {Number} newIndex
5209 "columnmoved" : true,
5211 * @event columlockchange
5212 * Fires when a column's locked state is changed
5213 * @param {ColumnModel} this
5214 * @param {Number} colIndex
5215 * @param {Boolean} locked true if locked
5217 "columnlockchange" : true
5219 Roo.grid.ColumnModel.superclass.constructor.call(this);
5221 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5223 * @cfg {String} header The header text to display in the Grid view.
5226 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5227 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5228 * specified, the column's index is used as an index into the Record's data Array.
5231 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5232 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5235 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5236 * Defaults to the value of the {@link #defaultSortable} property.
5237 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5240 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5243 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5246 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5249 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5252 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5253 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5254 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5255 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5258 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5261 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5264 * @cfg {String} cursor (Optional)
5267 * @cfg {String} tooltip (Optional)
5270 * @cfg {Number} xs (Optional)
5273 * @cfg {Number} sm (Optional)
5276 * @cfg {Number} md (Optional)
5279 * @cfg {Number} lg (Optional)
5282 * Returns the id of the column at the specified index.
5283 * @param {Number} index The column index
5284 * @return {String} the id
5286 getColumnId : function(index){
5287 return this.config[index].id;
5291 * Returns the column for a specified id.
5292 * @param {String} id The column id
5293 * @return {Object} the column
5295 getColumnById : function(id){
5296 return this.lookup[id];
5301 * Returns the column for a specified dataIndex.
5302 * @param {String} dataIndex The column dataIndex
5303 * @return {Object|Boolean} the column or false if not found
5305 getColumnByDataIndex: function(dataIndex){
5306 var index = this.findColumnIndex(dataIndex);
5307 return index > -1 ? this.config[index] : false;
5311 * Returns the index for a specified column id.
5312 * @param {String} id The column id
5313 * @return {Number} the index, or -1 if not found
5315 getIndexById : function(id){
5316 for(var i = 0, len = this.config.length; i < len; i++){
5317 if(this.config[i].id == id){
5325 * Returns the index for a specified column dataIndex.
5326 * @param {String} dataIndex The column dataIndex
5327 * @return {Number} the index, or -1 if not found
5330 findColumnIndex : function(dataIndex){
5331 for(var i = 0, len = this.config.length; i < len; i++){
5332 if(this.config[i].dataIndex == dataIndex){
5340 moveColumn : function(oldIndex, newIndex){
5341 var c = this.config[oldIndex];
5342 this.config.splice(oldIndex, 1);
5343 this.config.splice(newIndex, 0, c);
5344 this.dataMap = null;
5345 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5348 isLocked : function(colIndex){
5349 return this.config[colIndex].locked === true;
5352 setLocked : function(colIndex, value, suppressEvent){
5353 if(this.isLocked(colIndex) == value){
5356 this.config[colIndex].locked = value;
5358 this.fireEvent("columnlockchange", this, colIndex, value);
5362 getTotalLockedWidth : function(){
5364 for(var i = 0; i < this.config.length; i++){
5365 if(this.isLocked(i) && !this.isHidden(i)){
5366 this.totalWidth += this.getColumnWidth(i);
5372 getLockedCount : function(){
5373 for(var i = 0, len = this.config.length; i < len; i++){
5374 if(!this.isLocked(i)){
5379 return this.config.length;
5383 * Returns the number of columns.
5386 getColumnCount : function(visibleOnly){
5387 if(visibleOnly === true){
5389 for(var i = 0, len = this.config.length; i < len; i++){
5390 if(!this.isHidden(i)){
5396 return this.config.length;
5400 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5401 * @param {Function} fn
5402 * @param {Object} scope (optional)
5403 * @return {Array} result
5405 getColumnsBy : function(fn, scope){
5407 for(var i = 0, len = this.config.length; i < len; i++){
5408 var c = this.config[i];
5409 if(fn.call(scope||this, c, i) === true){
5417 * Returns true if the specified column is sortable.
5418 * @param {Number} col The column index
5421 isSortable : function(col){
5422 if(typeof this.config[col].sortable == "undefined"){
5423 return this.defaultSortable;
5425 return this.config[col].sortable;
5429 * Returns the rendering (formatting) function defined for the column.
5430 * @param {Number} col The column index.
5431 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5433 getRenderer : function(col){
5434 if(!this.config[col].renderer){
5435 return Roo.grid.ColumnModel.defaultRenderer;
5437 return this.config[col].renderer;
5441 * Sets the rendering (formatting) function for a column.
5442 * @param {Number} col The column index
5443 * @param {Function} fn The function to use to process the cell's raw data
5444 * to return HTML markup for the grid view. The render function is called with
5445 * the following parameters:<ul>
5446 * <li>Data value.</li>
5447 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5448 * <li>css A CSS style string to apply to the table cell.</li>
5449 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5450 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5451 * <li>Row index</li>
5452 * <li>Column index</li>
5453 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5455 setRenderer : function(col, fn){
5456 this.config[col].renderer = fn;
5460 * Returns the width for the specified column.
5461 * @param {Number} col The column index
5464 getColumnWidth : function(col){
5465 return this.config[col].width * 1 || this.defaultWidth;
5469 * Sets the width for a column.
5470 * @param {Number} col The column index
5471 * @param {Number} width The new width
5473 setColumnWidth : function(col, width, suppressEvent){
5474 this.config[col].width = width;
5475 this.totalWidth = null;
5477 this.fireEvent("widthchange", this, col, width);
5482 * Returns the total width of all columns.
5483 * @param {Boolean} includeHidden True to include hidden column widths
5486 getTotalWidth : function(includeHidden){
5487 if(!this.totalWidth){
5488 this.totalWidth = 0;
5489 for(var i = 0, len = this.config.length; i < len; i++){
5490 if(includeHidden || !this.isHidden(i)){
5491 this.totalWidth += this.getColumnWidth(i);
5495 return this.totalWidth;
5499 * Returns the header for the specified column.
5500 * @param {Number} col The column index
5503 getColumnHeader : function(col){
5504 return this.config[col].header;
5508 * Sets the header for a column.
5509 * @param {Number} col The column index
5510 * @param {String} header The new header
5512 setColumnHeader : function(col, header){
5513 this.config[col].header = header;
5514 this.fireEvent("headerchange", this, col, header);
5518 * Returns the tooltip for the specified column.
5519 * @param {Number} col The column index
5522 getColumnTooltip : function(col){
5523 return this.config[col].tooltip;
5526 * Sets the tooltip for a column.
5527 * @param {Number} col The column index
5528 * @param {String} tooltip The new tooltip
5530 setColumnTooltip : function(col, tooltip){
5531 this.config[col].tooltip = tooltip;
5535 * Returns the dataIndex for the specified column.
5536 * @param {Number} col The column index
5539 getDataIndex : function(col){
5540 return this.config[col].dataIndex;
5544 * Sets the dataIndex for a column.
5545 * @param {Number} col The column index
5546 * @param {Number} dataIndex The new dataIndex
5548 setDataIndex : function(col, dataIndex){
5549 this.config[col].dataIndex = dataIndex;
5555 * Returns true if the cell is editable.
5556 * @param {Number} colIndex The column index
5557 * @param {Number} rowIndex The row index - this is nto actually used..?
5560 isCellEditable : function(colIndex, rowIndex){
5561 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5565 * Returns the editor defined for the cell/column.
5566 * return false or null to disable editing.
5567 * @param {Number} colIndex The column index
5568 * @param {Number} rowIndex The row index
5571 getCellEditor : function(colIndex, rowIndex){
5572 return this.config[colIndex].editor;
5576 * Sets if a column is editable.
5577 * @param {Number} col The column index
5578 * @param {Boolean} editable True if the column is editable
5580 setEditable : function(col, editable){
5581 this.config[col].editable = editable;
5586 * Returns true if the column is hidden.
5587 * @param {Number} colIndex The column index
5590 isHidden : function(colIndex){
5591 return this.config[colIndex].hidden;
5596 * Returns true if the column width cannot be changed
5598 isFixed : function(colIndex){
5599 return this.config[colIndex].fixed;
5603 * Returns true if the column can be resized
5606 isResizable : function(colIndex){
5607 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5610 * Sets if a column is hidden.
5611 * @param {Number} colIndex The column index
5612 * @param {Boolean} hidden True if the column is hidden
5614 setHidden : function(colIndex, hidden){
5615 this.config[colIndex].hidden = hidden;
5616 this.totalWidth = null;
5617 this.fireEvent("hiddenchange", this, colIndex, hidden);
5621 * Sets the editor for a column.
5622 * @param {Number} col The column index
5623 * @param {Object} editor The editor object
5625 setEditor : function(col, editor){
5626 this.config[col].editor = editor;
5630 Roo.grid.ColumnModel.defaultRenderer = function(value)
5632 if(typeof value == "object") {
5635 if(typeof value == "string" && value.length < 1){
5639 return String.format("{0}", value);
5642 // Alias for backwards compatibility
5643 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5646 * Ext JS Library 1.1.1
5647 * Copyright(c) 2006-2007, Ext JS, LLC.
5649 * Originally Released Under LGPL - original licence link has changed is not relivant.
5652 * <script type="text/javascript">
5656 * @class Roo.LoadMask
5657 * A simple utility class for generically masking elements while loading data. If the element being masked has
5658 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5659 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5660 * element's UpdateManager load indicator and will be destroyed after the initial load.
5662 * Create a new LoadMask
5663 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5664 * @param {Object} config The config object
5666 Roo.LoadMask = function(el, config){
5667 this.el = Roo.get(el);
5668 Roo.apply(this, config);
5670 this.store.on('beforeload', this.onBeforeLoad, this);
5671 this.store.on('load', this.onLoad, this);
5672 this.store.on('loadexception', this.onLoadException, this);
5673 this.removeMask = false;
5675 var um = this.el.getUpdateManager();
5676 um.showLoadIndicator = false; // disable the default indicator
5677 um.on('beforeupdate', this.onBeforeLoad, this);
5678 um.on('update', this.onLoad, this);
5679 um.on('failure', this.onLoad, this);
5680 this.removeMask = true;
5684 Roo.LoadMask.prototype = {
5686 * @cfg {Boolean} removeMask
5687 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5688 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5692 * The text to display in a centered loading message box (defaults to 'Loading...')
5696 * @cfg {String} msgCls
5697 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5699 msgCls : 'x-mask-loading',
5702 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5708 * Disables the mask to prevent it from being displayed
5710 disable : function(){
5711 this.disabled = true;
5715 * Enables the mask so that it can be displayed
5717 enable : function(){
5718 this.disabled = false;
5721 onLoadException : function()
5725 if (typeof(arguments[3]) != 'undefined') {
5726 Roo.MessageBox.alert("Error loading",arguments[3]);
5730 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5731 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5738 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5743 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5747 onBeforeLoad : function(){
5749 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5754 destroy : function(){
5756 this.store.un('beforeload', this.onBeforeLoad, this);
5757 this.store.un('load', this.onLoad, this);
5758 this.store.un('loadexception', this.onLoadException, this);
5760 var um = this.el.getUpdateManager();
5761 um.un('beforeupdate', this.onBeforeLoad, this);
5762 um.un('update', this.onLoad, this);
5763 um.un('failure', this.onLoad, this);
5774 * @class Roo.bootstrap.Table
5775 * @extends Roo.bootstrap.Component
5776 * Bootstrap Table class
5777 * @cfg {String} cls table class
5778 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5779 * @cfg {String} bgcolor Specifies the background color for a table
5780 * @cfg {Number} border Specifies whether the table cells should have borders or not
5781 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5782 * @cfg {Number} cellspacing Specifies the space between cells
5783 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5784 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5785 * @cfg {String} sortable Specifies that the table should be sortable
5786 * @cfg {String} summary Specifies a summary of the content of a table
5787 * @cfg {Number} width Specifies the width of a table
5788 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5790 * @cfg {boolean} striped Should the rows be alternative striped
5791 * @cfg {boolean} bordered Add borders to the table
5792 * @cfg {boolean} hover Add hover highlighting
5793 * @cfg {boolean} condensed Format condensed
5794 * @cfg {boolean} responsive Format condensed
5795 * @cfg {Boolean} loadMask (true|false) default false
5796 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5797 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5798 * @cfg {Boolean} rowSelection (true|false) default false
5799 * @cfg {Boolean} cellSelection (true|false) default false
5800 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5801 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5802 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5806 * Create a new Table
5807 * @param {Object} config The config object
5810 Roo.bootstrap.Table = function(config){
5811 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5816 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5817 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5818 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5819 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5821 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5823 this.sm.grid = this;
5824 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5825 this.sm = this.selModel;
5826 this.sm.xmodule = this.xmodule || false;
5829 if (this.cm && typeof(this.cm.config) == 'undefined') {
5830 this.colModel = new Roo.grid.ColumnModel(this.cm);
5831 this.cm = this.colModel;
5832 this.cm.xmodule = this.xmodule || false;
5835 this.store= Roo.factory(this.store, Roo.data);
5836 this.ds = this.store;
5837 this.ds.xmodule = this.xmodule || false;
5840 if (this.footer && this.store) {
5841 this.footer.dataSource = this.ds;
5842 this.footer = Roo.factory(this.footer);
5849 * Fires when a cell is clicked
5850 * @param {Roo.bootstrap.Table} this
5851 * @param {Roo.Element} el
5852 * @param {Number} rowIndex
5853 * @param {Number} columnIndex
5854 * @param {Roo.EventObject} e
5858 * @event celldblclick
5859 * Fires when a cell is double clicked
5860 * @param {Roo.bootstrap.Table} this
5861 * @param {Roo.Element} el
5862 * @param {Number} rowIndex
5863 * @param {Number} columnIndex
5864 * @param {Roo.EventObject} e
5866 "celldblclick" : true,
5869 * Fires when a row is clicked
5870 * @param {Roo.bootstrap.Table} this
5871 * @param {Roo.Element} el
5872 * @param {Number} rowIndex
5873 * @param {Roo.EventObject} e
5877 * @event rowdblclick
5878 * Fires when a row is double clicked
5879 * @param {Roo.bootstrap.Table} this
5880 * @param {Roo.Element} el
5881 * @param {Number} rowIndex
5882 * @param {Roo.EventObject} e
5884 "rowdblclick" : true,
5887 * Fires when a mouseover occur
5888 * @param {Roo.bootstrap.Table} this
5889 * @param {Roo.Element} el
5890 * @param {Number} rowIndex
5891 * @param {Number} columnIndex
5892 * @param {Roo.EventObject} e
5897 * Fires when a mouseout occur
5898 * @param {Roo.bootstrap.Table} this
5899 * @param {Roo.Element} el
5900 * @param {Number} rowIndex
5901 * @param {Number} columnIndex
5902 * @param {Roo.EventObject} e
5907 * Fires when a row is rendered, so you can change add a style to it.
5908 * @param {Roo.bootstrap.Table} this
5909 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5913 * @event rowsrendered
5914 * Fires when all the rows have been rendered
5915 * @param {Roo.bootstrap.Table} this
5917 'rowsrendered' : true,
5919 * @event contextmenu
5920 * The raw contextmenu event for the entire grid.
5921 * @param {Roo.EventObject} e
5923 "contextmenu" : true,
5925 * @event rowcontextmenu
5926 * Fires when a row is right clicked
5927 * @param {Roo.bootstrap.Table} this
5928 * @param {Number} rowIndex
5929 * @param {Roo.EventObject} e
5931 "rowcontextmenu" : true,
5933 * @event cellcontextmenu
5934 * Fires when a cell is right clicked
5935 * @param {Roo.bootstrap.Table} this
5936 * @param {Number} rowIndex
5937 * @param {Number} cellIndex
5938 * @param {Roo.EventObject} e
5940 "cellcontextmenu" : true,
5942 * @event headercontextmenu
5943 * Fires when a header is right clicked
5944 * @param {Roo.bootstrap.Table} this
5945 * @param {Number} columnIndex
5946 * @param {Roo.EventObject} e
5948 "headercontextmenu" : true
5952 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5978 rowSelection : false,
5979 cellSelection : false,
5982 // Roo.Element - the tbody
5984 // Roo.Element - thead element
5987 container: false, // used by gridpanel...
5991 getAutoCreate : function()
5993 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6000 if (this.scrollBody) {
6001 cfg.cls += ' table-body-fixed';
6004 cfg.cls += ' table-striped';
6008 cfg.cls += ' table-hover';
6010 if (this.bordered) {
6011 cfg.cls += ' table-bordered';
6013 if (this.condensed) {
6014 cfg.cls += ' table-condensed';
6016 if (this.responsive) {
6017 cfg.cls += ' table-responsive';
6021 cfg.cls+= ' ' +this.cls;
6024 // this lot should be simplifed...
6027 cfg.align=this.align;
6030 cfg.bgcolor=this.bgcolor;
6033 cfg.border=this.border;
6035 if (this.cellpadding) {
6036 cfg.cellpadding=this.cellpadding;
6038 if (this.cellspacing) {
6039 cfg.cellspacing=this.cellspacing;
6042 cfg.frame=this.frame;
6045 cfg.rules=this.rules;
6047 if (this.sortable) {
6048 cfg.sortable=this.sortable;
6051 cfg.summary=this.summary;
6054 cfg.width=this.width;
6057 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6060 if(this.store || this.cm){
6061 if(this.headerShow){
6062 cfg.cn.push(this.renderHeader());
6065 cfg.cn.push(this.renderBody());
6067 if(this.footerShow){
6068 cfg.cn.push(this.renderFooter());
6070 // where does this come from?
6071 //cfg.cls+= ' TableGrid';
6074 return { cn : [ cfg ] };
6077 initEvents : function()
6079 if(!this.store || !this.cm){
6082 if (this.selModel) {
6083 this.selModel.initEvents();
6087 //Roo.log('initEvents with ds!!!!');
6089 this.mainBody = this.el.select('tbody', true).first();
6090 this.mainHead = this.el.select('thead', true).first();
6097 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6098 e.on('click', _this.sort, _this);
6101 this.mainBody.on("click", this.onClick, this);
6102 this.mainBody.on("dblclick", this.onDblClick, this);
6104 // why is this done????? = it breaks dialogs??
6105 //this.parent().el.setStyle('position', 'relative');
6109 this.footer.parentId = this.id;
6110 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6113 this.el.select('tfoot tr td').first().addClass('hide');
6117 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6119 this.store.on('load', this.onLoad, this);
6120 this.store.on('beforeload', this.onBeforeLoad, this);
6121 this.store.on('update', this.onUpdate, this);
6122 this.store.on('add', this.onAdd, this);
6123 this.store.on("clear", this.clear, this);
6125 this.el.on("contextmenu", this.onContextMenu, this);
6127 this.mainBody.on('scroll', this.onBodyScroll, this);
6132 onContextMenu : function(e, t)
6134 this.processEvent("contextmenu", e);
6137 processEvent : function(name, e)
6139 if (name != 'touchstart' ) {
6140 this.fireEvent(name, e);
6143 var t = e.getTarget();
6145 var cell = Roo.get(t);
6151 if(cell.findParent('tfoot', false, true)){
6155 if(cell.findParent('thead', false, true)){
6157 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6158 cell = Roo.get(t).findParent('th', false, true);
6160 Roo.log("failed to find th in thead?");
6161 Roo.log(e.getTarget());
6166 var cellIndex = cell.dom.cellIndex;
6168 var ename = name == 'touchstart' ? 'click' : name;
6169 this.fireEvent("header" + ename, this, cellIndex, e);
6174 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6175 cell = Roo.get(t).findParent('td', false, true);
6177 Roo.log("failed to find th in tbody?");
6178 Roo.log(e.getTarget());
6183 var row = cell.findParent('tr', false, true);
6184 var cellIndex = cell.dom.cellIndex;
6185 var rowIndex = row.dom.rowIndex - 1;
6189 this.fireEvent("row" + name, this, rowIndex, e);
6193 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6199 onMouseover : function(e, el)
6201 var cell = Roo.get(el);
6207 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6208 cell = cell.findParent('td', false, true);
6211 var row = cell.findParent('tr', false, true);
6212 var cellIndex = cell.dom.cellIndex;
6213 var rowIndex = row.dom.rowIndex - 1; // start from 0
6215 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6219 onMouseout : function(e, el)
6221 var cell = Roo.get(el);
6227 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6228 cell = cell.findParent('td', false, true);
6231 var row = cell.findParent('tr', false, true);
6232 var cellIndex = cell.dom.cellIndex;
6233 var rowIndex = row.dom.rowIndex - 1; // start from 0
6235 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6239 onClick : function(e, el)
6241 var cell = Roo.get(el);
6243 if(!cell || (!this.cellSelection && !this.rowSelection)){
6247 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6248 cell = cell.findParent('td', false, true);
6251 if(!cell || typeof(cell) == 'undefined'){
6255 var row = cell.findParent('tr', false, true);
6257 if(!row || typeof(row) == 'undefined'){
6261 var cellIndex = cell.dom.cellIndex;
6262 var rowIndex = this.getRowIndex(row);
6264 // why??? - should these not be based on SelectionModel?
6265 if(this.cellSelection){
6266 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6269 if(this.rowSelection){
6270 this.fireEvent('rowclick', this, row, rowIndex, e);
6276 onDblClick : function(e,el)
6278 var cell = Roo.get(el);
6280 if(!cell || (!this.cellSelection && !this.rowSelection)){
6284 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6285 cell = cell.findParent('td', false, true);
6288 if(!cell || typeof(cell) == 'undefined'){
6292 var row = cell.findParent('tr', false, true);
6294 if(!row || typeof(row) == 'undefined'){
6298 var cellIndex = cell.dom.cellIndex;
6299 var rowIndex = this.getRowIndex(row);
6301 if(this.cellSelection){
6302 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6305 if(this.rowSelection){
6306 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6310 sort : function(e,el)
6312 var col = Roo.get(el);
6314 if(!col.hasClass('sortable')){
6318 var sort = col.attr('sort');
6321 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6325 this.store.sortInfo = {field : sort, direction : dir};
6328 Roo.log("calling footer first");
6329 this.footer.onClick('first');
6332 this.store.load({ params : { start : 0 } });
6336 renderHeader : function()
6344 this.totalWidth = 0;
6346 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6348 var config = cm.config[i];
6353 html: cm.getColumnHeader(i)
6358 if(typeof(config.sortable) != 'undefined' && config.sortable){
6360 c.html = '<i class="glyphicon"></i>' + c.html;
6363 if(typeof(config.lgHeader) != 'undefined'){
6364 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6367 if(typeof(config.mdHeader) != 'undefined'){
6368 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6371 if(typeof(config.smHeader) != 'undefined'){
6372 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6375 if(typeof(config.xsHeader) != 'undefined'){
6376 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6383 if(typeof(config.tooltip) != 'undefined'){
6384 c.tooltip = config.tooltip;
6387 if(typeof(config.colspan) != 'undefined'){
6388 c.colspan = config.colspan;
6391 if(typeof(config.hidden) != 'undefined' && config.hidden){
6392 c.style += ' display:none;';
6395 if(typeof(config.dataIndex) != 'undefined'){
6396 c.sort = config.dataIndex;
6401 if(typeof(config.align) != 'undefined' && config.align.length){
6402 c.style += ' text-align:' + config.align + ';';
6405 if(typeof(config.width) != 'undefined'){
6406 c.style += ' width:' + config.width + 'px;';
6407 this.totalWidth += config.width;
6409 this.totalWidth += 100; // assume minimum of 100 per column?
6412 if(typeof(config.cls) != 'undefined'){
6413 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6416 ['xs','sm','md','lg'].map(function(size){
6418 if(typeof(config[size]) == 'undefined'){
6422 if (!config[size]) { // 0 = hidden
6423 c.cls += ' hidden-' + size;
6427 c.cls += ' col-' + size + '-' + config[size];
6437 renderBody : function()
6447 colspan : this.cm.getColumnCount()
6457 renderFooter : function()
6467 colspan : this.cm.getColumnCount()
6481 // Roo.log('ds onload');
6486 var ds = this.store;
6488 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6489 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6490 if (_this.store.sortInfo) {
6492 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6493 e.select('i', true).addClass(['glyphicon-arrow-up']);
6496 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6497 e.select('i', true).addClass(['glyphicon-arrow-down']);
6502 var tbody = this.mainBody;
6504 if(ds.getCount() > 0){
6505 ds.data.each(function(d,rowIndex){
6506 var row = this.renderRow(cm, ds, rowIndex);
6508 tbody.createChild(row);
6512 if(row.cellObjects.length){
6513 Roo.each(row.cellObjects, function(r){
6514 _this.renderCellObject(r);
6521 Roo.each(this.el.select('tbody td', true).elements, function(e){
6522 e.on('mouseover', _this.onMouseover, _this);
6525 Roo.each(this.el.select('tbody td', true).elements, function(e){
6526 e.on('mouseout', _this.onMouseout, _this);
6528 this.fireEvent('rowsrendered', this);
6529 //if(this.loadMask){
6530 // this.maskEl.hide();
6537 onUpdate : function(ds,record)
6539 this.refreshRow(record);
6543 onRemove : function(ds, record, index, isUpdate){
6544 if(isUpdate !== true){
6545 this.fireEvent("beforerowremoved", this, index, record);
6547 var bt = this.mainBody.dom;
6549 var rows = this.el.select('tbody > tr', true).elements;
6551 if(typeof(rows[index]) != 'undefined'){
6552 bt.removeChild(rows[index].dom);
6555 // if(bt.rows[index]){
6556 // bt.removeChild(bt.rows[index]);
6559 if(isUpdate !== true){
6560 //this.stripeRows(index);
6561 //this.syncRowHeights(index, index);
6563 this.fireEvent("rowremoved", this, index, record);
6567 onAdd : function(ds, records, rowIndex)
6569 //Roo.log('on Add called');
6570 // - note this does not handle multiple adding very well..
6571 var bt = this.mainBody.dom;
6572 for (var i =0 ; i < records.length;i++) {
6573 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6574 //Roo.log(records[i]);
6575 //Roo.log(this.store.getAt(rowIndex+i));
6576 this.insertRow(this.store, rowIndex + i, false);
6583 refreshRow : function(record){
6584 var ds = this.store, index;
6585 if(typeof record == 'number'){
6587 record = ds.getAt(index);
6589 index = ds.indexOf(record);
6591 this.insertRow(ds, index, true);
6593 this.onRemove(ds, record, index+1, true);
6595 //this.syncRowHeights(index, index);
6597 this.fireEvent("rowupdated", this, index, record);
6600 insertRow : function(dm, rowIndex, isUpdate){
6603 this.fireEvent("beforerowsinserted", this, rowIndex);
6605 //var s = this.getScrollState();
6606 var row = this.renderRow(this.cm, this.store, rowIndex);
6607 // insert before rowIndex..
6608 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6612 if(row.cellObjects.length){
6613 Roo.each(row.cellObjects, function(r){
6614 _this.renderCellObject(r);
6619 this.fireEvent("rowsinserted", this, rowIndex);
6620 //this.syncRowHeights(firstRow, lastRow);
6621 //this.stripeRows(firstRow);
6628 getRowDom : function(rowIndex)
6630 var rows = this.el.select('tbody > tr', true).elements;
6632 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6635 // returns the object tree for a tr..
6638 renderRow : function(cm, ds, rowIndex)
6641 var d = ds.getAt(rowIndex);
6648 var cellObjects = [];
6650 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6651 var config = cm.config[i];
6653 var renderer = cm.getRenderer(i);
6657 if(typeof(renderer) !== 'undefined'){
6658 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6660 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6661 // and are rendered into the cells after the row is rendered - using the id for the element.
6663 if(typeof(value) === 'object'){
6673 rowIndex : rowIndex,
6678 this.fireEvent('rowclass', this, rowcfg);
6682 cls : rowcfg.rowClass,
6684 html: (typeof(value) === 'object') ? '' : value
6691 if(typeof(config.colspan) != 'undefined'){
6692 td.colspan = config.colspan;
6695 if(typeof(config.hidden) != 'undefined' && config.hidden){
6696 td.style += ' display:none;';
6699 if(typeof(config.align) != 'undefined' && config.align.length){
6700 td.style += ' text-align:' + config.align + ';';
6703 if(typeof(config.width) != 'undefined'){
6704 td.style += ' width:' + config.width + 'px;';
6707 if(typeof(config.cursor) != 'undefined'){
6708 td.style += ' cursor:' + config.cursor + ';';
6711 if(typeof(config.cls) != 'undefined'){
6712 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6715 ['xs','sm','md','lg'].map(function(size){
6717 if(typeof(config[size]) == 'undefined'){
6721 if (!config[size]) { // 0 = hidden
6722 td.cls += ' hidden-' + size;
6726 td.cls += ' col-' + size + '-' + config[size];
6734 row.cellObjects = cellObjects;
6742 onBeforeLoad : function()
6744 //Roo.log('ds onBeforeLoad');
6748 //if(this.loadMask){
6749 // this.maskEl.show();
6757 this.el.select('tbody', true).first().dom.innerHTML = '';
6760 * Show or hide a row.
6761 * @param {Number} rowIndex to show or hide
6762 * @param {Boolean} state hide
6764 setRowVisibility : function(rowIndex, state)
6766 var bt = this.mainBody.dom;
6768 var rows = this.el.select('tbody > tr', true).elements;
6770 if(typeof(rows[rowIndex]) == 'undefined'){
6773 rows[rowIndex].dom.style.display = state ? '' : 'none';
6777 getSelectionModel : function(){
6779 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6781 return this.selModel;
6784 * Render the Roo.bootstrap object from renderder
6786 renderCellObject : function(r)
6790 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6792 var t = r.cfg.render(r.container);
6795 Roo.each(r.cfg.cn, function(c){
6797 container: t.getChildContainer(),
6800 _this.renderCellObject(child);
6805 getRowIndex : function(row)
6809 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6820 * Returns the grid's underlying element = used by panel.Grid
6821 * @return {Element} The element
6823 getGridEl : function(){
6827 * Forces a resize - used by panel.Grid
6828 * @return {Element} The element
6830 autoSize : function()
6832 //var ctr = Roo.get(this.container.dom.parentElement);
6833 var ctr = Roo.get(this.el.dom);
6835 var thd = this.getGridEl().select('thead',true).first();
6836 var tbd = this.getGridEl().select('tbody', true).first();
6837 var tfd = this.getGridEl().select('tfoot', true).first();
6839 var cw = ctr.getWidth();
6843 tbd.setSize(ctr.getWidth(),
6844 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6846 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6849 cw = Math.max(cw, this.totalWidth);
6850 this.getGridEl().select('tr',true).setWidth(cw);
6851 // resize 'expandable coloumn?
6853 return; // we doe not have a view in this design..
6856 onBodyScroll: function()
6858 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6859 this.mainHead.setStyle({
6860 'position' : 'relative',
6861 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6866 var scrollHeight = this.mainBody.dom.scrollHeight;
6868 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6870 var height = this.mainBody.getHeight();
6872 if(scrollHeight - height == scrollTop) {
6874 var total = this.ds.getTotalCount();
6876 if(this.footer.cursor + this.footer.pageSize < total){
6878 this.footer.ds.load({
6880 start : this.footer.cursor + this.footer.pageSize,
6881 limit : this.footer.pageSize
6902 * @class Roo.bootstrap.TableCell
6903 * @extends Roo.bootstrap.Component
6904 * Bootstrap TableCell class
6905 * @cfg {String} html cell contain text
6906 * @cfg {String} cls cell class
6907 * @cfg {String} tag cell tag (td|th) default td
6908 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6909 * @cfg {String} align Aligns the content in a cell
6910 * @cfg {String} axis Categorizes cells
6911 * @cfg {String} bgcolor Specifies the background color of a cell
6912 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6913 * @cfg {Number} colspan Specifies the number of columns a cell should span
6914 * @cfg {String} headers Specifies one or more header cells a cell is related to
6915 * @cfg {Number} height Sets the height of a cell
6916 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6917 * @cfg {Number} rowspan Sets the number of rows a cell should span
6918 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6919 * @cfg {String} valign Vertical aligns the content in a cell
6920 * @cfg {Number} width Specifies the width of a cell
6923 * Create a new TableCell
6924 * @param {Object} config The config object
6927 Roo.bootstrap.TableCell = function(config){
6928 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6931 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6951 getAutoCreate : function(){
6952 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6972 cfg.align=this.align
6978 cfg.bgcolor=this.bgcolor
6981 cfg.charoff=this.charoff
6984 cfg.colspan=this.colspan
6987 cfg.headers=this.headers
6990 cfg.height=this.height
6993 cfg.nowrap=this.nowrap
6996 cfg.rowspan=this.rowspan
6999 cfg.scope=this.scope
7002 cfg.valign=this.valign
7005 cfg.width=this.width
7024 * @class Roo.bootstrap.TableRow
7025 * @extends Roo.bootstrap.Component
7026 * Bootstrap TableRow class
7027 * @cfg {String} cls row class
7028 * @cfg {String} align Aligns the content in a table row
7029 * @cfg {String} bgcolor Specifies a background color for a table row
7030 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7031 * @cfg {String} valign Vertical aligns the content in a table row
7034 * Create a new TableRow
7035 * @param {Object} config The config object
7038 Roo.bootstrap.TableRow = function(config){
7039 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7042 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7050 getAutoCreate : function(){
7051 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7061 cfg.align = this.align;
7064 cfg.bgcolor = this.bgcolor;
7067 cfg.charoff = this.charoff;
7070 cfg.valign = this.valign;
7088 * @class Roo.bootstrap.TableBody
7089 * @extends Roo.bootstrap.Component
7090 * Bootstrap TableBody class
7091 * @cfg {String} cls element class
7092 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7093 * @cfg {String} align Aligns the content inside the element
7094 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7095 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7098 * Create a new TableBody
7099 * @param {Object} config The config object
7102 Roo.bootstrap.TableBody = function(config){
7103 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7106 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7114 getAutoCreate : function(){
7115 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7129 cfg.align = this.align;
7132 cfg.charoff = this.charoff;
7135 cfg.valign = this.valign;
7142 // initEvents : function()
7149 // this.store = Roo.factory(this.store, Roo.data);
7150 // this.store.on('load', this.onLoad, this);
7152 // this.store.load();
7156 // onLoad: function ()
7158 // this.fireEvent('load', this);
7168 * Ext JS Library 1.1.1
7169 * Copyright(c) 2006-2007, Ext JS, LLC.
7171 * Originally Released Under LGPL - original licence link has changed is not relivant.
7174 * <script type="text/javascript">
7177 // as we use this in bootstrap.
7178 Roo.namespace('Roo.form');
7180 * @class Roo.form.Action
7181 * Internal Class used to handle form actions
7183 * @param {Roo.form.BasicForm} el The form element or its id
7184 * @param {Object} config Configuration options
7189 // define the action interface
7190 Roo.form.Action = function(form, options){
7192 this.options = options || {};
7195 * Client Validation Failed
7198 Roo.form.Action.CLIENT_INVALID = 'client';
7200 * Server Validation Failed
7203 Roo.form.Action.SERVER_INVALID = 'server';
7205 * Connect to Server Failed
7208 Roo.form.Action.CONNECT_FAILURE = 'connect';
7210 * Reading Data from Server Failed
7213 Roo.form.Action.LOAD_FAILURE = 'load';
7215 Roo.form.Action.prototype = {
7217 failureType : undefined,
7218 response : undefined,
7222 run : function(options){
7227 success : function(response){
7232 handleResponse : function(response){
7236 // default connection failure
7237 failure : function(response){
7239 this.response = response;
7240 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7241 this.form.afterAction(this, false);
7244 processResponse : function(response){
7245 this.response = response;
7246 if(!response.responseText){
7249 this.result = this.handleResponse(response);
7253 // utility functions used internally
7254 getUrl : function(appendParams){
7255 var url = this.options.url || this.form.url || this.form.el.dom.action;
7257 var p = this.getParams();
7259 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7265 getMethod : function(){
7266 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7269 getParams : function(){
7270 var bp = this.form.baseParams;
7271 var p = this.options.params;
7273 if(typeof p == "object"){
7274 p = Roo.urlEncode(Roo.applyIf(p, bp));
7275 }else if(typeof p == 'string' && bp){
7276 p += '&' + Roo.urlEncode(bp);
7279 p = Roo.urlEncode(bp);
7284 createCallback : function(){
7286 success: this.success,
7287 failure: this.failure,
7289 timeout: (this.form.timeout*1000),
7290 upload: this.form.fileUpload ? this.success : undefined
7295 Roo.form.Action.Submit = function(form, options){
7296 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7299 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7302 haveProgress : false,
7303 uploadComplete : false,
7305 // uploadProgress indicator.
7306 uploadProgress : function()
7308 if (!this.form.progressUrl) {
7312 if (!this.haveProgress) {
7313 Roo.MessageBox.progress("Uploading", "Uploading");
7315 if (this.uploadComplete) {
7316 Roo.MessageBox.hide();
7320 this.haveProgress = true;
7322 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7324 var c = new Roo.data.Connection();
7326 url : this.form.progressUrl,
7331 success : function(req){
7332 //console.log(data);
7336 rdata = Roo.decode(req.responseText)
7338 Roo.log("Invalid data from server..");
7342 if (!rdata || !rdata.success) {
7344 Roo.MessageBox.alert(Roo.encode(rdata));
7347 var data = rdata.data;
7349 if (this.uploadComplete) {
7350 Roo.MessageBox.hide();
7355 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7356 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7359 this.uploadProgress.defer(2000,this);
7362 failure: function(data) {
7363 Roo.log('progress url failed ');
7374 // run get Values on the form, so it syncs any secondary forms.
7375 this.form.getValues();
7377 var o = this.options;
7378 var method = this.getMethod();
7379 var isPost = method == 'POST';
7380 if(o.clientValidation === false || this.form.isValid()){
7382 if (this.form.progressUrl) {
7383 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7384 (new Date() * 1) + '' + Math.random());
7389 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7390 form:this.form.el.dom,
7391 url:this.getUrl(!isPost),
7393 params:isPost ? this.getParams() : null,
7394 isUpload: this.form.fileUpload
7397 this.uploadProgress();
7399 }else if (o.clientValidation !== false){ // client validation failed
7400 this.failureType = Roo.form.Action.CLIENT_INVALID;
7401 this.form.afterAction(this, false);
7405 success : function(response)
7407 this.uploadComplete= true;
7408 if (this.haveProgress) {
7409 Roo.MessageBox.hide();
7413 var result = this.processResponse(response);
7414 if(result === true || result.success){
7415 this.form.afterAction(this, true);
7419 this.form.markInvalid(result.errors);
7420 this.failureType = Roo.form.Action.SERVER_INVALID;
7422 this.form.afterAction(this, false);
7424 failure : function(response)
7426 this.uploadComplete= true;
7427 if (this.haveProgress) {
7428 Roo.MessageBox.hide();
7431 this.response = response;
7432 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7433 this.form.afterAction(this, false);
7436 handleResponse : function(response){
7437 if(this.form.errorReader){
7438 var rs = this.form.errorReader.read(response);
7441 for(var i = 0, len = rs.records.length; i < len; i++) {
7442 var r = rs.records[i];
7446 if(errors.length < 1){
7450 success : rs.success,
7456 ret = Roo.decode(response.responseText);
7460 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7470 Roo.form.Action.Load = function(form, options){
7471 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7472 this.reader = this.form.reader;
7475 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7480 Roo.Ajax.request(Roo.apply(
7481 this.createCallback(), {
7482 method:this.getMethod(),
7483 url:this.getUrl(false),
7484 params:this.getParams()
7488 success : function(response){
7490 var result = this.processResponse(response);
7491 if(result === true || !result.success || !result.data){
7492 this.failureType = Roo.form.Action.LOAD_FAILURE;
7493 this.form.afterAction(this, false);
7496 this.form.clearInvalid();
7497 this.form.setValues(result.data);
7498 this.form.afterAction(this, true);
7501 handleResponse : function(response){
7502 if(this.form.reader){
7503 var rs = this.form.reader.read(response);
7504 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7506 success : rs.success,
7510 return Roo.decode(response.responseText);
7514 Roo.form.Action.ACTION_TYPES = {
7515 'load' : Roo.form.Action.Load,
7516 'submit' : Roo.form.Action.Submit
7525 * @class Roo.bootstrap.Form
7526 * @extends Roo.bootstrap.Component
7527 * Bootstrap Form class
7528 * @cfg {String} method GET | POST (default POST)
7529 * @cfg {String} labelAlign top | left (default top)
7530 * @cfg {String} align left | right - for navbars
7531 * @cfg {Boolean} loadMask load mask when submit (default true)
7536 * @param {Object} config The config object
7540 Roo.bootstrap.Form = function(config){
7541 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7543 Roo.bootstrap.Form.popover.apply();
7547 * @event clientvalidation
7548 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7549 * @param {Form} this
7550 * @param {Boolean} valid true if the form has passed client-side validation
7552 clientvalidation: true,
7554 * @event beforeaction
7555 * Fires before any action is performed. Return false to cancel the action.
7556 * @param {Form} this
7557 * @param {Action} action The action to be performed
7561 * @event actionfailed
7562 * Fires when an action fails.
7563 * @param {Form} this
7564 * @param {Action} action The action that failed
7566 actionfailed : true,
7568 * @event actioncomplete
7569 * Fires when an action is completed.
7570 * @param {Form} this
7571 * @param {Action} action The action that completed
7573 actioncomplete : true
7578 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7581 * @cfg {String} method
7582 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7587 * The URL to use for form actions if one isn't supplied in the action options.
7590 * @cfg {Boolean} fileUpload
7591 * Set to true if this form is a file upload.
7595 * @cfg {Object} baseParams
7596 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7600 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7604 * @cfg {Sting} align (left|right) for navbar forms
7609 activeAction : null,
7612 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7613 * element by passing it or its id or mask the form itself by passing in true.
7616 waitMsgTarget : false,
7621 * @cfg {Boolean} errorMask (true|false) default false
7626 * @cfg {Number} maskOffset Default 100
7631 * @cfg {Boolean} maskBody
7635 getAutoCreate : function(){
7639 method : this.method || 'POST',
7640 id : this.id || Roo.id(),
7643 if (this.parent().xtype.match(/^Nav/)) {
7644 cfg.cls = 'navbar-form navbar-' + this.align;
7648 if (this.labelAlign == 'left' ) {
7649 cfg.cls += ' form-horizontal';
7655 initEvents : function()
7657 this.el.on('submit', this.onSubmit, this);
7658 // this was added as random key presses on the form where triggering form submit.
7659 this.el.on('keypress', function(e) {
7660 if (e.getCharCode() != 13) {
7663 // we might need to allow it for textareas.. and some other items.
7664 // check e.getTarget().
7666 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7670 Roo.log("keypress blocked");
7678 onSubmit : function(e){
7683 * Returns true if client-side validation on the form is successful.
7686 isValid : function(){
7687 var items = this.getItems();
7691 items.each(function(f){
7697 if(!target && f.el.isVisible(true)){
7703 if(this.errorMask && !valid){
7704 Roo.bootstrap.Form.popover.mask(this, target);
7711 * Returns true if any fields in this form have changed since their original load.
7714 isDirty : function(){
7716 var items = this.getItems();
7717 items.each(function(f){
7727 * Performs a predefined action (submit or load) or custom actions you define on this form.
7728 * @param {String} actionName The name of the action type
7729 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7730 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7731 * accept other config options):
7733 Property Type Description
7734 ---------------- --------------- ----------------------------------------------------------------------------------
7735 url String The url for the action (defaults to the form's url)
7736 method String The form method to use (defaults to the form's method, or POST if not defined)
7737 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7738 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7739 validate the form on the client (defaults to false)
7741 * @return {BasicForm} this
7743 doAction : function(action, options){
7744 if(typeof action == 'string'){
7745 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7747 if(this.fireEvent('beforeaction', this, action) !== false){
7748 this.beforeAction(action);
7749 action.run.defer(100, action);
7755 beforeAction : function(action){
7756 var o = action.options;
7761 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7763 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7766 // not really supported yet.. ??
7768 //if(this.waitMsgTarget === true){
7769 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7770 //}else if(this.waitMsgTarget){
7771 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7772 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7774 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7780 afterAction : function(action, success){
7781 this.activeAction = null;
7782 var o = action.options;
7787 Roo.get(document.body).unmask();
7793 //if(this.waitMsgTarget === true){
7794 // this.el.unmask();
7795 //}else if(this.waitMsgTarget){
7796 // this.waitMsgTarget.unmask();
7798 // Roo.MessageBox.updateProgress(1);
7799 // Roo.MessageBox.hide();
7806 Roo.callback(o.success, o.scope, [this, action]);
7807 this.fireEvent('actioncomplete', this, action);
7811 // failure condition..
7812 // we have a scenario where updates need confirming.
7813 // eg. if a locking scenario exists..
7814 // we look for { errors : { needs_confirm : true }} in the response.
7816 (typeof(action.result) != 'undefined') &&
7817 (typeof(action.result.errors) != 'undefined') &&
7818 (typeof(action.result.errors.needs_confirm) != 'undefined')
7821 Roo.log("not supported yet");
7824 Roo.MessageBox.confirm(
7825 "Change requires confirmation",
7826 action.result.errorMsg,
7831 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7841 Roo.callback(o.failure, o.scope, [this, action]);
7842 // show an error message if no failed handler is set..
7843 if (!this.hasListener('actionfailed')) {
7844 Roo.log("need to add dialog support");
7846 Roo.MessageBox.alert("Error",
7847 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7848 action.result.errorMsg :
7849 "Saving Failed, please check your entries or try again"
7854 this.fireEvent('actionfailed', this, action);
7859 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7860 * @param {String} id The value to search for
7863 findField : function(id){
7864 var items = this.getItems();
7865 var field = items.get(id);
7867 items.each(function(f){
7868 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7875 return field || null;
7878 * Mark fields in this form invalid in bulk.
7879 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7880 * @return {BasicForm} this
7882 markInvalid : function(errors){
7883 if(errors instanceof Array){
7884 for(var i = 0, len = errors.length; i < len; i++){
7885 var fieldError = errors[i];
7886 var f = this.findField(fieldError.id);
7888 f.markInvalid(fieldError.msg);
7894 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7895 field.markInvalid(errors[id]);
7899 //Roo.each(this.childForms || [], function (f) {
7900 // f.markInvalid(errors);
7907 * Set values for fields in this form in bulk.
7908 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7909 * @return {BasicForm} this
7911 setValues : function(values){
7912 if(values instanceof Array){ // array of objects
7913 for(var i = 0, len = values.length; i < len; i++){
7915 var f = this.findField(v.id);
7917 f.setValue(v.value);
7918 if(this.trackResetOnLoad){
7919 f.originalValue = f.getValue();
7923 }else{ // object hash
7926 if(typeof values[id] != 'function' && (field = this.findField(id))){
7928 if (field.setFromData &&
7930 field.displayField &&
7931 // combos' with local stores can
7932 // be queried via setValue()
7933 // to set their value..
7934 (field.store && !field.store.isLocal)
7938 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7939 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7940 field.setFromData(sd);
7942 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
7944 field.setFromData(values);
7947 field.setValue(values[id]);
7951 if(this.trackResetOnLoad){
7952 field.originalValue = field.getValue();
7958 //Roo.each(this.childForms || [], function (f) {
7959 // f.setValues(values);
7966 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7967 * they are returned as an array.
7968 * @param {Boolean} asString
7971 getValues : function(asString){
7972 //if (this.childForms) {
7973 // copy values from the child forms
7974 // Roo.each(this.childForms, function (f) {
7975 // this.setValues(f.getValues());
7981 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7982 if(asString === true){
7985 return Roo.urlDecode(fs);
7989 * Returns the fields in this form as an object with key/value pairs.
7990 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7993 getFieldValues : function(with_hidden)
7995 var items = this.getItems();
7997 items.each(function(f){
8003 var v = f.getValue();
8005 if (f.inputType =='radio') {
8006 if (typeof(ret[f.getName()]) == 'undefined') {
8007 ret[f.getName()] = ''; // empty..
8010 if (!f.el.dom.checked) {
8018 if(f.xtype == 'MoneyField'){
8019 ret[f.currencyName] = f.getCurrency();
8022 // not sure if this supported any more..
8023 if ((typeof(v) == 'object') && f.getRawValue) {
8024 v = f.getRawValue() ; // dates..
8026 // combo boxes where name != hiddenName...
8027 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8028 ret[f.name] = f.getRawValue();
8030 ret[f.getName()] = v;
8037 * Clears all invalid messages in this form.
8038 * @return {BasicForm} this
8040 clearInvalid : function(){
8041 var items = this.getItems();
8043 items.each(function(f){
8054 * @return {BasicForm} this
8057 var items = this.getItems();
8058 items.each(function(f){
8062 Roo.each(this.childForms || [], function (f) {
8070 getItems : function()
8072 var r=new Roo.util.MixedCollection(false, function(o){
8073 return o.id || (o.id = Roo.id());
8075 var iter = function(el) {
8082 Roo.each(el.items,function(e) {
8096 Roo.apply(Roo.bootstrap.Form, {
8123 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8124 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8125 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8126 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8129 this.maskEl.top.enableDisplayMode("block");
8130 this.maskEl.left.enableDisplayMode("block");
8131 this.maskEl.bottom.enableDisplayMode("block");
8132 this.maskEl.right.enableDisplayMode("block");
8134 this.toolTip = new Roo.bootstrap.Tooltip({
8135 cls : 'roo-form-error-popover',
8137 'left' : ['r-l', [-2,0], 'right'],
8138 'right' : ['l-r', [2,0], 'left'],
8139 'bottom' : ['tl-bl', [0,2], 'top'],
8140 'top' : [ 'bl-tl', [0,-2], 'bottom']
8144 this.toolTip.render(Roo.get(document.body));
8146 this.toolTip.el.enableDisplayMode("block");
8148 Roo.get(document.body).on('click', function(){
8152 Roo.get(document.body).on('touchstart', function(){
8156 this.isApplied = true
8159 mask : function(form, target)
8163 this.target = target;
8165 if(!this.form.errorMask || !target.el){
8169 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8171 Roo.log(scrollable);
8173 var ot = this.target.el.calcOffsetsTo(scrollable);
8175 var scrollTo = ot[1] - this.form.maskOffset;
8177 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8179 scrollable.scrollTo('top', scrollTo);
8181 var box = this.target.el.getBox();
8183 var zIndex = Roo.bootstrap.Modal.zIndex++;
8186 this.maskEl.top.setStyle('position', 'absolute');
8187 this.maskEl.top.setStyle('z-index', zIndex);
8188 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8189 this.maskEl.top.setLeft(0);
8190 this.maskEl.top.setTop(0);
8191 this.maskEl.top.show();
8193 this.maskEl.left.setStyle('position', 'absolute');
8194 this.maskEl.left.setStyle('z-index', zIndex);
8195 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8196 this.maskEl.left.setLeft(0);
8197 this.maskEl.left.setTop(box.y - this.padding);
8198 this.maskEl.left.show();
8200 this.maskEl.bottom.setStyle('position', 'absolute');
8201 this.maskEl.bottom.setStyle('z-index', zIndex);
8202 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8203 this.maskEl.bottom.setLeft(0);
8204 this.maskEl.bottom.setTop(box.bottom + this.padding);
8205 this.maskEl.bottom.show();
8207 this.maskEl.right.setStyle('position', 'absolute');
8208 this.maskEl.right.setStyle('z-index', zIndex);
8209 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8210 this.maskEl.right.setLeft(box.right + this.padding);
8211 this.maskEl.right.setTop(box.y - this.padding);
8212 this.maskEl.right.show();
8214 this.toolTip.bindEl = this.target.el;
8216 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8218 var tip = this.target.blankText;
8220 if(this.target.getValue() !== '' ) {
8222 if (this.target.invalidText.length) {
8223 tip = this.target.invalidText;
8224 } else if (this.target.regexText.length){
8225 tip = this.target.regexText;
8229 this.toolTip.show(tip);
8231 this.intervalID = window.setInterval(function() {
8232 Roo.bootstrap.Form.popover.unmask();
8235 window.onwheel = function(){ return false;};
8237 (function(){ this.isMasked = true; }).defer(500, this);
8243 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8247 this.maskEl.top.setStyle('position', 'absolute');
8248 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8249 this.maskEl.top.hide();
8251 this.maskEl.left.setStyle('position', 'absolute');
8252 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8253 this.maskEl.left.hide();
8255 this.maskEl.bottom.setStyle('position', 'absolute');
8256 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8257 this.maskEl.bottom.hide();
8259 this.maskEl.right.setStyle('position', 'absolute');
8260 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8261 this.maskEl.right.hide();
8263 this.toolTip.hide();
8265 this.toolTip.el.hide();
8267 window.onwheel = function(){ return true;};
8269 if(this.intervalID){
8270 window.clearInterval(this.intervalID);
8271 this.intervalID = false;
8274 this.isMasked = false;
8284 * Ext JS Library 1.1.1
8285 * Copyright(c) 2006-2007, Ext JS, LLC.
8287 * Originally Released Under LGPL - original licence link has changed is not relivant.
8290 * <script type="text/javascript">
8293 * @class Roo.form.VTypes
8294 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8297 Roo.form.VTypes = function(){
8298 // closure these in so they are only created once.
8299 var alpha = /^[a-zA-Z_]+$/;
8300 var alphanum = /^[a-zA-Z0-9_]+$/;
8301 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8302 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8304 // All these messages and functions are configurable
8307 * The function used to validate email addresses
8308 * @param {String} value The email address
8310 'email' : function(v){
8311 return email.test(v);
8314 * The error text to display when the email validation function returns false
8317 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8319 * The keystroke filter mask to be applied on email input
8322 'emailMask' : /[a-z0-9_\.\-@]/i,
8325 * The function used to validate URLs
8326 * @param {String} value The URL
8328 'url' : function(v){
8332 * The error text to display when the url validation function returns false
8335 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8338 * The function used to validate alpha values
8339 * @param {String} value The value
8341 'alpha' : function(v){
8342 return alpha.test(v);
8345 * The error text to display when the alpha validation function returns false
8348 'alphaText' : 'This field should only contain letters and _',
8350 * The keystroke filter mask to be applied on alpha input
8353 'alphaMask' : /[a-z_]/i,
8356 * The function used to validate alphanumeric values
8357 * @param {String} value The value
8359 'alphanum' : function(v){
8360 return alphanum.test(v);
8363 * The error text to display when the alphanumeric validation function returns false
8366 'alphanumText' : 'This field should only contain letters, numbers and _',
8368 * The keystroke filter mask to be applied on alphanumeric input
8371 'alphanumMask' : /[a-z0-9_]/i
8381 * @class Roo.bootstrap.Input
8382 * @extends Roo.bootstrap.Component
8383 * Bootstrap Input class
8384 * @cfg {Boolean} disabled is it disabled
8385 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8386 * @cfg {String} name name of the input
8387 * @cfg {string} fieldLabel - the label associated
8388 * @cfg {string} placeholder - placeholder to put in text.
8389 * @cfg {string} before - input group add on before
8390 * @cfg {string} after - input group add on after
8391 * @cfg {string} size - (lg|sm) or leave empty..
8392 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8393 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8394 * @cfg {Number} md colspan out of 12 for computer-sized screens
8395 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8396 * @cfg {string} value default value of the input
8397 * @cfg {Number} labelWidth set the width of label
8398 * @cfg {Number} labellg set the width of label (1-12)
8399 * @cfg {Number} labelmd set the width of label (1-12)
8400 * @cfg {Number} labelsm set the width of label (1-12)
8401 * @cfg {Number} labelxs set the width of label (1-12)
8402 * @cfg {String} labelAlign (top|left)
8403 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8404 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8405 * @cfg {String} indicatorpos (left|right) default left
8407 * @cfg {String} align (left|center|right) Default left
8408 * @cfg {Boolean} forceFeedback (true|false) Default false
8414 * Create a new Input
8415 * @param {Object} config The config object
8418 Roo.bootstrap.Input = function(config){
8420 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8425 * Fires when this field receives input focus.
8426 * @param {Roo.form.Field} this
8431 * Fires when this field loses input focus.
8432 * @param {Roo.form.Field} this
8437 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8438 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8439 * @param {Roo.form.Field} this
8440 * @param {Roo.EventObject} e The event object
8445 * Fires just before the field blurs if the field value has changed.
8446 * @param {Roo.form.Field} this
8447 * @param {Mixed} newValue The new value
8448 * @param {Mixed} oldValue The original value
8453 * Fires after the field has been marked as invalid.
8454 * @param {Roo.form.Field} this
8455 * @param {String} msg The validation message
8460 * Fires after the field has been validated with no errors.
8461 * @param {Roo.form.Field} this
8466 * Fires after the key up
8467 * @param {Roo.form.Field} this
8468 * @param {Roo.EventObject} e The event Object
8474 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8476 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8477 automatic validation (defaults to "keyup").
8479 validationEvent : "keyup",
8481 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8483 validateOnBlur : true,
8485 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8487 validationDelay : 250,
8489 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8491 focusClass : "x-form-focus", // not needed???
8495 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8497 invalidClass : "has-warning",
8500 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8502 validClass : "has-success",
8505 * @cfg {Boolean} hasFeedback (true|false) default true
8510 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8512 invalidFeedbackClass : "glyphicon-warning-sign",
8515 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8517 validFeedbackClass : "glyphicon-ok",
8520 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8522 selectOnFocus : false,
8525 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8529 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8534 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8536 disableKeyFilter : false,
8539 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8543 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8547 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8549 blankText : "Please complete this mandatory field",
8552 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8556 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8558 maxLength : Number.MAX_VALUE,
8560 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8562 minLengthText : "The minimum length for this field is {0}",
8564 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8566 maxLengthText : "The maximum length for this field is {0}",
8570 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8571 * If available, this function will be called only after the basic validators all return true, and will be passed the
8572 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8576 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8577 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8578 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8582 * @cfg {String} regexText -- Depricated - use Invalid Text
8587 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8593 autocomplete: false,
8612 formatedValue : false,
8613 forceFeedback : false,
8615 indicatorpos : 'left',
8622 parentLabelAlign : function()
8625 while (parent.parent()) {
8626 parent = parent.parent();
8627 if (typeof(parent.labelAlign) !='undefined') {
8628 return parent.labelAlign;
8635 getAutoCreate : function()
8637 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8643 if(this.inputType != 'hidden'){
8644 cfg.cls = 'form-group' //input-group
8650 type : this.inputType,
8652 cls : 'form-control',
8653 placeholder : this.placeholder || '',
8654 autocomplete : this.autocomplete || 'new-password'
8658 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8661 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8662 input.maxLength = this.maxLength;
8665 if (this.disabled) {
8666 input.disabled=true;
8669 if (this.readOnly) {
8670 input.readonly=true;
8674 input.name = this.name;
8678 input.cls += ' input-' + this.size;
8682 ['xs','sm','md','lg'].map(function(size){
8683 if (settings[size]) {
8684 cfg.cls += ' col-' + size + '-' + settings[size];
8688 var inputblock = input;
8692 cls: 'glyphicon form-control-feedback'
8695 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8698 cls : 'has-feedback',
8706 if (this.before || this.after) {
8709 cls : 'input-group',
8713 if (this.before && typeof(this.before) == 'string') {
8715 inputblock.cn.push({
8717 cls : 'roo-input-before input-group-addon',
8721 if (this.before && typeof(this.before) == 'object') {
8722 this.before = Roo.factory(this.before);
8724 inputblock.cn.push({
8726 cls : 'roo-input-before input-group-' +
8727 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8731 inputblock.cn.push(input);
8733 if (this.after && typeof(this.after) == 'string') {
8734 inputblock.cn.push({
8736 cls : 'roo-input-after input-group-addon',
8740 if (this.after && typeof(this.after) == 'object') {
8741 this.after = Roo.factory(this.after);
8743 inputblock.cn.push({
8745 cls : 'roo-input-after input-group-' +
8746 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8750 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8751 inputblock.cls += ' has-feedback';
8752 inputblock.cn.push(feedback);
8756 if (align ==='left' && this.fieldLabel.length) {
8758 cfg.cls += ' roo-form-group-label-left';
8763 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8764 tooltip : 'This field is required'
8769 cls : 'control-label',
8770 html : this.fieldLabel
8781 var labelCfg = cfg.cn[1];
8782 var contentCfg = cfg.cn[2];
8784 if(this.indicatorpos == 'right'){
8789 cls : 'control-label',
8793 html : this.fieldLabel
8797 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8798 tooltip : 'This field is required'
8811 labelCfg = cfg.cn[0];
8812 contentCfg = cfg.cn[1];
8816 if(this.labelWidth > 12){
8817 labelCfg.style = "width: " + this.labelWidth + 'px';
8820 if(this.labelWidth < 13 && this.labelmd == 0){
8821 this.labelmd = this.labelWidth;
8824 if(this.labellg > 0){
8825 labelCfg.cls += ' col-lg-' + this.labellg;
8826 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8829 if(this.labelmd > 0){
8830 labelCfg.cls += ' col-md-' + this.labelmd;
8831 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8834 if(this.labelsm > 0){
8835 labelCfg.cls += ' col-sm-' + this.labelsm;
8836 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8839 if(this.labelxs > 0){
8840 labelCfg.cls += ' col-xs-' + this.labelxs;
8841 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8845 } else if ( this.fieldLabel.length) {
8850 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8851 tooltip : 'This field is required'
8855 //cls : 'input-group-addon',
8856 html : this.fieldLabel
8864 if(this.indicatorpos == 'right'){
8869 //cls : 'input-group-addon',
8870 html : this.fieldLabel
8875 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8876 tooltip : 'This field is required'
8896 if (this.parentType === 'Navbar' && this.parent().bar) {
8897 cfg.cls += ' navbar-form';
8900 if (this.parentType === 'NavGroup') {
8901 cfg.cls += ' navbar-form';
8909 * return the real input element.
8911 inputEl: function ()
8913 return this.el.select('input.form-control',true).first();
8916 tooltipEl : function()
8918 return this.inputEl();
8921 indicatorEl : function()
8923 var indicator = this.el.select('i.roo-required-indicator',true).first();
8933 setDisabled : function(v)
8935 var i = this.inputEl().dom;
8937 i.removeAttribute('disabled');
8941 i.setAttribute('disabled','true');
8943 initEvents : function()
8946 this.inputEl().on("keydown" , this.fireKey, this);
8947 this.inputEl().on("focus", this.onFocus, this);
8948 this.inputEl().on("blur", this.onBlur, this);
8950 this.inputEl().relayEvent('keyup', this);
8952 this.indicator = this.indicatorEl();
8955 this.indicator.addClass('invisible');
8959 // reference to original value for reset
8960 this.originalValue = this.getValue();
8961 //Roo.form.TextField.superclass.initEvents.call(this);
8962 if(this.validationEvent == 'keyup'){
8963 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8964 this.inputEl().on('keyup', this.filterValidation, this);
8966 else if(this.validationEvent !== false){
8967 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8970 if(this.selectOnFocus){
8971 this.on("focus", this.preFocus, this);
8974 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8975 this.inputEl().on("keypress", this.filterKeys, this);
8977 this.inputEl().relayEvent('keypress', this);
8980 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8981 this.el.on("click", this.autoSize, this);
8984 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8985 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8988 if (typeof(this.before) == 'object') {
8989 this.before.render(this.el.select('.roo-input-before',true).first());
8991 if (typeof(this.after) == 'object') {
8992 this.after.render(this.el.select('.roo-input-after',true).first());
8997 filterValidation : function(e){
8998 if(!e.isNavKeyPress()){
8999 this.validationTask.delay(this.validationDelay);
9003 * Validates the field value
9004 * @return {Boolean} True if the value is valid, else false
9006 validate : function(){
9007 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9008 if(this.disabled || this.validateValue(this.getRawValue())){
9019 * Validates a value according to the field's validation rules and marks the field as invalid
9020 * if the validation fails
9021 * @param {Mixed} value The value to validate
9022 * @return {Boolean} True if the value is valid, else false
9024 validateValue : function(value){
9025 if(value.length < 1) { // if it's blank
9026 if(this.allowBlank){
9029 return this.inputEl().hasClass('hide') ? true : false;
9032 if(value.length < this.minLength){
9035 if(value.length > this.maxLength){
9039 var vt = Roo.form.VTypes;
9040 if(!vt[this.vtype](value, this)){
9044 if(typeof this.validator == "function"){
9045 var msg = this.validator(value);
9049 if (typeof(msg) == 'string') {
9050 this.invalidText = msg;
9054 if(this.regex && !this.regex.test(value)){
9064 fireKey : function(e){
9065 //Roo.log('field ' + e.getKey());
9066 if(e.isNavKeyPress()){
9067 this.fireEvent("specialkey", this, e);
9070 focus : function (selectText){
9072 this.inputEl().focus();
9073 if(selectText === true){
9074 this.inputEl().dom.select();
9080 onFocus : function(){
9081 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9082 // this.el.addClass(this.focusClass);
9085 this.hasFocus = true;
9086 this.startValue = this.getValue();
9087 this.fireEvent("focus", this);
9091 beforeBlur : Roo.emptyFn,
9095 onBlur : function(){
9097 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9098 //this.el.removeClass(this.focusClass);
9100 this.hasFocus = false;
9101 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9104 var v = this.getValue();
9105 if(String(v) !== String(this.startValue)){
9106 this.fireEvent('change', this, v, this.startValue);
9108 this.fireEvent("blur", this);
9112 * Resets the current field value to the originally loaded value and clears any validation messages
9115 this.setValue(this.originalValue);
9119 * Returns the name of the field
9120 * @return {Mixed} name The name field
9122 getName: function(){
9126 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9127 * @return {Mixed} value The field value
9129 getValue : function(){
9131 var v = this.inputEl().getValue();
9136 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9137 * @return {Mixed} value The field value
9139 getRawValue : function(){
9140 var v = this.inputEl().getValue();
9146 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9147 * @param {Mixed} value The value to set
9149 setRawValue : function(v){
9150 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9153 selectText : function(start, end){
9154 var v = this.getRawValue();
9156 start = start === undefined ? 0 : start;
9157 end = end === undefined ? v.length : end;
9158 var d = this.inputEl().dom;
9159 if(d.setSelectionRange){
9160 d.setSelectionRange(start, end);
9161 }else if(d.createTextRange){
9162 var range = d.createTextRange();
9163 range.moveStart("character", start);
9164 range.moveEnd("character", v.length-end);
9171 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9172 * @param {Mixed} value The value to set
9174 setValue : function(v){
9177 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9183 processValue : function(value){
9184 if(this.stripCharsRe){
9185 var newValue = value.replace(this.stripCharsRe, '');
9186 if(newValue !== value){
9187 this.setRawValue(newValue);
9194 preFocus : function(){
9196 if(this.selectOnFocus){
9197 this.inputEl().dom.select();
9200 filterKeys : function(e){
9202 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9205 var c = e.getCharCode(), cc = String.fromCharCode(c);
9206 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9209 if(!this.maskRe.test(cc)){
9214 * Clear any invalid styles/messages for this field
9216 clearInvalid : function(){
9218 if(!this.el || this.preventMark){ // not rendered
9223 this.el.removeClass(this.invalidClass);
9225 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9227 var feedback = this.el.select('.form-control-feedback', true).first();
9230 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9235 this.fireEvent('valid', this);
9239 * Mark this field as valid
9241 markValid : function()
9243 if(!this.el || this.preventMark){ // not rendered...
9247 this.el.removeClass([this.invalidClass, this.validClass]);
9249 var feedback = this.el.select('.form-control-feedback', true).first();
9252 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9259 if(this.allowBlank && !this.getRawValue().length){
9264 this.indicator.removeClass('visible');
9265 this.indicator.addClass('invisible');
9268 this.el.addClass(this.validClass);
9270 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9272 var feedback = this.el.select('.form-control-feedback', true).first();
9275 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9276 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9281 this.fireEvent('valid', this);
9285 * Mark this field as invalid
9286 * @param {String} msg The validation message
9288 markInvalid : function(msg)
9290 if(!this.el || this.preventMark){ // not rendered
9294 this.el.removeClass([this.invalidClass, this.validClass]);
9296 var feedback = this.el.select('.form-control-feedback', true).first();
9299 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9306 if(this.allowBlank && !this.getRawValue().length){
9311 this.indicator.removeClass('invisible');
9312 this.indicator.addClass('visible');
9315 this.el.addClass(this.invalidClass);
9317 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9319 var feedback = this.el.select('.form-control-feedback', true).first();
9322 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9324 if(this.getValue().length || this.forceFeedback){
9325 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9332 this.fireEvent('invalid', this, msg);
9335 SafariOnKeyDown : function(event)
9337 // this is a workaround for a password hang bug on chrome/ webkit.
9338 if (this.inputEl().dom.type != 'password') {
9342 var isSelectAll = false;
9344 if(this.inputEl().dom.selectionEnd > 0){
9345 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9347 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9348 event.preventDefault();
9353 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9355 event.preventDefault();
9356 // this is very hacky as keydown always get's upper case.
9358 var cc = String.fromCharCode(event.getCharCode());
9359 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9363 adjustWidth : function(tag, w){
9364 tag = tag.toLowerCase();
9365 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9366 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9370 if(tag == 'textarea'){
9373 }else if(Roo.isOpera){
9377 if(tag == 'textarea'){
9385 setFieldLabel : function(v)
9392 var ar = this.el.select('label > span',true);
9394 if (ar.elements.length) {
9395 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9396 this.fieldLabel = v;
9400 var br = this.el.select('label',true);
9402 if(br.elements.length) {
9403 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9404 this.fieldLabel = v;
9408 Roo.log('Cannot Found any of label > span || label in input');
9412 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9413 this.fieldLabel = v;
9428 * @class Roo.bootstrap.TextArea
9429 * @extends Roo.bootstrap.Input
9430 * Bootstrap TextArea class
9431 * @cfg {Number} cols Specifies the visible width of a text area
9432 * @cfg {Number} rows Specifies the visible number of lines in a text area
9433 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9434 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9435 * @cfg {string} html text
9438 * Create a new TextArea
9439 * @param {Object} config The config object
9442 Roo.bootstrap.TextArea = function(config){
9443 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9447 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9457 getAutoCreate : function(){
9459 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9465 if(this.inputType != 'hidden'){
9466 cfg.cls = 'form-group' //input-group
9474 value : this.value || '',
9475 html: this.html || '',
9476 cls : 'form-control',
9477 placeholder : this.placeholder || ''
9481 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9482 input.maxLength = this.maxLength;
9486 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9490 input.cols = this.cols;
9493 if (this.readOnly) {
9494 input.readonly = true;
9498 input.name = this.name;
9502 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9506 ['xs','sm','md','lg'].map(function(size){
9507 if (settings[size]) {
9508 cfg.cls += ' col-' + size + '-' + settings[size];
9512 var inputblock = input;
9514 if(this.hasFeedback && !this.allowBlank){
9518 cls: 'glyphicon form-control-feedback'
9522 cls : 'has-feedback',
9531 if (this.before || this.after) {
9534 cls : 'input-group',
9538 inputblock.cn.push({
9540 cls : 'input-group-addon',
9545 inputblock.cn.push(input);
9547 if(this.hasFeedback && !this.allowBlank){
9548 inputblock.cls += ' has-feedback';
9549 inputblock.cn.push(feedback);
9553 inputblock.cn.push({
9555 cls : 'input-group-addon',
9562 if (align ==='left' && this.fieldLabel.length) {
9567 cls : 'control-label',
9568 html : this.fieldLabel
9579 if(this.labelWidth > 12){
9580 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9583 if(this.labelWidth < 13 && this.labelmd == 0){
9584 this.labelmd = this.labelWidth;
9587 if(this.labellg > 0){
9588 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9589 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9592 if(this.labelmd > 0){
9593 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9594 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9597 if(this.labelsm > 0){
9598 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9599 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9602 if(this.labelxs > 0){
9603 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9604 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9607 } else if ( this.fieldLabel.length) {
9612 //cls : 'input-group-addon',
9613 html : this.fieldLabel
9631 if (this.disabled) {
9632 input.disabled=true;
9639 * return the real textarea element.
9641 inputEl: function ()
9643 return this.el.select('textarea.form-control',true).first();
9647 * Clear any invalid styles/messages for this field
9649 clearInvalid : function()
9652 if(!this.el || this.preventMark){ // not rendered
9656 var label = this.el.select('label', true).first();
9657 var icon = this.el.select('i.fa-star', true).first();
9663 this.el.removeClass(this.invalidClass);
9665 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9667 var feedback = this.el.select('.form-control-feedback', true).first();
9670 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9675 this.fireEvent('valid', this);
9679 * Mark this field as valid
9681 markValid : function()
9683 if(!this.el || this.preventMark){ // not rendered
9687 this.el.removeClass([this.invalidClass, this.validClass]);
9689 var feedback = this.el.select('.form-control-feedback', true).first();
9692 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9695 if(this.disabled || this.allowBlank){
9699 var label = this.el.select('label', true).first();
9700 var icon = this.el.select('i.fa-star', true).first();
9706 this.el.addClass(this.validClass);
9708 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9710 var feedback = this.el.select('.form-control-feedback', true).first();
9713 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9714 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9719 this.fireEvent('valid', this);
9723 * Mark this field as invalid
9724 * @param {String} msg The validation message
9726 markInvalid : function(msg)
9728 if(!this.el || this.preventMark){ // not rendered
9732 this.el.removeClass([this.invalidClass, this.validClass]);
9734 var feedback = this.el.select('.form-control-feedback', true).first();
9737 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9740 if(this.disabled || this.allowBlank){
9744 var label = this.el.select('label', true).first();
9745 var icon = this.el.select('i.fa-star', true).first();
9747 if(!this.getValue().length && label && !icon){
9748 this.el.createChild({
9750 cls : 'text-danger fa fa-lg fa-star',
9751 tooltip : 'This field is required',
9752 style : 'margin-right:5px;'
9756 this.el.addClass(this.invalidClass);
9758 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9760 var feedback = this.el.select('.form-control-feedback', true).first();
9763 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9765 if(this.getValue().length || this.forceFeedback){
9766 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9773 this.fireEvent('invalid', this, msg);
9781 * trigger field - base class for combo..
9786 * @class Roo.bootstrap.TriggerField
9787 * @extends Roo.bootstrap.Input
9788 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9789 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9790 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9791 * for which you can provide a custom implementation. For example:
9793 var trigger = new Roo.bootstrap.TriggerField();
9794 trigger.onTriggerClick = myTriggerFn;
9795 trigger.applyTo('my-field');
9798 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9799 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9800 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9801 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9802 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9805 * Create a new TriggerField.
9806 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9807 * to the base TextField)
9809 Roo.bootstrap.TriggerField = function(config){
9810 this.mimicing = false;
9811 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9814 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9816 * @cfg {String} triggerClass A CSS class to apply to the trigger
9819 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9824 * @cfg {Boolean} removable (true|false) special filter default false
9828 /** @cfg {Boolean} grow @hide */
9829 /** @cfg {Number} growMin @hide */
9830 /** @cfg {Number} growMax @hide */
9836 autoSize: Roo.emptyFn,
9843 actionMode : 'wrap',
9848 getAutoCreate : function(){
9850 var align = this.labelAlign || this.parentLabelAlign();
9855 cls: 'form-group' //input-group
9862 type : this.inputType,
9863 cls : 'form-control',
9864 autocomplete: 'new-password',
9865 placeholder : this.placeholder || ''
9869 input.name = this.name;
9872 input.cls += ' input-' + this.size;
9875 if (this.disabled) {
9876 input.disabled=true;
9879 var inputblock = input;
9881 if(this.hasFeedback && !this.allowBlank){
9885 cls: 'glyphicon form-control-feedback'
9888 if(this.removable && !this.editable && !this.tickable){
9890 cls : 'has-feedback',
9896 cls : 'roo-combo-removable-btn close'
9903 cls : 'has-feedback',
9912 if(this.removable && !this.editable && !this.tickable){
9914 cls : 'roo-removable',
9920 cls : 'roo-combo-removable-btn close'
9927 if (this.before || this.after) {
9930 cls : 'input-group',
9934 inputblock.cn.push({
9936 cls : 'input-group-addon',
9941 inputblock.cn.push(input);
9943 if(this.hasFeedback && !this.allowBlank){
9944 inputblock.cls += ' has-feedback';
9945 inputblock.cn.push(feedback);
9949 inputblock.cn.push({
9951 cls : 'input-group-addon',
9964 cls: 'form-hidden-field'
9978 cls: 'form-hidden-field'
9982 cls: 'roo-select2-choices',
9986 cls: 'roo-select2-search-field',
9999 cls: 'roo-select2-container input-group',
10004 // cls: 'typeahead typeahead-long dropdown-menu',
10005 // style: 'display:none'
10010 if(!this.multiple && this.showToggleBtn){
10016 if (this.caret != false) {
10019 cls: 'fa fa-' + this.caret
10026 cls : 'input-group-addon btn dropdown-toggle',
10031 cls: 'combobox-clear',
10045 combobox.cls += ' roo-select2-container-multi';
10048 if (align ==='left' && this.fieldLabel.length) {
10050 cfg.cls += ' roo-form-group-label-left';
10055 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10056 tooltip : 'This field is required'
10061 cls : 'control-label',
10062 html : this.fieldLabel
10074 var labelCfg = cfg.cn[1];
10075 var contentCfg = cfg.cn[2];
10077 if(this.indicatorpos == 'right'){
10082 cls : 'control-label',
10086 html : this.fieldLabel
10090 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10091 tooltip : 'This field is required'
10104 labelCfg = cfg.cn[0];
10105 contentCfg = cfg.cn[1];
10108 if(this.labelWidth > 12){
10109 labelCfg.style = "width: " + this.labelWidth + 'px';
10112 if(this.labelWidth < 13 && this.labelmd == 0){
10113 this.labelmd = this.labelWidth;
10116 if(this.labellg > 0){
10117 labelCfg.cls += ' col-lg-' + this.labellg;
10118 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10121 if(this.labelmd > 0){
10122 labelCfg.cls += ' col-md-' + this.labelmd;
10123 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10126 if(this.labelsm > 0){
10127 labelCfg.cls += ' col-sm-' + this.labelsm;
10128 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10131 if(this.labelxs > 0){
10132 labelCfg.cls += ' col-xs-' + this.labelxs;
10133 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10136 } else if ( this.fieldLabel.length) {
10137 // Roo.log(" label");
10141 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10142 tooltip : 'This field is required'
10146 //cls : 'input-group-addon',
10147 html : this.fieldLabel
10155 if(this.indicatorpos == 'right'){
10163 html : this.fieldLabel
10167 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10168 tooltip : 'This field is required'
10181 // Roo.log(" no label && no align");
10188 ['xs','sm','md','lg'].map(function(size){
10189 if (settings[size]) {
10190 cfg.cls += ' col-' + size + '-' + settings[size];
10201 onResize : function(w, h){
10202 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10203 // if(typeof w == 'number'){
10204 // var x = w - this.trigger.getWidth();
10205 // this.inputEl().setWidth(this.adjustWidth('input', x));
10206 // this.trigger.setStyle('left', x+'px');
10211 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10214 getResizeEl : function(){
10215 return this.inputEl();
10219 getPositionEl : function(){
10220 return this.inputEl();
10224 alignErrorIcon : function(){
10225 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10229 initEvents : function(){
10233 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10234 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10235 if(!this.multiple && this.showToggleBtn){
10236 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10237 if(this.hideTrigger){
10238 this.trigger.setDisplayed(false);
10240 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10244 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10247 if(this.removable && !this.editable && !this.tickable){
10248 var close = this.closeTriggerEl();
10251 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10252 close.on('click', this.removeBtnClick, this, close);
10256 //this.trigger.addClassOnOver('x-form-trigger-over');
10257 //this.trigger.addClassOnClick('x-form-trigger-click');
10260 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10264 closeTriggerEl : function()
10266 var close = this.el.select('.roo-combo-removable-btn', true).first();
10267 return close ? close : false;
10270 removeBtnClick : function(e, h, el)
10272 e.preventDefault();
10274 if(this.fireEvent("remove", this) !== false){
10276 this.fireEvent("afterremove", this)
10280 createList : function()
10282 this.list = Roo.get(document.body).createChild({
10284 cls: 'typeahead typeahead-long dropdown-menu',
10285 style: 'display:none'
10288 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10293 initTrigger : function(){
10298 onDestroy : function(){
10300 this.trigger.removeAllListeners();
10301 // this.trigger.remove();
10304 // this.wrap.remove();
10306 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10310 onFocus : function(){
10311 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10313 if(!this.mimicing){
10314 this.wrap.addClass('x-trigger-wrap-focus');
10315 this.mimicing = true;
10316 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10317 if(this.monitorTab){
10318 this.el.on("keydown", this.checkTab, this);
10325 checkTab : function(e){
10326 if(e.getKey() == e.TAB){
10327 this.triggerBlur();
10332 onBlur : function(){
10337 mimicBlur : function(e, t){
10339 if(!this.wrap.contains(t) && this.validateBlur()){
10340 this.triggerBlur();
10346 triggerBlur : function(){
10347 this.mimicing = false;
10348 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10349 if(this.monitorTab){
10350 this.el.un("keydown", this.checkTab, this);
10352 //this.wrap.removeClass('x-trigger-wrap-focus');
10353 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10357 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10358 validateBlur : function(e, t){
10363 onDisable : function(){
10364 this.inputEl().dom.disabled = true;
10365 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10367 // this.wrap.addClass('x-item-disabled');
10372 onEnable : function(){
10373 this.inputEl().dom.disabled = false;
10374 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10376 // this.el.removeClass('x-item-disabled');
10381 onShow : function(){
10382 var ae = this.getActionEl();
10385 ae.dom.style.display = '';
10386 ae.dom.style.visibility = 'visible';
10392 onHide : function(){
10393 var ae = this.getActionEl();
10394 ae.dom.style.display = 'none';
10398 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10399 * by an implementing function.
10401 * @param {EventObject} e
10403 onTriggerClick : Roo.emptyFn
10407 * Ext JS Library 1.1.1
10408 * Copyright(c) 2006-2007, Ext JS, LLC.
10410 * Originally Released Under LGPL - original licence link has changed is not relivant.
10413 * <script type="text/javascript">
10418 * @class Roo.data.SortTypes
10420 * Defines the default sorting (casting?) comparison functions used when sorting data.
10422 Roo.data.SortTypes = {
10424 * Default sort that does nothing
10425 * @param {Mixed} s The value being converted
10426 * @return {Mixed} The comparison value
10428 none : function(s){
10433 * The regular expression used to strip tags
10437 stripTagsRE : /<\/?[^>]+>/gi,
10440 * Strips all HTML tags to sort on text only
10441 * @param {Mixed} s The value being converted
10442 * @return {String} The comparison value
10444 asText : function(s){
10445 return String(s).replace(this.stripTagsRE, "");
10449 * Strips all HTML tags to sort on text only - Case insensitive
10450 * @param {Mixed} s The value being converted
10451 * @return {String} The comparison value
10453 asUCText : function(s){
10454 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10458 * Case insensitive string
10459 * @param {Mixed} s The value being converted
10460 * @return {String} The comparison value
10462 asUCString : function(s) {
10463 return String(s).toUpperCase();
10468 * @param {Mixed} s The value being converted
10469 * @return {Number} The comparison value
10471 asDate : function(s) {
10475 if(s instanceof Date){
10476 return s.getTime();
10478 return Date.parse(String(s));
10483 * @param {Mixed} s The value being converted
10484 * @return {Float} The comparison value
10486 asFloat : function(s) {
10487 var val = parseFloat(String(s).replace(/,/g, ""));
10496 * @param {Mixed} s The value being converted
10497 * @return {Number} The comparison value
10499 asInt : function(s) {
10500 var val = parseInt(String(s).replace(/,/g, ""));
10508 * Ext JS Library 1.1.1
10509 * Copyright(c) 2006-2007, Ext JS, LLC.
10511 * Originally Released Under LGPL - original licence link has changed is not relivant.
10514 * <script type="text/javascript">
10518 * @class Roo.data.Record
10519 * Instances of this class encapsulate both record <em>definition</em> information, and record
10520 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10521 * to access Records cached in an {@link Roo.data.Store} object.<br>
10523 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10524 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10527 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10529 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10530 * {@link #create}. The parameters are the same.
10531 * @param {Array} data An associative Array of data values keyed by the field name.
10532 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10533 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10534 * not specified an integer id is generated.
10536 Roo.data.Record = function(data, id){
10537 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10542 * Generate a constructor for a specific record layout.
10543 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10544 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10545 * Each field definition object may contain the following properties: <ul>
10546 * <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,
10547 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10548 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10549 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10550 * is being used, then this is a string containing the javascript expression to reference the data relative to
10551 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10552 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10553 * this may be omitted.</p></li>
10554 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10555 * <ul><li>auto (Default, implies no conversion)</li>
10560 * <li>date</li></ul></p></li>
10561 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10562 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10563 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10564 * by the Reader into an object that will be stored in the Record. It is passed the
10565 * following parameters:<ul>
10566 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10568 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10570 * <br>usage:<br><pre><code>
10571 var TopicRecord = Roo.data.Record.create(
10572 {name: 'title', mapping: 'topic_title'},
10573 {name: 'author', mapping: 'username'},
10574 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10575 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10576 {name: 'lastPoster', mapping: 'user2'},
10577 {name: 'excerpt', mapping: 'post_text'}
10580 var myNewRecord = new TopicRecord({
10581 title: 'Do my job please',
10584 lastPost: new Date(),
10585 lastPoster: 'Animal',
10586 excerpt: 'No way dude!'
10588 myStore.add(myNewRecord);
10593 Roo.data.Record.create = function(o){
10594 var f = function(){
10595 f.superclass.constructor.apply(this, arguments);
10597 Roo.extend(f, Roo.data.Record);
10598 var p = f.prototype;
10599 p.fields = new Roo.util.MixedCollection(false, function(field){
10602 for(var i = 0, len = o.length; i < len; i++){
10603 p.fields.add(new Roo.data.Field(o[i]));
10605 f.getField = function(name){
10606 return p.fields.get(name);
10611 Roo.data.Record.AUTO_ID = 1000;
10612 Roo.data.Record.EDIT = 'edit';
10613 Roo.data.Record.REJECT = 'reject';
10614 Roo.data.Record.COMMIT = 'commit';
10616 Roo.data.Record.prototype = {
10618 * Readonly flag - true if this record has been modified.
10627 join : function(store){
10628 this.store = store;
10632 * Set the named field to the specified value.
10633 * @param {String} name The name of the field to set.
10634 * @param {Object} value The value to set the field to.
10636 set : function(name, value){
10637 if(this.data[name] == value){
10641 if(!this.modified){
10642 this.modified = {};
10644 if(typeof this.modified[name] == 'undefined'){
10645 this.modified[name] = this.data[name];
10647 this.data[name] = value;
10648 if(!this.editing && this.store){
10649 this.store.afterEdit(this);
10654 * Get the value of the named field.
10655 * @param {String} name The name of the field to get the value of.
10656 * @return {Object} The value of the field.
10658 get : function(name){
10659 return this.data[name];
10663 beginEdit : function(){
10664 this.editing = true;
10665 this.modified = {};
10669 cancelEdit : function(){
10670 this.editing = false;
10671 delete this.modified;
10675 endEdit : function(){
10676 this.editing = false;
10677 if(this.dirty && this.store){
10678 this.store.afterEdit(this);
10683 * Usually called by the {@link Roo.data.Store} which owns the Record.
10684 * Rejects all changes made to the Record since either creation, or the last commit operation.
10685 * Modified fields are reverted to their original values.
10687 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10688 * of reject operations.
10690 reject : function(){
10691 var m = this.modified;
10693 if(typeof m[n] != "function"){
10694 this.data[n] = m[n];
10697 this.dirty = false;
10698 delete this.modified;
10699 this.editing = false;
10701 this.store.afterReject(this);
10706 * Usually called by the {@link Roo.data.Store} which owns the Record.
10707 * Commits all changes made to the Record since either creation, or the last commit operation.
10709 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10710 * of commit operations.
10712 commit : function(){
10713 this.dirty = false;
10714 delete this.modified;
10715 this.editing = false;
10717 this.store.afterCommit(this);
10722 hasError : function(){
10723 return this.error != null;
10727 clearError : function(){
10732 * Creates a copy of this record.
10733 * @param {String} id (optional) A new record id if you don't want to use this record's id
10736 copy : function(newId) {
10737 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10741 * Ext JS Library 1.1.1
10742 * Copyright(c) 2006-2007, Ext JS, LLC.
10744 * Originally Released Under LGPL - original licence link has changed is not relivant.
10747 * <script type="text/javascript">
10753 * @class Roo.data.Store
10754 * @extends Roo.util.Observable
10755 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10756 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10758 * 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
10759 * has no knowledge of the format of the data returned by the Proxy.<br>
10761 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10762 * instances from the data object. These records are cached and made available through accessor functions.
10764 * Creates a new Store.
10765 * @param {Object} config A config object containing the objects needed for the Store to access data,
10766 * and read the data into Records.
10768 Roo.data.Store = function(config){
10769 this.data = new Roo.util.MixedCollection(false);
10770 this.data.getKey = function(o){
10773 this.baseParams = {};
10775 this.paramNames = {
10780 "multisort" : "_multisort"
10783 if(config && config.data){
10784 this.inlineData = config.data;
10785 delete config.data;
10788 Roo.apply(this, config);
10790 if(this.reader){ // reader passed
10791 this.reader = Roo.factory(this.reader, Roo.data);
10792 this.reader.xmodule = this.xmodule || false;
10793 if(!this.recordType){
10794 this.recordType = this.reader.recordType;
10796 if(this.reader.onMetaChange){
10797 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10801 if(this.recordType){
10802 this.fields = this.recordType.prototype.fields;
10804 this.modified = [];
10808 * @event datachanged
10809 * Fires when the data cache has changed, and a widget which is using this Store
10810 * as a Record cache should refresh its view.
10811 * @param {Store} this
10813 datachanged : true,
10815 * @event metachange
10816 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10817 * @param {Store} this
10818 * @param {Object} meta The JSON metadata
10823 * Fires when Records have been added to the Store
10824 * @param {Store} this
10825 * @param {Roo.data.Record[]} records The array of Records added
10826 * @param {Number} index The index at which the record(s) were added
10831 * Fires when a Record has been removed from the Store
10832 * @param {Store} this
10833 * @param {Roo.data.Record} record The Record that was removed
10834 * @param {Number} index The index at which the record was removed
10839 * Fires when a Record has been updated
10840 * @param {Store} this
10841 * @param {Roo.data.Record} record The Record that was updated
10842 * @param {String} operation The update operation being performed. Value may be one of:
10844 Roo.data.Record.EDIT
10845 Roo.data.Record.REJECT
10846 Roo.data.Record.COMMIT
10852 * Fires when the data cache has been cleared.
10853 * @param {Store} this
10857 * @event beforeload
10858 * Fires before a request is made for a new data object. If the beforeload handler returns false
10859 * the load action will be canceled.
10860 * @param {Store} this
10861 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10865 * @event beforeloadadd
10866 * Fires after a new set of Records has been loaded.
10867 * @param {Store} this
10868 * @param {Roo.data.Record[]} records The Records that were loaded
10869 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10871 beforeloadadd : true,
10874 * Fires after a new set of Records has been loaded, before they are added to the store.
10875 * @param {Store} this
10876 * @param {Roo.data.Record[]} records The Records that were loaded
10877 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10878 * @params {Object} return from reader
10882 * @event loadexception
10883 * Fires if an exception occurs in the Proxy during loading.
10884 * Called with the signature of the Proxy's "loadexception" event.
10885 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10888 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10889 * @param {Object} load options
10890 * @param {Object} jsonData from your request (normally this contains the Exception)
10892 loadexception : true
10896 this.proxy = Roo.factory(this.proxy, Roo.data);
10897 this.proxy.xmodule = this.xmodule || false;
10898 this.relayEvents(this.proxy, ["loadexception"]);
10900 this.sortToggle = {};
10901 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10903 Roo.data.Store.superclass.constructor.call(this);
10905 if(this.inlineData){
10906 this.loadData(this.inlineData);
10907 delete this.inlineData;
10911 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10913 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10914 * without a remote query - used by combo/forms at present.
10918 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10921 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10924 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10925 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10928 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10929 * on any HTTP request
10932 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10935 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10939 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10940 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10942 remoteSort : false,
10945 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10946 * loaded or when a record is removed. (defaults to false).
10948 pruneModifiedRecords : false,
10951 lastOptions : null,
10954 * Add Records to the Store and fires the add event.
10955 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10957 add : function(records){
10958 records = [].concat(records);
10959 for(var i = 0, len = records.length; i < len; i++){
10960 records[i].join(this);
10962 var index = this.data.length;
10963 this.data.addAll(records);
10964 this.fireEvent("add", this, records, index);
10968 * Remove a Record from the Store and fires the remove event.
10969 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10971 remove : function(record){
10972 var index = this.data.indexOf(record);
10973 this.data.removeAt(index);
10974 if(this.pruneModifiedRecords){
10975 this.modified.remove(record);
10977 this.fireEvent("remove", this, record, index);
10981 * Remove all Records from the Store and fires the clear event.
10983 removeAll : function(){
10985 if(this.pruneModifiedRecords){
10986 this.modified = [];
10988 this.fireEvent("clear", this);
10992 * Inserts Records to the Store at the given index and fires the add event.
10993 * @param {Number} index The start index at which to insert the passed Records.
10994 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10996 insert : function(index, records){
10997 records = [].concat(records);
10998 for(var i = 0, len = records.length; i < len; i++){
10999 this.data.insert(index, records[i]);
11000 records[i].join(this);
11002 this.fireEvent("add", this, records, index);
11006 * Get the index within the cache of the passed Record.
11007 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11008 * @return {Number} The index of the passed Record. Returns -1 if not found.
11010 indexOf : function(record){
11011 return this.data.indexOf(record);
11015 * Get the index within the cache of the Record with the passed id.
11016 * @param {String} id The id of the Record to find.
11017 * @return {Number} The index of the Record. Returns -1 if not found.
11019 indexOfId : function(id){
11020 return this.data.indexOfKey(id);
11024 * Get the Record with the specified id.
11025 * @param {String} id The id of the Record to find.
11026 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11028 getById : function(id){
11029 return this.data.key(id);
11033 * Get the Record at the specified index.
11034 * @param {Number} index The index of the Record to find.
11035 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11037 getAt : function(index){
11038 return this.data.itemAt(index);
11042 * Returns a range of Records between specified indices.
11043 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11044 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11045 * @return {Roo.data.Record[]} An array of Records
11047 getRange : function(start, end){
11048 return this.data.getRange(start, end);
11052 storeOptions : function(o){
11053 o = Roo.apply({}, o);
11056 this.lastOptions = o;
11060 * Loads the Record cache from the configured Proxy using the configured Reader.
11062 * If using remote paging, then the first load call must specify the <em>start</em>
11063 * and <em>limit</em> properties in the options.params property to establish the initial
11064 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11066 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11067 * and this call will return before the new data has been loaded. Perform any post-processing
11068 * in a callback function, or in a "load" event handler.</strong>
11070 * @param {Object} options An object containing properties which control loading options:<ul>
11071 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11072 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11073 * passed the following arguments:<ul>
11074 * <li>r : Roo.data.Record[]</li>
11075 * <li>options: Options object from the load call</li>
11076 * <li>success: Boolean success indicator</li></ul></li>
11077 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11078 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11081 load : function(options){
11082 options = options || {};
11083 if(this.fireEvent("beforeload", this, options) !== false){
11084 this.storeOptions(options);
11085 var p = Roo.apply(options.params || {}, this.baseParams);
11086 // if meta was not loaded from remote source.. try requesting it.
11087 if (!this.reader.metaFromRemote) {
11088 p._requestMeta = 1;
11090 if(this.sortInfo && this.remoteSort){
11091 var pn = this.paramNames;
11092 p[pn["sort"]] = this.sortInfo.field;
11093 p[pn["dir"]] = this.sortInfo.direction;
11095 if (this.multiSort) {
11096 var pn = this.paramNames;
11097 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11100 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11105 * Reloads the Record cache from the configured Proxy using the configured Reader and
11106 * the options from the last load operation performed.
11107 * @param {Object} options (optional) An object containing properties which may override the options
11108 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11109 * the most recently used options are reused).
11111 reload : function(options){
11112 this.load(Roo.applyIf(options||{}, this.lastOptions));
11116 // Called as a callback by the Reader during a load operation.
11117 loadRecords : function(o, options, success){
11118 if(!o || success === false){
11119 if(success !== false){
11120 this.fireEvent("load", this, [], options, o);
11122 if(options.callback){
11123 options.callback.call(options.scope || this, [], options, false);
11127 // if data returned failure - throw an exception.
11128 if (o.success === false) {
11129 // show a message if no listener is registered.
11130 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11131 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11133 // loadmask wil be hooked into this..
11134 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11137 var r = o.records, t = o.totalRecords || r.length;
11139 this.fireEvent("beforeloadadd", this, r, options, o);
11141 if(!options || options.add !== true){
11142 if(this.pruneModifiedRecords){
11143 this.modified = [];
11145 for(var i = 0, len = r.length; i < len; i++){
11149 this.data = this.snapshot;
11150 delete this.snapshot;
11153 this.data.addAll(r);
11154 this.totalLength = t;
11156 this.fireEvent("datachanged", this);
11158 this.totalLength = Math.max(t, this.data.length+r.length);
11162 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11164 var e = new Roo.data.Record({});
11166 e.set(this.parent.displayField, this.parent.emptyTitle);
11167 e.set(this.parent.valueField, '');
11172 this.fireEvent("load", this, r, options, o);
11173 if(options.callback){
11174 options.callback.call(options.scope || this, r, options, true);
11180 * Loads data from a passed data block. A Reader which understands the format of the data
11181 * must have been configured in the constructor.
11182 * @param {Object} data The data block from which to read the Records. The format of the data expected
11183 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11184 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11186 loadData : function(o, append){
11187 var r = this.reader.readRecords(o);
11188 this.loadRecords(r, {add: append}, true);
11192 * Gets the number of cached records.
11194 * <em>If using paging, this may not be the total size of the dataset. If the data object
11195 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11196 * the data set size</em>
11198 getCount : function(){
11199 return this.data.length || 0;
11203 * Gets the total number of records in the dataset as returned by the server.
11205 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11206 * the dataset size</em>
11208 getTotalCount : function(){
11209 return this.totalLength || 0;
11213 * Returns the sort state of the Store as an object with two properties:
11215 field {String} The name of the field by which the Records are sorted
11216 direction {String} The sort order, "ASC" or "DESC"
11219 getSortState : function(){
11220 return this.sortInfo;
11224 applySort : function(){
11225 if(this.sortInfo && !this.remoteSort){
11226 var s = this.sortInfo, f = s.field;
11227 var st = this.fields.get(f).sortType;
11228 var fn = function(r1, r2){
11229 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11230 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11232 this.data.sort(s.direction, fn);
11233 if(this.snapshot && this.snapshot != this.data){
11234 this.snapshot.sort(s.direction, fn);
11240 * Sets the default sort column and order to be used by the next load operation.
11241 * @param {String} fieldName The name of the field to sort by.
11242 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11244 setDefaultSort : function(field, dir){
11245 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11249 * Sort the Records.
11250 * If remote sorting is used, the sort is performed on the server, and the cache is
11251 * reloaded. If local sorting is used, the cache is sorted internally.
11252 * @param {String} fieldName The name of the field to sort by.
11253 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11255 sort : function(fieldName, dir){
11256 var f = this.fields.get(fieldName);
11258 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11260 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11261 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11266 this.sortToggle[f.name] = dir;
11267 this.sortInfo = {field: f.name, direction: dir};
11268 if(!this.remoteSort){
11270 this.fireEvent("datachanged", this);
11272 this.load(this.lastOptions);
11277 * Calls the specified function for each of the Records in the cache.
11278 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11279 * Returning <em>false</em> aborts and exits the iteration.
11280 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11282 each : function(fn, scope){
11283 this.data.each(fn, scope);
11287 * Gets all records modified since the last commit. Modified records are persisted across load operations
11288 * (e.g., during paging).
11289 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11291 getModifiedRecords : function(){
11292 return this.modified;
11296 createFilterFn : function(property, value, anyMatch){
11297 if(!value.exec){ // not a regex
11298 value = String(value);
11299 if(value.length == 0){
11302 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11304 return function(r){
11305 return value.test(r.data[property]);
11310 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11311 * @param {String} property A field on your records
11312 * @param {Number} start The record index to start at (defaults to 0)
11313 * @param {Number} end The last record index to include (defaults to length - 1)
11314 * @return {Number} The sum
11316 sum : function(property, start, end){
11317 var rs = this.data.items, v = 0;
11318 start = start || 0;
11319 end = (end || end === 0) ? end : rs.length-1;
11321 for(var i = start; i <= end; i++){
11322 v += (rs[i].data[property] || 0);
11328 * Filter the records by a specified property.
11329 * @param {String} field A field on your records
11330 * @param {String/RegExp} value Either a string that the field
11331 * should start with or a RegExp to test against the field
11332 * @param {Boolean} anyMatch True to match any part not just the beginning
11334 filter : function(property, value, anyMatch){
11335 var fn = this.createFilterFn(property, value, anyMatch);
11336 return fn ? this.filterBy(fn) : this.clearFilter();
11340 * Filter by a function. The specified function will be called with each
11341 * record in this data source. If the function returns true the record is included,
11342 * otherwise it is filtered.
11343 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11344 * @param {Object} scope (optional) The scope of the function (defaults to this)
11346 filterBy : function(fn, scope){
11347 this.snapshot = this.snapshot || this.data;
11348 this.data = this.queryBy(fn, scope||this);
11349 this.fireEvent("datachanged", this);
11353 * Query the records by a specified property.
11354 * @param {String} field A field on your records
11355 * @param {String/RegExp} value Either a string that the field
11356 * should start with or a RegExp to test against the field
11357 * @param {Boolean} anyMatch True to match any part not just the beginning
11358 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11360 query : function(property, value, anyMatch){
11361 var fn = this.createFilterFn(property, value, anyMatch);
11362 return fn ? this.queryBy(fn) : this.data.clone();
11366 * Query by a function. The specified function will be called with each
11367 * record in this data source. If the function returns true the record is included
11369 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11370 * @param {Object} scope (optional) The scope of the function (defaults to this)
11371 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11373 queryBy : function(fn, scope){
11374 var data = this.snapshot || this.data;
11375 return data.filterBy(fn, scope||this);
11379 * Collects unique values for a particular dataIndex from this store.
11380 * @param {String} dataIndex The property to collect
11381 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11382 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11383 * @return {Array} An array of the unique values
11385 collect : function(dataIndex, allowNull, bypassFilter){
11386 var d = (bypassFilter === true && this.snapshot) ?
11387 this.snapshot.items : this.data.items;
11388 var v, sv, r = [], l = {};
11389 for(var i = 0, len = d.length; i < len; i++){
11390 v = d[i].data[dataIndex];
11392 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11401 * Revert to a view of the Record cache with no filtering applied.
11402 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11404 clearFilter : function(suppressEvent){
11405 if(this.snapshot && this.snapshot != this.data){
11406 this.data = this.snapshot;
11407 delete this.snapshot;
11408 if(suppressEvent !== true){
11409 this.fireEvent("datachanged", this);
11415 afterEdit : function(record){
11416 if(this.modified.indexOf(record) == -1){
11417 this.modified.push(record);
11419 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11423 afterReject : function(record){
11424 this.modified.remove(record);
11425 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11429 afterCommit : function(record){
11430 this.modified.remove(record);
11431 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11435 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11436 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11438 commitChanges : function(){
11439 var m = this.modified.slice(0);
11440 this.modified = [];
11441 for(var i = 0, len = m.length; i < len; i++){
11447 * Cancel outstanding changes on all changed records.
11449 rejectChanges : function(){
11450 var m = this.modified.slice(0);
11451 this.modified = [];
11452 for(var i = 0, len = m.length; i < len; i++){
11457 onMetaChange : function(meta, rtype, o){
11458 this.recordType = rtype;
11459 this.fields = rtype.prototype.fields;
11460 delete this.snapshot;
11461 this.sortInfo = meta.sortInfo || this.sortInfo;
11462 this.modified = [];
11463 this.fireEvent('metachange', this, this.reader.meta);
11466 moveIndex : function(data, type)
11468 var index = this.indexOf(data);
11470 var newIndex = index + type;
11474 this.insert(newIndex, data);
11479 * Ext JS Library 1.1.1
11480 * Copyright(c) 2006-2007, Ext JS, LLC.
11482 * Originally Released Under LGPL - original licence link has changed is not relivant.
11485 * <script type="text/javascript">
11489 * @class Roo.data.SimpleStore
11490 * @extends Roo.data.Store
11491 * Small helper class to make creating Stores from Array data easier.
11492 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11493 * @cfg {Array} fields An array of field definition objects, or field name strings.
11494 * @cfg {Array} data The multi-dimensional array of data
11496 * @param {Object} config
11498 Roo.data.SimpleStore = function(config){
11499 Roo.data.SimpleStore.superclass.constructor.call(this, {
11501 reader: new Roo.data.ArrayReader({
11504 Roo.data.Record.create(config.fields)
11506 proxy : new Roo.data.MemoryProxy(config.data)
11510 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11512 * Ext JS Library 1.1.1
11513 * Copyright(c) 2006-2007, Ext JS, LLC.
11515 * Originally Released Under LGPL - original licence link has changed is not relivant.
11518 * <script type="text/javascript">
11523 * @extends Roo.data.Store
11524 * @class Roo.data.JsonStore
11525 * Small helper class to make creating Stores for JSON data easier. <br/>
11527 var store = new Roo.data.JsonStore({
11528 url: 'get-images.php',
11530 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11533 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11534 * JsonReader and HttpProxy (unless inline data is provided).</b>
11535 * @cfg {Array} fields An array of field definition objects, or field name strings.
11537 * @param {Object} config
11539 Roo.data.JsonStore = function(c){
11540 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11541 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11542 reader: new Roo.data.JsonReader(c, c.fields)
11545 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11547 * Ext JS Library 1.1.1
11548 * Copyright(c) 2006-2007, Ext JS, LLC.
11550 * Originally Released Under LGPL - original licence link has changed is not relivant.
11553 * <script type="text/javascript">
11557 Roo.data.Field = function(config){
11558 if(typeof config == "string"){
11559 config = {name: config};
11561 Roo.apply(this, config);
11564 this.type = "auto";
11567 var st = Roo.data.SortTypes;
11568 // named sortTypes are supported, here we look them up
11569 if(typeof this.sortType == "string"){
11570 this.sortType = st[this.sortType];
11573 // set default sortType for strings and dates
11574 if(!this.sortType){
11577 this.sortType = st.asUCString;
11580 this.sortType = st.asDate;
11583 this.sortType = st.none;
11588 var stripRe = /[\$,%]/g;
11590 // prebuilt conversion function for this field, instead of
11591 // switching every time we're reading a value
11593 var cv, dateFormat = this.dateFormat;
11598 cv = function(v){ return v; };
11601 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11605 return v !== undefined && v !== null && v !== '' ?
11606 parseInt(String(v).replace(stripRe, ""), 10) : '';
11611 return v !== undefined && v !== null && v !== '' ?
11612 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11617 cv = function(v){ return v === true || v === "true" || v == 1; };
11624 if(v instanceof Date){
11628 if(dateFormat == "timestamp"){
11629 return new Date(v*1000);
11631 return Date.parseDate(v, dateFormat);
11633 var parsed = Date.parse(v);
11634 return parsed ? new Date(parsed) : null;
11643 Roo.data.Field.prototype = {
11651 * Ext JS Library 1.1.1
11652 * Copyright(c) 2006-2007, Ext JS, LLC.
11654 * Originally Released Under LGPL - original licence link has changed is not relivant.
11657 * <script type="text/javascript">
11660 // Base class for reading structured data from a data source. This class is intended to be
11661 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11664 * @class Roo.data.DataReader
11665 * Base class for reading structured data from a data source. This class is intended to be
11666 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11669 Roo.data.DataReader = function(meta, recordType){
11673 this.recordType = recordType instanceof Array ?
11674 Roo.data.Record.create(recordType) : recordType;
11677 Roo.data.DataReader.prototype = {
11679 * Create an empty record
11680 * @param {Object} data (optional) - overlay some values
11681 * @return {Roo.data.Record} record created.
11683 newRow : function(d) {
11685 this.recordType.prototype.fields.each(function(c) {
11687 case 'int' : da[c.name] = 0; break;
11688 case 'date' : da[c.name] = new Date(); break;
11689 case 'float' : da[c.name] = 0.0; break;
11690 case 'boolean' : da[c.name] = false; break;
11691 default : da[c.name] = ""; break;
11695 return new this.recordType(Roo.apply(da, d));
11700 * Ext JS Library 1.1.1
11701 * Copyright(c) 2006-2007, Ext JS, LLC.
11703 * Originally Released Under LGPL - original licence link has changed is not relivant.
11706 * <script type="text/javascript">
11710 * @class Roo.data.DataProxy
11711 * @extends Roo.data.Observable
11712 * This class is an abstract base class for implementations which provide retrieval of
11713 * unformatted data objects.<br>
11715 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11716 * (of the appropriate type which knows how to parse the data object) to provide a block of
11717 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11719 * Custom implementations must implement the load method as described in
11720 * {@link Roo.data.HttpProxy#load}.
11722 Roo.data.DataProxy = function(){
11725 * @event beforeload
11726 * Fires before a network request is made to retrieve a data object.
11727 * @param {Object} This DataProxy object.
11728 * @param {Object} params The params parameter to the load function.
11733 * Fires before the load method's callback is called.
11734 * @param {Object} This DataProxy object.
11735 * @param {Object} o The data object.
11736 * @param {Object} arg The callback argument object passed to the load function.
11740 * @event loadexception
11741 * Fires if an Exception occurs during data retrieval.
11742 * @param {Object} This DataProxy object.
11743 * @param {Object} o The data object.
11744 * @param {Object} arg The callback argument object passed to the load function.
11745 * @param {Object} e The Exception.
11747 loadexception : true
11749 Roo.data.DataProxy.superclass.constructor.call(this);
11752 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11755 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11759 * Ext JS Library 1.1.1
11760 * Copyright(c) 2006-2007, Ext JS, LLC.
11762 * Originally Released Under LGPL - original licence link has changed is not relivant.
11765 * <script type="text/javascript">
11768 * @class Roo.data.MemoryProxy
11769 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11770 * to the Reader when its load method is called.
11772 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11774 Roo.data.MemoryProxy = function(data){
11778 Roo.data.MemoryProxy.superclass.constructor.call(this);
11782 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11785 * Load data from the requested source (in this case an in-memory
11786 * data object passed to the constructor), read the data object into
11787 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11788 * process that block using the passed callback.
11789 * @param {Object} params This parameter is not used by the MemoryProxy class.
11790 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11791 * object into a block of Roo.data.Records.
11792 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11793 * The function must be passed <ul>
11794 * <li>The Record block object</li>
11795 * <li>The "arg" argument from the load function</li>
11796 * <li>A boolean success indicator</li>
11798 * @param {Object} scope The scope in which to call the callback
11799 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11801 load : function(params, reader, callback, scope, arg){
11802 params = params || {};
11805 result = reader.readRecords(this.data);
11807 this.fireEvent("loadexception", this, arg, null, e);
11808 callback.call(scope, null, arg, false);
11811 callback.call(scope, result, arg, true);
11815 update : function(params, records){
11820 * Ext JS Library 1.1.1
11821 * Copyright(c) 2006-2007, Ext JS, LLC.
11823 * Originally Released Under LGPL - original licence link has changed is not relivant.
11826 * <script type="text/javascript">
11829 * @class Roo.data.HttpProxy
11830 * @extends Roo.data.DataProxy
11831 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11832 * configured to reference a certain URL.<br><br>
11834 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11835 * from which the running page was served.<br><br>
11837 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11839 * Be aware that to enable the browser to parse an XML document, the server must set
11840 * the Content-Type header in the HTTP response to "text/xml".
11842 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11843 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11844 * will be used to make the request.
11846 Roo.data.HttpProxy = function(conn){
11847 Roo.data.HttpProxy.superclass.constructor.call(this);
11848 // is conn a conn config or a real conn?
11850 this.useAjax = !conn || !conn.events;
11854 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11855 // thse are take from connection...
11858 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11861 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11862 * extra parameters to each request made by this object. (defaults to undefined)
11865 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11866 * to each request made by this object. (defaults to undefined)
11869 * @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)
11872 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11875 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11881 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11885 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11886 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11887 * a finer-grained basis than the DataProxy events.
11889 getConnection : function(){
11890 return this.useAjax ? Roo.Ajax : this.conn;
11894 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11895 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11896 * process that block using the passed callback.
11897 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11898 * for the request to the remote server.
11899 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11900 * object into a block of Roo.data.Records.
11901 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11902 * The function must be passed <ul>
11903 * <li>The Record block object</li>
11904 * <li>The "arg" argument from the load function</li>
11905 * <li>A boolean success indicator</li>
11907 * @param {Object} scope The scope in which to call the callback
11908 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11910 load : function(params, reader, callback, scope, arg){
11911 if(this.fireEvent("beforeload", this, params) !== false){
11913 params : params || {},
11915 callback : callback,
11920 callback : this.loadResponse,
11924 Roo.applyIf(o, this.conn);
11925 if(this.activeRequest){
11926 Roo.Ajax.abort(this.activeRequest);
11928 this.activeRequest = Roo.Ajax.request(o);
11930 this.conn.request(o);
11933 callback.call(scope||this, null, arg, false);
11938 loadResponse : function(o, success, response){
11939 delete this.activeRequest;
11941 this.fireEvent("loadexception", this, o, response);
11942 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11947 result = o.reader.read(response);
11949 this.fireEvent("loadexception", this, o, response, e);
11950 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11954 this.fireEvent("load", this, o, o.request.arg);
11955 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11959 update : function(dataSet){
11964 updateResponse : function(dataSet){
11969 * Ext JS Library 1.1.1
11970 * Copyright(c) 2006-2007, Ext JS, LLC.
11972 * Originally Released Under LGPL - original licence link has changed is not relivant.
11975 * <script type="text/javascript">
11979 * @class Roo.data.ScriptTagProxy
11980 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11981 * other than the originating domain of the running page.<br><br>
11983 * <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
11984 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11986 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11987 * source code that is used as the source inside a <script> tag.<br><br>
11989 * In order for the browser to process the returned data, the server must wrap the data object
11990 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11991 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11992 * depending on whether the callback name was passed:
11995 boolean scriptTag = false;
11996 String cb = request.getParameter("callback");
11999 response.setContentType("text/javascript");
12001 response.setContentType("application/x-json");
12003 Writer out = response.getWriter();
12005 out.write(cb + "(");
12007 out.print(dataBlock.toJsonString());
12014 * @param {Object} config A configuration object.
12016 Roo.data.ScriptTagProxy = function(config){
12017 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12018 Roo.apply(this, config);
12019 this.head = document.getElementsByTagName("head")[0];
12022 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12024 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12026 * @cfg {String} url The URL from which to request the data object.
12029 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12033 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12034 * the server the name of the callback function set up by the load call to process the returned data object.
12035 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12036 * javascript output which calls this named function passing the data object as its only parameter.
12038 callbackParam : "callback",
12040 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12041 * name to the request.
12046 * Load data from the configured URL, read the data object into
12047 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12048 * process that block using the passed callback.
12049 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12050 * for the request to the remote server.
12051 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12052 * object into a block of Roo.data.Records.
12053 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12054 * The function must be passed <ul>
12055 * <li>The Record block object</li>
12056 * <li>The "arg" argument from the load function</li>
12057 * <li>A boolean success indicator</li>
12059 * @param {Object} scope The scope in which to call the callback
12060 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12062 load : function(params, reader, callback, scope, arg){
12063 if(this.fireEvent("beforeload", this, params) !== false){
12065 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12067 var url = this.url;
12068 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12070 url += "&_dc=" + (new Date().getTime());
12072 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12075 cb : "stcCallback"+transId,
12076 scriptId : "stcScript"+transId,
12080 callback : callback,
12086 window[trans.cb] = function(o){
12087 conn.handleResponse(o, trans);
12090 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12092 if(this.autoAbort !== false){
12096 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12098 var script = document.createElement("script");
12099 script.setAttribute("src", url);
12100 script.setAttribute("type", "text/javascript");
12101 script.setAttribute("id", trans.scriptId);
12102 this.head.appendChild(script);
12104 this.trans = trans;
12106 callback.call(scope||this, null, arg, false);
12111 isLoading : function(){
12112 return this.trans ? true : false;
12116 * Abort the current server request.
12118 abort : function(){
12119 if(this.isLoading()){
12120 this.destroyTrans(this.trans);
12125 destroyTrans : function(trans, isLoaded){
12126 this.head.removeChild(document.getElementById(trans.scriptId));
12127 clearTimeout(trans.timeoutId);
12129 window[trans.cb] = undefined;
12131 delete window[trans.cb];
12134 // if hasn't been loaded, wait for load to remove it to prevent script error
12135 window[trans.cb] = function(){
12136 window[trans.cb] = undefined;
12138 delete window[trans.cb];
12145 handleResponse : function(o, trans){
12146 this.trans = false;
12147 this.destroyTrans(trans, true);
12150 result = trans.reader.readRecords(o);
12152 this.fireEvent("loadexception", this, o, trans.arg, e);
12153 trans.callback.call(trans.scope||window, null, trans.arg, false);
12156 this.fireEvent("load", this, o, trans.arg);
12157 trans.callback.call(trans.scope||window, result, trans.arg, true);
12161 handleFailure : function(trans){
12162 this.trans = false;
12163 this.destroyTrans(trans, false);
12164 this.fireEvent("loadexception", this, null, trans.arg);
12165 trans.callback.call(trans.scope||window, null, trans.arg, false);
12169 * Ext JS Library 1.1.1
12170 * Copyright(c) 2006-2007, Ext JS, LLC.
12172 * Originally Released Under LGPL - original licence link has changed is not relivant.
12175 * <script type="text/javascript">
12179 * @class Roo.data.JsonReader
12180 * @extends Roo.data.DataReader
12181 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12182 * based on mappings in a provided Roo.data.Record constructor.
12184 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12185 * in the reply previously.
12190 var RecordDef = Roo.data.Record.create([
12191 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12192 {name: 'occupation'} // This field will use "occupation" as the mapping.
12194 var myReader = new Roo.data.JsonReader({
12195 totalProperty: "results", // The property which contains the total dataset size (optional)
12196 root: "rows", // The property which contains an Array of row objects
12197 id: "id" // The property within each row object that provides an ID for the record (optional)
12201 * This would consume a JSON file like this:
12203 { 'results': 2, 'rows': [
12204 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12205 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12208 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12209 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12210 * paged from the remote server.
12211 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12212 * @cfg {String} root name of the property which contains the Array of row objects.
12213 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12214 * @cfg {Array} fields Array of field definition objects
12216 * Create a new JsonReader
12217 * @param {Object} meta Metadata configuration options
12218 * @param {Object} recordType Either an Array of field definition objects,
12219 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12221 Roo.data.JsonReader = function(meta, recordType){
12224 // set some defaults:
12225 Roo.applyIf(meta, {
12226 totalProperty: 'total',
12227 successProperty : 'success',
12232 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12234 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12237 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12238 * Used by Store query builder to append _requestMeta to params.
12241 metaFromRemote : false,
12243 * This method is only used by a DataProxy which has retrieved data from a remote server.
12244 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12245 * @return {Object} data A data block which is used by an Roo.data.Store object as
12246 * a cache of Roo.data.Records.
12248 read : function(response){
12249 var json = response.responseText;
12251 var o = /* eval:var:o */ eval("("+json+")");
12253 throw {message: "JsonReader.read: Json object not found"};
12259 this.metaFromRemote = true;
12260 this.meta = o.metaData;
12261 this.recordType = Roo.data.Record.create(o.metaData.fields);
12262 this.onMetaChange(this.meta, this.recordType, o);
12264 return this.readRecords(o);
12267 // private function a store will implement
12268 onMetaChange : function(meta, recordType, o){
12275 simpleAccess: function(obj, subsc) {
12282 getJsonAccessor: function(){
12284 return function(expr) {
12286 return(re.test(expr))
12287 ? new Function("obj", "return obj." + expr)
12292 return Roo.emptyFn;
12297 * Create a data block containing Roo.data.Records from an XML document.
12298 * @param {Object} o An object which contains an Array of row objects in the property specified
12299 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12300 * which contains the total size of the dataset.
12301 * @return {Object} data A data block which is used by an Roo.data.Store object as
12302 * a cache of Roo.data.Records.
12304 readRecords : function(o){
12306 * After any data loads, the raw JSON data is available for further custom processing.
12310 var s = this.meta, Record = this.recordType,
12311 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12313 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12315 if(s.totalProperty) {
12316 this.getTotal = this.getJsonAccessor(s.totalProperty);
12318 if(s.successProperty) {
12319 this.getSuccess = this.getJsonAccessor(s.successProperty);
12321 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12323 var g = this.getJsonAccessor(s.id);
12324 this.getId = function(rec) {
12326 return (r === undefined || r === "") ? null : r;
12329 this.getId = function(){return null;};
12332 for(var jj = 0; jj < fl; jj++){
12334 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12335 this.ef[jj] = this.getJsonAccessor(map);
12339 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12340 if(s.totalProperty){
12341 var vt = parseInt(this.getTotal(o), 10);
12346 if(s.successProperty){
12347 var vs = this.getSuccess(o);
12348 if(vs === false || vs === 'false'){
12353 for(var i = 0; i < c; i++){
12356 var id = this.getId(n);
12357 for(var j = 0; j < fl; j++){
12359 var v = this.ef[j](n);
12361 Roo.log('missing convert for ' + f.name);
12365 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12367 var record = new Record(values, id);
12369 records[i] = record;
12375 totalRecords : totalRecords
12380 * Ext JS Library 1.1.1
12381 * Copyright(c) 2006-2007, Ext JS, LLC.
12383 * Originally Released Under LGPL - original licence link has changed is not relivant.
12386 * <script type="text/javascript">
12390 * @class Roo.data.ArrayReader
12391 * @extends Roo.data.DataReader
12392 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12393 * Each element of that Array represents a row of data fields. The
12394 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12395 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12399 var RecordDef = Roo.data.Record.create([
12400 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12401 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12403 var myReader = new Roo.data.ArrayReader({
12404 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12408 * This would consume an Array like this:
12410 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12412 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12414 * Create a new JsonReader
12415 * @param {Object} meta Metadata configuration options.
12416 * @param {Object} recordType Either an Array of field definition objects
12417 * as specified to {@link Roo.data.Record#create},
12418 * or an {@link Roo.data.Record} object
12419 * created using {@link Roo.data.Record#create}.
12421 Roo.data.ArrayReader = function(meta, recordType){
12422 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12425 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12427 * Create a data block containing Roo.data.Records from an XML document.
12428 * @param {Object} o An Array of row objects which represents the dataset.
12429 * @return {Object} data A data block which is used by an Roo.data.Store object as
12430 * a cache of Roo.data.Records.
12432 readRecords : function(o){
12433 var sid = this.meta ? this.meta.id : null;
12434 var recordType = this.recordType, fields = recordType.prototype.fields;
12437 for(var i = 0; i < root.length; i++){
12440 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12441 for(var j = 0, jlen = fields.length; j < jlen; j++){
12442 var f = fields.items[j];
12443 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12444 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12446 values[f.name] = v;
12448 var record = new recordType(values, id);
12450 records[records.length] = record;
12454 totalRecords : records.length
12463 * @class Roo.bootstrap.ComboBox
12464 * @extends Roo.bootstrap.TriggerField
12465 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12466 * @cfg {Boolean} append (true|false) default false
12467 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12468 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12469 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12470 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12471 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12472 * @cfg {Boolean} animate default true
12473 * @cfg {Boolean} emptyResultText only for touch device
12474 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12475 * @cfg {String} emptyTitle default ''
12477 * Create a new ComboBox.
12478 * @param {Object} config Configuration options
12480 Roo.bootstrap.ComboBox = function(config){
12481 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12485 * Fires when the dropdown list is expanded
12486 * @param {Roo.bootstrap.ComboBox} combo This combo box
12491 * Fires when the dropdown list is collapsed
12492 * @param {Roo.bootstrap.ComboBox} combo This combo box
12496 * @event beforeselect
12497 * Fires before a list item is selected. Return false to cancel the selection.
12498 * @param {Roo.bootstrap.ComboBox} combo This combo box
12499 * @param {Roo.data.Record} record The data record returned from the underlying store
12500 * @param {Number} index The index of the selected item in the dropdown list
12502 'beforeselect' : true,
12505 * Fires when a list item is selected
12506 * @param {Roo.bootstrap.ComboBox} combo This combo box
12507 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12508 * @param {Number} index The index of the selected item in the dropdown list
12512 * @event beforequery
12513 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12514 * The event object passed has these properties:
12515 * @param {Roo.bootstrap.ComboBox} combo This combo box
12516 * @param {String} query The query
12517 * @param {Boolean} forceAll true to force "all" query
12518 * @param {Boolean} cancel true to cancel the query
12519 * @param {Object} e The query event object
12521 'beforequery': true,
12524 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12525 * @param {Roo.bootstrap.ComboBox} combo This combo box
12530 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12531 * @param {Roo.bootstrap.ComboBox} combo This combo box
12532 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12537 * Fires when the remove value from the combobox array
12538 * @param {Roo.bootstrap.ComboBox} combo This combo box
12542 * @event afterremove
12543 * Fires when the remove value from the combobox array
12544 * @param {Roo.bootstrap.ComboBox} combo This combo box
12546 'afterremove' : true,
12548 * @event specialfilter
12549 * Fires when specialfilter
12550 * @param {Roo.bootstrap.ComboBox} combo This combo box
12552 'specialfilter' : true,
12555 * Fires when tick the element
12556 * @param {Roo.bootstrap.ComboBox} combo This combo box
12560 * @event touchviewdisplay
12561 * Fires when touch view require special display (default is using displayField)
12562 * @param {Roo.bootstrap.ComboBox} combo This combo box
12563 * @param {Object} cfg set html .
12565 'touchviewdisplay' : true
12570 this.tickItems = [];
12572 this.selectedIndex = -1;
12573 if(this.mode == 'local'){
12574 if(config.queryDelay === undefined){
12575 this.queryDelay = 10;
12577 if(config.minChars === undefined){
12583 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12586 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12587 * rendering into an Roo.Editor, defaults to false)
12590 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12591 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12594 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12597 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12598 * the dropdown list (defaults to undefined, with no header element)
12602 * @cfg {String/Roo.Template} tpl The template to use to render the output
12606 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12608 listWidth: undefined,
12610 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12611 * mode = 'remote' or 'text' if mode = 'local')
12613 displayField: undefined,
12616 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12617 * mode = 'remote' or 'value' if mode = 'local').
12618 * Note: use of a valueField requires the user make a selection
12619 * in order for a value to be mapped.
12621 valueField: undefined,
12623 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12628 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12629 * field's data value (defaults to the underlying DOM element's name)
12631 hiddenName: undefined,
12633 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12637 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12639 selectedClass: 'active',
12642 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12646 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12647 * anchor positions (defaults to 'tl-bl')
12649 listAlign: 'tl-bl?',
12651 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12655 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12656 * query specified by the allQuery config option (defaults to 'query')
12658 triggerAction: 'query',
12660 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12661 * (defaults to 4, does not apply if editable = false)
12665 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12666 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12670 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12671 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12675 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12676 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12680 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12681 * when editable = true (defaults to false)
12683 selectOnFocus:false,
12685 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12687 queryParam: 'query',
12689 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12690 * when mode = 'remote' (defaults to 'Loading...')
12692 loadingText: 'Loading...',
12694 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12698 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12702 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12703 * traditional select (defaults to true)
12707 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12711 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12715 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12716 * listWidth has a higher value)
12720 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12721 * allow the user to set arbitrary text into the field (defaults to false)
12723 forceSelection:false,
12725 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12726 * if typeAhead = true (defaults to 250)
12728 typeAheadDelay : 250,
12730 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12731 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12733 valueNotFoundText : undefined,
12735 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12737 blockFocus : false,
12740 * @cfg {Boolean} disableClear Disable showing of clear button.
12742 disableClear : false,
12744 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12746 alwaysQuery : false,
12749 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12754 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12756 invalidClass : "has-warning",
12759 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12761 validClass : "has-success",
12764 * @cfg {Boolean} specialFilter (true|false) special filter default false
12766 specialFilter : false,
12769 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12771 mobileTouchView : true,
12774 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12776 useNativeIOS : false,
12778 ios_options : false,
12790 btnPosition : 'right',
12791 triggerList : true,
12792 showToggleBtn : true,
12794 emptyResultText: 'Empty',
12795 triggerText : 'Select',
12798 // element that contains real text value.. (when hidden is used..)
12800 getAutoCreate : function()
12805 * Render classic select for iso
12808 if(Roo.isIOS && this.useNativeIOS){
12809 cfg = this.getAutoCreateNativeIOS();
12817 if(Roo.isTouch && this.mobileTouchView){
12818 cfg = this.getAutoCreateTouchView();
12825 if(!this.tickable){
12826 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12831 * ComboBox with tickable selections
12834 var align = this.labelAlign || this.parentLabelAlign();
12837 cls : 'form-group roo-combobox-tickable' //input-group
12840 var btn_text_select = '';
12841 var btn_text_done = '';
12842 var btn_text_cancel = '';
12844 if (this.btn_text_show) {
12845 btn_text_select = 'Select';
12846 btn_text_done = 'Done';
12847 btn_text_cancel = 'Cancel';
12852 cls : 'tickable-buttons',
12857 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12858 //html : this.triggerText
12859 html: btn_text_select
12865 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12867 html: btn_text_done
12873 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12875 html: btn_text_cancel
12881 buttons.cn.unshift({
12883 cls: 'roo-select2-search-field-input'
12889 Roo.each(buttons.cn, function(c){
12891 c.cls += ' btn-' + _this.size;
12894 if (_this.disabled) {
12905 cls: 'form-hidden-field'
12909 cls: 'roo-select2-choices',
12913 cls: 'roo-select2-search-field',
12924 cls: 'roo-select2-container input-group roo-select2-container-multi',
12929 // cls: 'typeahead typeahead-long dropdown-menu',
12930 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12935 if(this.hasFeedback && !this.allowBlank){
12939 cls: 'glyphicon form-control-feedback'
12942 combobox.cn.push(feedback);
12946 if (align ==='left' && this.fieldLabel.length) {
12948 cfg.cls += ' roo-form-group-label-left';
12953 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12954 tooltip : 'This field is required'
12959 cls : 'control-label',
12960 html : this.fieldLabel
12972 var labelCfg = cfg.cn[1];
12973 var contentCfg = cfg.cn[2];
12976 if(this.indicatorpos == 'right'){
12982 cls : 'control-label',
12986 html : this.fieldLabel
12990 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12991 tooltip : 'This field is required'
13006 labelCfg = cfg.cn[0];
13007 contentCfg = cfg.cn[1];
13011 if(this.labelWidth > 12){
13012 labelCfg.style = "width: " + this.labelWidth + 'px';
13015 if(this.labelWidth < 13 && this.labelmd == 0){
13016 this.labelmd = this.labelWidth;
13019 if(this.labellg > 0){
13020 labelCfg.cls += ' col-lg-' + this.labellg;
13021 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13024 if(this.labelmd > 0){
13025 labelCfg.cls += ' col-md-' + this.labelmd;
13026 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13029 if(this.labelsm > 0){
13030 labelCfg.cls += ' col-sm-' + this.labelsm;
13031 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13034 if(this.labelxs > 0){
13035 labelCfg.cls += ' col-xs-' + this.labelxs;
13036 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13040 } else if ( this.fieldLabel.length) {
13041 // Roo.log(" label");
13045 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13046 tooltip : 'This field is required'
13050 //cls : 'input-group-addon',
13051 html : this.fieldLabel
13056 if(this.indicatorpos == 'right'){
13060 //cls : 'input-group-addon',
13061 html : this.fieldLabel
13065 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13066 tooltip : 'This field is required'
13075 // Roo.log(" no label && no align");
13082 ['xs','sm','md','lg'].map(function(size){
13083 if (settings[size]) {
13084 cfg.cls += ' col-' + size + '-' + settings[size];
13092 _initEventsCalled : false,
13095 initEvents: function()
13097 if (this._initEventsCalled) { // as we call render... prevent looping...
13100 this._initEventsCalled = true;
13103 throw "can not find store for combo";
13106 this.indicator = this.indicatorEl();
13108 this.store = Roo.factory(this.store, Roo.data);
13109 this.store.parent = this;
13111 // if we are building from html. then this element is so complex, that we can not really
13112 // use the rendered HTML.
13113 // so we have to trash and replace the previous code.
13114 if (Roo.XComponent.build_from_html) {
13115 // remove this element....
13116 var e = this.el.dom, k=0;
13117 while (e ) { e = e.previousSibling; ++k;}
13122 this.rendered = false;
13124 this.render(this.parent().getChildContainer(true), k);
13127 if(Roo.isIOS && this.useNativeIOS){
13128 this.initIOSView();
13136 if(Roo.isTouch && this.mobileTouchView){
13137 this.initTouchView();
13142 this.initTickableEvents();
13146 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13148 if(this.hiddenName){
13150 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13152 this.hiddenField.dom.value =
13153 this.hiddenValue !== undefined ? this.hiddenValue :
13154 this.value !== undefined ? this.value : '';
13156 // prevent input submission
13157 this.el.dom.removeAttribute('name');
13158 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13163 // this.el.dom.setAttribute('autocomplete', 'off');
13166 var cls = 'x-combo-list';
13168 //this.list = new Roo.Layer({
13169 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13175 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13176 _this.list.setWidth(lw);
13179 this.list.on('mouseover', this.onViewOver, this);
13180 this.list.on('mousemove', this.onViewMove, this);
13181 this.list.on('scroll', this.onViewScroll, this);
13184 this.list.swallowEvent('mousewheel');
13185 this.assetHeight = 0;
13188 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13189 this.assetHeight += this.header.getHeight();
13192 this.innerList = this.list.createChild({cls:cls+'-inner'});
13193 this.innerList.on('mouseover', this.onViewOver, this);
13194 this.innerList.on('mousemove', this.onViewMove, this);
13195 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13197 if(this.allowBlank && !this.pageSize && !this.disableClear){
13198 this.footer = this.list.createChild({cls:cls+'-ft'});
13199 this.pageTb = new Roo.Toolbar(this.footer);
13203 this.footer = this.list.createChild({cls:cls+'-ft'});
13204 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13205 {pageSize: this.pageSize});
13209 if (this.pageTb && this.allowBlank && !this.disableClear) {
13211 this.pageTb.add(new Roo.Toolbar.Fill(), {
13212 cls: 'x-btn-icon x-btn-clear',
13214 handler: function()
13217 _this.clearValue();
13218 _this.onSelect(false, -1);
13223 this.assetHeight += this.footer.getHeight();
13228 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13231 this.view = new Roo.View(this.list, this.tpl, {
13232 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13234 //this.view.wrapEl.setDisplayed(false);
13235 this.view.on('click', this.onViewClick, this);
13238 this.store.on('beforeload', this.onBeforeLoad, this);
13239 this.store.on('load', this.onLoad, this);
13240 this.store.on('loadexception', this.onLoadException, this);
13242 if(this.resizable){
13243 this.resizer = new Roo.Resizable(this.list, {
13244 pinned:true, handles:'se'
13246 this.resizer.on('resize', function(r, w, h){
13247 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13248 this.listWidth = w;
13249 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13250 this.restrictHeight();
13252 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13255 if(!this.editable){
13256 this.editable = true;
13257 this.setEditable(false);
13262 if (typeof(this.events.add.listeners) != 'undefined') {
13264 this.addicon = this.wrap.createChild(
13265 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13267 this.addicon.on('click', function(e) {
13268 this.fireEvent('add', this);
13271 if (typeof(this.events.edit.listeners) != 'undefined') {
13273 this.editicon = this.wrap.createChild(
13274 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13275 if (this.addicon) {
13276 this.editicon.setStyle('margin-left', '40px');
13278 this.editicon.on('click', function(e) {
13280 // we fire even if inothing is selected..
13281 this.fireEvent('edit', this, this.lastData );
13287 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13288 "up" : function(e){
13289 this.inKeyMode = true;
13293 "down" : function(e){
13294 if(!this.isExpanded()){
13295 this.onTriggerClick();
13297 this.inKeyMode = true;
13302 "enter" : function(e){
13303 // this.onViewClick();
13307 if(this.fireEvent("specialkey", this, e)){
13308 this.onViewClick(false);
13314 "esc" : function(e){
13318 "tab" : function(e){
13321 if(this.fireEvent("specialkey", this, e)){
13322 this.onViewClick(false);
13330 doRelay : function(foo, bar, hname){
13331 if(hname == 'down' || this.scope.isExpanded()){
13332 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13341 this.queryDelay = Math.max(this.queryDelay || 10,
13342 this.mode == 'local' ? 10 : 250);
13345 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13347 if(this.typeAhead){
13348 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13350 if(this.editable !== false){
13351 this.inputEl().on("keyup", this.onKeyUp, this);
13353 if(this.forceSelection){
13354 this.inputEl().on('blur', this.doForce, this);
13358 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13359 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13363 initTickableEvents: function()
13367 if(this.hiddenName){
13369 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13371 this.hiddenField.dom.value =
13372 this.hiddenValue !== undefined ? this.hiddenValue :
13373 this.value !== undefined ? this.value : '';
13375 // prevent input submission
13376 this.el.dom.removeAttribute('name');
13377 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13382 // this.list = this.el.select('ul.dropdown-menu',true).first();
13384 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13385 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13386 if(this.triggerList){
13387 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13390 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13391 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13393 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13394 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13396 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13397 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13399 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13400 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13401 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13404 this.cancelBtn.hide();
13409 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13410 _this.list.setWidth(lw);
13413 this.list.on('mouseover', this.onViewOver, this);
13414 this.list.on('mousemove', this.onViewMove, this);
13416 this.list.on('scroll', this.onViewScroll, this);
13419 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>';
13422 this.view = new Roo.View(this.list, this.tpl, {
13423 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13426 //this.view.wrapEl.setDisplayed(false);
13427 this.view.on('click', this.onViewClick, this);
13431 this.store.on('beforeload', this.onBeforeLoad, this);
13432 this.store.on('load', this.onLoad, this);
13433 this.store.on('loadexception', this.onLoadException, this);
13436 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13437 "up" : function(e){
13438 this.inKeyMode = true;
13442 "down" : function(e){
13443 this.inKeyMode = true;
13447 "enter" : function(e){
13448 if(this.fireEvent("specialkey", this, e)){
13449 this.onViewClick(false);
13455 "esc" : function(e){
13456 this.onTickableFooterButtonClick(e, false, false);
13459 "tab" : function(e){
13460 this.fireEvent("specialkey", this, e);
13462 this.onTickableFooterButtonClick(e, false, false);
13469 doRelay : function(e, fn, key){
13470 if(this.scope.isExpanded()){
13471 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13480 this.queryDelay = Math.max(this.queryDelay || 10,
13481 this.mode == 'local' ? 10 : 250);
13484 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13486 if(this.typeAhead){
13487 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13490 if(this.editable !== false){
13491 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13494 this.indicator = this.indicatorEl();
13496 if(this.indicator){
13497 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13498 this.indicator.hide();
13503 onDestroy : function(){
13505 this.view.setStore(null);
13506 this.view.el.removeAllListeners();
13507 this.view.el.remove();
13508 this.view.purgeListeners();
13511 this.list.dom.innerHTML = '';
13515 this.store.un('beforeload', this.onBeforeLoad, this);
13516 this.store.un('load', this.onLoad, this);
13517 this.store.un('loadexception', this.onLoadException, this);
13519 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13523 fireKey : function(e){
13524 if(e.isNavKeyPress() && !this.list.isVisible()){
13525 this.fireEvent("specialkey", this, e);
13530 onResize: function(w, h){
13531 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13533 // if(typeof w != 'number'){
13534 // // we do not handle it!?!?
13537 // var tw = this.trigger.getWidth();
13538 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13539 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13541 // this.inputEl().setWidth( this.adjustWidth('input', x));
13543 // //this.trigger.setStyle('left', x+'px');
13545 // if(this.list && this.listWidth === undefined){
13546 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13547 // this.list.setWidth(lw);
13548 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13556 * Allow or prevent the user from directly editing the field text. If false is passed,
13557 * the user will only be able to select from the items defined in the dropdown list. This method
13558 * is the runtime equivalent of setting the 'editable' config option at config time.
13559 * @param {Boolean} value True to allow the user to directly edit the field text
13561 setEditable : function(value){
13562 if(value == this.editable){
13565 this.editable = value;
13567 this.inputEl().dom.setAttribute('readOnly', true);
13568 this.inputEl().on('mousedown', this.onTriggerClick, this);
13569 this.inputEl().addClass('x-combo-noedit');
13571 this.inputEl().dom.setAttribute('readOnly', false);
13572 this.inputEl().un('mousedown', this.onTriggerClick, this);
13573 this.inputEl().removeClass('x-combo-noedit');
13579 onBeforeLoad : function(combo,opts){
13580 if(!this.hasFocus){
13584 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13586 this.restrictHeight();
13587 this.selectedIndex = -1;
13591 onLoad : function(){
13593 this.hasQuery = false;
13595 if(!this.hasFocus){
13599 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13600 this.loading.hide();
13603 if(this.store.getCount() > 0){
13606 this.restrictHeight();
13607 if(this.lastQuery == this.allQuery){
13608 if(this.editable && !this.tickable){
13609 this.inputEl().dom.select();
13613 !this.selectByValue(this.value, true) &&
13616 !this.store.lastOptions ||
13617 typeof(this.store.lastOptions.add) == 'undefined' ||
13618 this.store.lastOptions.add != true
13621 this.select(0, true);
13624 if(this.autoFocus){
13627 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13628 this.taTask.delay(this.typeAheadDelay);
13632 this.onEmptyResults();
13638 onLoadException : function()
13640 this.hasQuery = false;
13642 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13643 this.loading.hide();
13646 if(this.tickable && this.editable){
13651 // only causes errors at present
13652 //Roo.log(this.store.reader.jsonData);
13653 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13655 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13661 onTypeAhead : function(){
13662 if(this.store.getCount() > 0){
13663 var r = this.store.getAt(0);
13664 var newValue = r.data[this.displayField];
13665 var len = newValue.length;
13666 var selStart = this.getRawValue().length;
13668 if(selStart != len){
13669 this.setRawValue(newValue);
13670 this.selectText(selStart, newValue.length);
13676 onSelect : function(record, index){
13678 if(this.fireEvent('beforeselect', this, record, index) !== false){
13680 this.setFromData(index > -1 ? record.data : false);
13683 this.fireEvent('select', this, record, index);
13688 * Returns the currently selected field value or empty string if no value is set.
13689 * @return {String} value The selected value
13691 getValue : function()
13693 if(Roo.isIOS && this.useNativeIOS){
13694 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13698 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13701 if(this.valueField){
13702 return typeof this.value != 'undefined' ? this.value : '';
13704 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13708 getRawValue : function()
13710 if(Roo.isIOS && this.useNativeIOS){
13711 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13714 var v = this.inputEl().getValue();
13720 * Clears any text/value currently set in the field
13722 clearValue : function(){
13724 if(this.hiddenField){
13725 this.hiddenField.dom.value = '';
13728 this.setRawValue('');
13729 this.lastSelectionText = '';
13730 this.lastData = false;
13732 var close = this.closeTriggerEl();
13743 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13744 * will be displayed in the field. If the value does not match the data value of an existing item,
13745 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13746 * Otherwise the field will be blank (although the value will still be set).
13747 * @param {String} value The value to match
13749 setValue : function(v)
13751 if(Roo.isIOS && this.useNativeIOS){
13752 this.setIOSValue(v);
13762 if(this.valueField){
13763 var r = this.findRecord(this.valueField, v);
13765 text = r.data[this.displayField];
13766 }else if(this.valueNotFoundText !== undefined){
13767 text = this.valueNotFoundText;
13770 this.lastSelectionText = text;
13771 if(this.hiddenField){
13772 this.hiddenField.dom.value = v;
13774 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13777 var close = this.closeTriggerEl();
13780 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13786 * @property {Object} the last set data for the element
13791 * Sets the value of the field based on a object which is related to the record format for the store.
13792 * @param {Object} value the value to set as. or false on reset?
13794 setFromData : function(o){
13801 var dv = ''; // display value
13802 var vv = ''; // value value..
13804 if (this.displayField) {
13805 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13807 // this is an error condition!!!
13808 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13811 if(this.valueField){
13812 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13815 var close = this.closeTriggerEl();
13818 if(dv.length || vv * 1 > 0){
13820 this.blockFocus=true;
13826 if(this.hiddenField){
13827 this.hiddenField.dom.value = vv;
13829 this.lastSelectionText = dv;
13830 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13834 // no hidden field.. - we store the value in 'value', but still display
13835 // display field!!!!
13836 this.lastSelectionText = dv;
13837 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13844 reset : function(){
13845 // overridden so that last data is reset..
13852 this.setValue(this.originalValue);
13853 //this.clearInvalid();
13854 this.lastData = false;
13856 this.view.clearSelections();
13862 findRecord : function(prop, value){
13864 if(this.store.getCount() > 0){
13865 this.store.each(function(r){
13866 if(r.data[prop] == value){
13876 getName: function()
13878 // returns hidden if it's set..
13879 if (!this.rendered) {return ''};
13880 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13884 onViewMove : function(e, t){
13885 this.inKeyMode = false;
13889 onViewOver : function(e, t){
13890 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13893 var item = this.view.findItemFromChild(t);
13896 var index = this.view.indexOf(item);
13897 this.select(index, false);
13902 onViewClick : function(view, doFocus, el, e)
13904 var index = this.view.getSelectedIndexes()[0];
13906 var r = this.store.getAt(index);
13910 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13917 Roo.each(this.tickItems, function(v,k){
13919 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13921 _this.tickItems.splice(k, 1);
13923 if(typeof(e) == 'undefined' && view == false){
13924 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13936 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13937 this.tickItems.push(r.data);
13940 if(typeof(e) == 'undefined' && view == false){
13941 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13948 this.onSelect(r, index);
13950 if(doFocus !== false && !this.blockFocus){
13951 this.inputEl().focus();
13956 restrictHeight : function(){
13957 //this.innerList.dom.style.height = '';
13958 //var inner = this.innerList.dom;
13959 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13960 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13961 //this.list.beginUpdate();
13962 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13963 this.list.alignTo(this.inputEl(), this.listAlign);
13964 this.list.alignTo(this.inputEl(), this.listAlign);
13965 //this.list.endUpdate();
13969 onEmptyResults : function(){
13971 if(this.tickable && this.editable){
13972 this.restrictHeight();
13980 * Returns true if the dropdown list is expanded, else false.
13982 isExpanded : function(){
13983 return this.list.isVisible();
13987 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13988 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13989 * @param {String} value The data value of the item to select
13990 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13991 * selected item if it is not currently in view (defaults to true)
13992 * @return {Boolean} True if the value matched an item in the list, else false
13994 selectByValue : function(v, scrollIntoView){
13995 if(v !== undefined && v !== null){
13996 var r = this.findRecord(this.valueField || this.displayField, v);
13998 this.select(this.store.indexOf(r), scrollIntoView);
14006 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14007 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14008 * @param {Number} index The zero-based index of the list item to select
14009 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14010 * selected item if it is not currently in view (defaults to true)
14012 select : function(index, scrollIntoView){
14013 this.selectedIndex = index;
14014 this.view.select(index);
14015 if(scrollIntoView !== false){
14016 var el = this.view.getNode(index);
14018 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14021 this.list.scrollChildIntoView(el, false);
14027 selectNext : function(){
14028 var ct = this.store.getCount();
14030 if(this.selectedIndex == -1){
14032 }else if(this.selectedIndex < ct-1){
14033 this.select(this.selectedIndex+1);
14039 selectPrev : function(){
14040 var ct = this.store.getCount();
14042 if(this.selectedIndex == -1){
14044 }else if(this.selectedIndex != 0){
14045 this.select(this.selectedIndex-1);
14051 onKeyUp : function(e){
14052 if(this.editable !== false && !e.isSpecialKey()){
14053 this.lastKey = e.getKey();
14054 this.dqTask.delay(this.queryDelay);
14059 validateBlur : function(){
14060 return !this.list || !this.list.isVisible();
14064 initQuery : function(){
14066 var v = this.getRawValue();
14068 if(this.tickable && this.editable){
14069 v = this.tickableInputEl().getValue();
14076 doForce : function(){
14077 if(this.inputEl().dom.value.length > 0){
14078 this.inputEl().dom.value =
14079 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14085 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14086 * query allowing the query action to be canceled if needed.
14087 * @param {String} query The SQL query to execute
14088 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14089 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14090 * saved in the current store (defaults to false)
14092 doQuery : function(q, forceAll){
14094 if(q === undefined || q === null){
14099 forceAll: forceAll,
14103 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14108 forceAll = qe.forceAll;
14109 if(forceAll === true || (q.length >= this.minChars)){
14111 this.hasQuery = true;
14113 if(this.lastQuery != q || this.alwaysQuery){
14114 this.lastQuery = q;
14115 if(this.mode == 'local'){
14116 this.selectedIndex = -1;
14118 this.store.clearFilter();
14121 if(this.specialFilter){
14122 this.fireEvent('specialfilter', this);
14127 this.store.filter(this.displayField, q);
14130 this.store.fireEvent("datachanged", this.store);
14137 this.store.baseParams[this.queryParam] = q;
14139 var options = {params : this.getParams(q)};
14142 options.add = true;
14143 options.params.start = this.page * this.pageSize;
14146 this.store.load(options);
14149 * this code will make the page width larger, at the beginning, the list not align correctly,
14150 * we should expand the list on onLoad
14151 * so command out it
14156 this.selectedIndex = -1;
14161 this.loadNext = false;
14165 getParams : function(q){
14167 //p[this.queryParam] = q;
14171 p.limit = this.pageSize;
14177 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14179 collapse : function(){
14180 if(!this.isExpanded()){
14186 this.hasFocus = false;
14190 this.cancelBtn.hide();
14191 this.trigger.show();
14194 this.tickableInputEl().dom.value = '';
14195 this.tickableInputEl().blur();
14200 Roo.get(document).un('mousedown', this.collapseIf, this);
14201 Roo.get(document).un('mousewheel', this.collapseIf, this);
14202 if (!this.editable) {
14203 Roo.get(document).un('keydown', this.listKeyPress, this);
14205 this.fireEvent('collapse', this);
14211 collapseIf : function(e){
14212 var in_combo = e.within(this.el);
14213 var in_list = e.within(this.list);
14214 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14216 if (in_combo || in_list || is_list) {
14217 //e.stopPropagation();
14222 this.onTickableFooterButtonClick(e, false, false);
14230 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14232 expand : function(){
14234 if(this.isExpanded() || !this.hasFocus){
14238 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14239 this.list.setWidth(lw);
14245 this.restrictHeight();
14249 this.tickItems = Roo.apply([], this.item);
14252 this.cancelBtn.show();
14253 this.trigger.hide();
14256 this.tickableInputEl().focus();
14261 Roo.get(document).on('mousedown', this.collapseIf, this);
14262 Roo.get(document).on('mousewheel', this.collapseIf, this);
14263 if (!this.editable) {
14264 Roo.get(document).on('keydown', this.listKeyPress, this);
14267 this.fireEvent('expand', this);
14271 // Implements the default empty TriggerField.onTriggerClick function
14272 onTriggerClick : function(e)
14274 Roo.log('trigger click');
14276 if(this.disabled || !this.triggerList){
14281 this.loadNext = false;
14283 if(this.isExpanded()){
14285 if (!this.blockFocus) {
14286 this.inputEl().focus();
14290 this.hasFocus = true;
14291 if(this.triggerAction == 'all') {
14292 this.doQuery(this.allQuery, true);
14294 this.doQuery(this.getRawValue());
14296 if (!this.blockFocus) {
14297 this.inputEl().focus();
14302 onTickableTriggerClick : function(e)
14309 this.loadNext = false;
14310 this.hasFocus = true;
14312 if(this.triggerAction == 'all') {
14313 this.doQuery(this.allQuery, true);
14315 this.doQuery(this.getRawValue());
14319 onSearchFieldClick : function(e)
14321 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14322 this.onTickableFooterButtonClick(e, false, false);
14326 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14331 this.loadNext = false;
14332 this.hasFocus = true;
14334 if(this.triggerAction == 'all') {
14335 this.doQuery(this.allQuery, true);
14337 this.doQuery(this.getRawValue());
14341 listKeyPress : function(e)
14343 //Roo.log('listkeypress');
14344 // scroll to first matching element based on key pres..
14345 if (e.isSpecialKey()) {
14348 var k = String.fromCharCode(e.getKey()).toUpperCase();
14351 var csel = this.view.getSelectedNodes();
14352 var cselitem = false;
14354 var ix = this.view.indexOf(csel[0]);
14355 cselitem = this.store.getAt(ix);
14356 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14362 this.store.each(function(v) {
14364 // start at existing selection.
14365 if (cselitem.id == v.id) {
14371 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14372 match = this.store.indexOf(v);
14378 if (match === false) {
14379 return true; // no more action?
14382 this.view.select(match);
14383 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14384 sn.scrollIntoView(sn.dom.parentNode, false);
14387 onViewScroll : function(e, t){
14389 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){
14393 this.hasQuery = true;
14395 this.loading = this.list.select('.loading', true).first();
14397 if(this.loading === null){
14398 this.list.createChild({
14400 cls: 'loading roo-select2-more-results roo-select2-active',
14401 html: 'Loading more results...'
14404 this.loading = this.list.select('.loading', true).first();
14406 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14408 this.loading.hide();
14411 this.loading.show();
14416 this.loadNext = true;
14418 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14423 addItem : function(o)
14425 var dv = ''; // display value
14427 if (this.displayField) {
14428 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14430 // this is an error condition!!!
14431 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14438 var choice = this.choices.createChild({
14440 cls: 'roo-select2-search-choice',
14449 cls: 'roo-select2-search-choice-close fa fa-times',
14454 }, this.searchField);
14456 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14458 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14466 this.inputEl().dom.value = '';
14471 onRemoveItem : function(e, _self, o)
14473 e.preventDefault();
14475 this.lastItem = Roo.apply([], this.item);
14477 var index = this.item.indexOf(o.data) * 1;
14480 Roo.log('not this item?!');
14484 this.item.splice(index, 1);
14489 this.fireEvent('remove', this, e);
14495 syncValue : function()
14497 if(!this.item.length){
14504 Roo.each(this.item, function(i){
14505 if(_this.valueField){
14506 value.push(i[_this.valueField]);
14513 this.value = value.join(',');
14515 if(this.hiddenField){
14516 this.hiddenField.dom.value = this.value;
14519 this.store.fireEvent("datachanged", this.store);
14524 clearItem : function()
14526 if(!this.multiple){
14532 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14540 if(this.tickable && !Roo.isTouch){
14541 this.view.refresh();
14545 inputEl: function ()
14547 if(Roo.isIOS && this.useNativeIOS){
14548 return this.el.select('select.roo-ios-select', true).first();
14551 if(Roo.isTouch && this.mobileTouchView){
14552 return this.el.select('input.form-control',true).first();
14556 return this.searchField;
14559 return this.el.select('input.form-control',true).first();
14562 onTickableFooterButtonClick : function(e, btn, el)
14564 e.preventDefault();
14566 this.lastItem = Roo.apply([], this.item);
14568 if(btn && btn.name == 'cancel'){
14569 this.tickItems = Roo.apply([], this.item);
14578 Roo.each(this.tickItems, function(o){
14586 validate : function()
14588 var v = this.getRawValue();
14591 v = this.getValue();
14594 if(this.disabled || this.allowBlank || v.length){
14599 this.markInvalid();
14603 tickableInputEl : function()
14605 if(!this.tickable || !this.editable){
14606 return this.inputEl();
14609 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14613 getAutoCreateTouchView : function()
14618 cls: 'form-group' //input-group
14624 type : this.inputType,
14625 cls : 'form-control x-combo-noedit',
14626 autocomplete: 'new-password',
14627 placeholder : this.placeholder || '',
14632 input.name = this.name;
14636 input.cls += ' input-' + this.size;
14639 if (this.disabled) {
14640 input.disabled = true;
14651 inputblock.cls += ' input-group';
14653 inputblock.cn.unshift({
14655 cls : 'input-group-addon',
14660 if(this.removable && !this.multiple){
14661 inputblock.cls += ' roo-removable';
14663 inputblock.cn.push({
14666 cls : 'roo-combo-removable-btn close'
14670 if(this.hasFeedback && !this.allowBlank){
14672 inputblock.cls += ' has-feedback';
14674 inputblock.cn.push({
14676 cls: 'glyphicon form-control-feedback'
14683 inputblock.cls += (this.before) ? '' : ' input-group';
14685 inputblock.cn.push({
14687 cls : 'input-group-addon',
14698 cls: 'form-hidden-field'
14712 cls: 'form-hidden-field'
14716 cls: 'roo-select2-choices',
14720 cls: 'roo-select2-search-field',
14733 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14739 if(!this.multiple && this.showToggleBtn){
14746 if (this.caret != false) {
14749 cls: 'fa fa-' + this.caret
14756 cls : 'input-group-addon btn dropdown-toggle',
14761 cls: 'combobox-clear',
14775 combobox.cls += ' roo-select2-container-multi';
14778 var align = this.labelAlign || this.parentLabelAlign();
14780 if (align ==='left' && this.fieldLabel.length) {
14785 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14786 tooltip : 'This field is required'
14790 cls : 'control-label',
14791 html : this.fieldLabel
14802 var labelCfg = cfg.cn[1];
14803 var contentCfg = cfg.cn[2];
14806 if(this.indicatorpos == 'right'){
14811 cls : 'control-label',
14815 html : this.fieldLabel
14819 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14820 tooltip : 'This field is required'
14833 labelCfg = cfg.cn[0];
14834 contentCfg = cfg.cn[1];
14839 if(this.labelWidth > 12){
14840 labelCfg.style = "width: " + this.labelWidth + 'px';
14843 if(this.labelWidth < 13 && this.labelmd == 0){
14844 this.labelmd = this.labelWidth;
14847 if(this.labellg > 0){
14848 labelCfg.cls += ' col-lg-' + this.labellg;
14849 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14852 if(this.labelmd > 0){
14853 labelCfg.cls += ' col-md-' + this.labelmd;
14854 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14857 if(this.labelsm > 0){
14858 labelCfg.cls += ' col-sm-' + this.labelsm;
14859 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14862 if(this.labelxs > 0){
14863 labelCfg.cls += ' col-xs-' + this.labelxs;
14864 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14868 } else if ( this.fieldLabel.length) {
14872 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14873 tooltip : 'This field is required'
14877 cls : 'control-label',
14878 html : this.fieldLabel
14889 if(this.indicatorpos == 'right'){
14893 cls : 'control-label',
14894 html : this.fieldLabel,
14898 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14899 tooltip : 'This field is required'
14916 var settings = this;
14918 ['xs','sm','md','lg'].map(function(size){
14919 if (settings[size]) {
14920 cfg.cls += ' col-' + size + '-' + settings[size];
14927 initTouchView : function()
14929 this.renderTouchView();
14931 this.touchViewEl.on('scroll', function(){
14932 this.el.dom.scrollTop = 0;
14935 this.originalValue = this.getValue();
14937 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14939 this.inputEl().on("click", this.showTouchView, this);
14940 if (this.triggerEl) {
14941 this.triggerEl.on("click", this.showTouchView, this);
14945 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14946 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14948 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14950 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14951 this.store.on('load', this.onTouchViewLoad, this);
14952 this.store.on('loadexception', this.onTouchViewLoadException, this);
14954 if(this.hiddenName){
14956 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14958 this.hiddenField.dom.value =
14959 this.hiddenValue !== undefined ? this.hiddenValue :
14960 this.value !== undefined ? this.value : '';
14962 this.el.dom.removeAttribute('name');
14963 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14967 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14968 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14971 if(this.removable && !this.multiple){
14972 var close = this.closeTriggerEl();
14974 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14975 close.on('click', this.removeBtnClick, this, close);
14979 * fix the bug in Safari iOS8
14981 this.inputEl().on("focus", function(e){
14982 document.activeElement.blur();
14990 renderTouchView : function()
14992 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14993 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14995 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14996 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14998 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14999 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15000 this.touchViewBodyEl.setStyle('overflow', 'auto');
15002 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15003 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15005 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15006 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15010 showTouchView : function()
15016 this.touchViewHeaderEl.hide();
15018 if(this.modalTitle.length){
15019 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15020 this.touchViewHeaderEl.show();
15023 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15024 this.touchViewEl.show();
15026 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15028 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15029 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15031 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15033 if(this.modalTitle.length){
15034 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15037 this.touchViewBodyEl.setHeight(bodyHeight);
15041 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15043 this.touchViewEl.addClass('in');
15046 this.doTouchViewQuery();
15050 hideTouchView : function()
15052 this.touchViewEl.removeClass('in');
15056 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15058 this.touchViewEl.setStyle('display', 'none');
15063 setTouchViewValue : function()
15070 Roo.each(this.tickItems, function(o){
15075 this.hideTouchView();
15078 doTouchViewQuery : function()
15087 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15091 if(!this.alwaysQuery || this.mode == 'local'){
15092 this.onTouchViewLoad();
15099 onTouchViewBeforeLoad : function(combo,opts)
15105 onTouchViewLoad : function()
15107 if(this.store.getCount() < 1){
15108 this.onTouchViewEmptyResults();
15112 this.clearTouchView();
15114 var rawValue = this.getRawValue();
15116 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15118 this.tickItems = [];
15120 this.store.data.each(function(d, rowIndex){
15121 var row = this.touchViewListGroup.createChild(template);
15123 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15124 row.addClass(d.data.cls);
15127 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15130 html : d.data[this.displayField]
15133 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15134 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15137 row.removeClass('selected');
15138 if(!this.multiple && this.valueField &&
15139 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15142 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15143 row.addClass('selected');
15146 if(this.multiple && this.valueField &&
15147 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15151 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15152 this.tickItems.push(d.data);
15155 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15159 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15161 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15163 if(this.modalTitle.length){
15164 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15167 var listHeight = this.touchViewListGroup.getHeight();
15171 if(firstChecked && listHeight > bodyHeight){
15172 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15177 onTouchViewLoadException : function()
15179 this.hideTouchView();
15182 onTouchViewEmptyResults : function()
15184 this.clearTouchView();
15186 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15188 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15192 clearTouchView : function()
15194 this.touchViewListGroup.dom.innerHTML = '';
15197 onTouchViewClick : function(e, el, o)
15199 e.preventDefault();
15202 var rowIndex = o.rowIndex;
15204 var r = this.store.getAt(rowIndex);
15206 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15208 if(!this.multiple){
15209 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15210 c.dom.removeAttribute('checked');
15213 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15215 this.setFromData(r.data);
15217 var close = this.closeTriggerEl();
15223 this.hideTouchView();
15225 this.fireEvent('select', this, r, rowIndex);
15230 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15231 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15232 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15236 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15237 this.addItem(r.data);
15238 this.tickItems.push(r.data);
15242 getAutoCreateNativeIOS : function()
15245 cls: 'form-group' //input-group,
15250 cls : 'roo-ios-select'
15254 combobox.name = this.name;
15257 if (this.disabled) {
15258 combobox.disabled = true;
15261 var settings = this;
15263 ['xs','sm','md','lg'].map(function(size){
15264 if (settings[size]) {
15265 cfg.cls += ' col-' + size + '-' + settings[size];
15275 initIOSView : function()
15277 this.store.on('load', this.onIOSViewLoad, this);
15282 onIOSViewLoad : function()
15284 if(this.store.getCount() < 1){
15288 this.clearIOSView();
15290 if(this.allowBlank) {
15292 var default_text = '-- SELECT --';
15294 if(this.placeholder.length){
15295 default_text = this.placeholder;
15298 if(this.emptyTitle.length){
15299 default_text += ' - ' + this.emptyTitle + ' -';
15302 var opt = this.inputEl().createChild({
15305 html : default_text
15309 o[this.valueField] = 0;
15310 o[this.displayField] = default_text;
15312 this.ios_options.push({
15319 this.store.data.each(function(d, rowIndex){
15323 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15324 html = d.data[this.displayField];
15329 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15330 value = d.data[this.valueField];
15339 if(this.value == d.data[this.valueField]){
15340 option['selected'] = true;
15343 var opt = this.inputEl().createChild(option);
15345 this.ios_options.push({
15352 this.inputEl().on('change', function(){
15353 this.fireEvent('select', this);
15358 clearIOSView: function()
15360 this.inputEl().dom.innerHTML = '';
15362 this.ios_options = [];
15365 setIOSValue: function(v)
15369 if(!this.ios_options){
15373 Roo.each(this.ios_options, function(opts){
15375 opts.el.dom.removeAttribute('selected');
15377 if(opts.data[this.valueField] != v){
15381 opts.el.dom.setAttribute('selected', true);
15387 * @cfg {Boolean} grow
15391 * @cfg {Number} growMin
15395 * @cfg {Number} growMax
15404 Roo.apply(Roo.bootstrap.ComboBox, {
15408 cls: 'modal-header',
15430 cls: 'list-group-item',
15434 cls: 'roo-combobox-list-group-item-value'
15438 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15452 listItemCheckbox : {
15454 cls: 'list-group-item',
15458 cls: 'roo-combobox-list-group-item-value'
15462 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15478 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15483 cls: 'modal-footer',
15491 cls: 'col-xs-6 text-left',
15494 cls: 'btn btn-danger roo-touch-view-cancel',
15500 cls: 'col-xs-6 text-right',
15503 cls: 'btn btn-success roo-touch-view-ok',
15514 Roo.apply(Roo.bootstrap.ComboBox, {
15516 touchViewTemplate : {
15518 cls: 'modal fade roo-combobox-touch-view',
15522 cls: 'modal-dialog',
15523 style : 'position:fixed', // we have to fix position....
15527 cls: 'modal-content',
15529 Roo.bootstrap.ComboBox.header,
15530 Roo.bootstrap.ComboBox.body,
15531 Roo.bootstrap.ComboBox.footer
15540 * Ext JS Library 1.1.1
15541 * Copyright(c) 2006-2007, Ext JS, LLC.
15543 * Originally Released Under LGPL - original licence link has changed is not relivant.
15546 * <script type="text/javascript">
15551 * @extends Roo.util.Observable
15552 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15553 * This class also supports single and multi selection modes. <br>
15554 * Create a data model bound view:
15556 var store = new Roo.data.Store(...);
15558 var view = new Roo.View({
15560 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15562 singleSelect: true,
15563 selectedClass: "ydataview-selected",
15567 // listen for node click?
15568 view.on("click", function(vw, index, node, e){
15569 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15573 dataModel.load("foobar.xml");
15575 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15577 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15578 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15580 * Note: old style constructor is still suported (container, template, config)
15583 * Create a new View
15584 * @param {Object} config The config object
15587 Roo.View = function(config, depreciated_tpl, depreciated_config){
15589 this.parent = false;
15591 if (typeof(depreciated_tpl) == 'undefined') {
15592 // new way.. - universal constructor.
15593 Roo.apply(this, config);
15594 this.el = Roo.get(this.el);
15597 this.el = Roo.get(config);
15598 this.tpl = depreciated_tpl;
15599 Roo.apply(this, depreciated_config);
15601 this.wrapEl = this.el.wrap().wrap();
15602 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15605 if(typeof(this.tpl) == "string"){
15606 this.tpl = new Roo.Template(this.tpl);
15608 // support xtype ctors..
15609 this.tpl = new Roo.factory(this.tpl, Roo);
15613 this.tpl.compile();
15618 * @event beforeclick
15619 * Fires before a click is processed. Returns false to cancel the default action.
15620 * @param {Roo.View} this
15621 * @param {Number} index The index of the target node
15622 * @param {HTMLElement} node The target node
15623 * @param {Roo.EventObject} e The raw event object
15625 "beforeclick" : true,
15628 * Fires when a template node is clicked.
15629 * @param {Roo.View} this
15630 * @param {Number} index The index of the target node
15631 * @param {HTMLElement} node The target node
15632 * @param {Roo.EventObject} e The raw event object
15637 * Fires when a template node is double clicked.
15638 * @param {Roo.View} this
15639 * @param {Number} index The index of the target node
15640 * @param {HTMLElement} node The target node
15641 * @param {Roo.EventObject} e The raw event object
15645 * @event contextmenu
15646 * Fires when a template node is right clicked.
15647 * @param {Roo.View} this
15648 * @param {Number} index The index of the target node
15649 * @param {HTMLElement} node The target node
15650 * @param {Roo.EventObject} e The raw event object
15652 "contextmenu" : true,
15654 * @event selectionchange
15655 * Fires when the selected nodes change.
15656 * @param {Roo.View} this
15657 * @param {Array} selections Array of the selected nodes
15659 "selectionchange" : true,
15662 * @event beforeselect
15663 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15664 * @param {Roo.View} this
15665 * @param {HTMLElement} node The node to be selected
15666 * @param {Array} selections Array of currently selected nodes
15668 "beforeselect" : true,
15670 * @event preparedata
15671 * Fires on every row to render, to allow you to change the data.
15672 * @param {Roo.View} this
15673 * @param {Object} data to be rendered (change this)
15675 "preparedata" : true
15683 "click": this.onClick,
15684 "dblclick": this.onDblClick,
15685 "contextmenu": this.onContextMenu,
15689 this.selections = [];
15691 this.cmp = new Roo.CompositeElementLite([]);
15693 this.store = Roo.factory(this.store, Roo.data);
15694 this.setStore(this.store, true);
15697 if ( this.footer && this.footer.xtype) {
15699 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15701 this.footer.dataSource = this.store;
15702 this.footer.container = fctr;
15703 this.footer = Roo.factory(this.footer, Roo);
15704 fctr.insertFirst(this.el);
15706 // this is a bit insane - as the paging toolbar seems to detach the el..
15707 // dom.parentNode.parentNode.parentNode
15708 // they get detached?
15712 Roo.View.superclass.constructor.call(this);
15717 Roo.extend(Roo.View, Roo.util.Observable, {
15720 * @cfg {Roo.data.Store} store Data store to load data from.
15725 * @cfg {String|Roo.Element} el The container element.
15730 * @cfg {String|Roo.Template} tpl The template used by this View
15734 * @cfg {String} dataName the named area of the template to use as the data area
15735 * Works with domtemplates roo-name="name"
15739 * @cfg {String} selectedClass The css class to add to selected nodes
15741 selectedClass : "x-view-selected",
15743 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15748 * @cfg {String} text to display on mask (default Loading)
15752 * @cfg {Boolean} multiSelect Allow multiple selection
15754 multiSelect : false,
15756 * @cfg {Boolean} singleSelect Allow single selection
15758 singleSelect: false,
15761 * @cfg {Boolean} toggleSelect - selecting
15763 toggleSelect : false,
15766 * @cfg {Boolean} tickable - selecting
15771 * Returns the element this view is bound to.
15772 * @return {Roo.Element}
15774 getEl : function(){
15775 return this.wrapEl;
15781 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15783 refresh : function(){
15784 //Roo.log('refresh');
15787 // if we are using something like 'domtemplate', then
15788 // the what gets used is:
15789 // t.applySubtemplate(NAME, data, wrapping data..)
15790 // the outer template then get' applied with
15791 // the store 'extra data'
15792 // and the body get's added to the
15793 // roo-name="data" node?
15794 // <span class='roo-tpl-{name}'></span> ?????
15798 this.clearSelections();
15799 this.el.update("");
15801 var records = this.store.getRange();
15802 if(records.length < 1) {
15804 // is this valid?? = should it render a template??
15806 this.el.update(this.emptyText);
15810 if (this.dataName) {
15811 this.el.update(t.apply(this.store.meta)); //????
15812 el = this.el.child('.roo-tpl-' + this.dataName);
15815 for(var i = 0, len = records.length; i < len; i++){
15816 var data = this.prepareData(records[i].data, i, records[i]);
15817 this.fireEvent("preparedata", this, data, i, records[i]);
15819 var d = Roo.apply({}, data);
15822 Roo.apply(d, {'roo-id' : Roo.id()});
15826 Roo.each(this.parent.item, function(item){
15827 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15830 Roo.apply(d, {'roo-data-checked' : 'checked'});
15834 html[html.length] = Roo.util.Format.trim(
15836 t.applySubtemplate(this.dataName, d, this.store.meta) :
15843 el.update(html.join(""));
15844 this.nodes = el.dom.childNodes;
15845 this.updateIndexes(0);
15850 * Function to override to reformat the data that is sent to
15851 * the template for each node.
15852 * DEPRICATED - use the preparedata event handler.
15853 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15854 * a JSON object for an UpdateManager bound view).
15856 prepareData : function(data, index, record)
15858 this.fireEvent("preparedata", this, data, index, record);
15862 onUpdate : function(ds, record){
15863 // Roo.log('on update');
15864 this.clearSelections();
15865 var index = this.store.indexOf(record);
15866 var n = this.nodes[index];
15867 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15868 n.parentNode.removeChild(n);
15869 this.updateIndexes(index, index);
15875 onAdd : function(ds, records, index)
15877 //Roo.log(['on Add', ds, records, index] );
15878 this.clearSelections();
15879 if(this.nodes.length == 0){
15883 var n = this.nodes[index];
15884 for(var i = 0, len = records.length; i < len; i++){
15885 var d = this.prepareData(records[i].data, i, records[i]);
15887 this.tpl.insertBefore(n, d);
15890 this.tpl.append(this.el, d);
15893 this.updateIndexes(index);
15896 onRemove : function(ds, record, index){
15897 // Roo.log('onRemove');
15898 this.clearSelections();
15899 var el = this.dataName ?
15900 this.el.child('.roo-tpl-' + this.dataName) :
15903 el.dom.removeChild(this.nodes[index]);
15904 this.updateIndexes(index);
15908 * Refresh an individual node.
15909 * @param {Number} index
15911 refreshNode : function(index){
15912 this.onUpdate(this.store, this.store.getAt(index));
15915 updateIndexes : function(startIndex, endIndex){
15916 var ns = this.nodes;
15917 startIndex = startIndex || 0;
15918 endIndex = endIndex || ns.length - 1;
15919 for(var i = startIndex; i <= endIndex; i++){
15920 ns[i].nodeIndex = i;
15925 * Changes the data store this view uses and refresh the view.
15926 * @param {Store} store
15928 setStore : function(store, initial){
15929 if(!initial && this.store){
15930 this.store.un("datachanged", this.refresh);
15931 this.store.un("add", this.onAdd);
15932 this.store.un("remove", this.onRemove);
15933 this.store.un("update", this.onUpdate);
15934 this.store.un("clear", this.refresh);
15935 this.store.un("beforeload", this.onBeforeLoad);
15936 this.store.un("load", this.onLoad);
15937 this.store.un("loadexception", this.onLoad);
15941 store.on("datachanged", this.refresh, this);
15942 store.on("add", this.onAdd, this);
15943 store.on("remove", this.onRemove, this);
15944 store.on("update", this.onUpdate, this);
15945 store.on("clear", this.refresh, this);
15946 store.on("beforeload", this.onBeforeLoad, this);
15947 store.on("load", this.onLoad, this);
15948 store.on("loadexception", this.onLoad, this);
15956 * onbeforeLoad - masks the loading area.
15959 onBeforeLoad : function(store,opts)
15961 //Roo.log('onBeforeLoad');
15963 this.el.update("");
15965 this.el.mask(this.mask ? this.mask : "Loading" );
15967 onLoad : function ()
15974 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15975 * @param {HTMLElement} node
15976 * @return {HTMLElement} The template node
15978 findItemFromChild : function(node){
15979 var el = this.dataName ?
15980 this.el.child('.roo-tpl-' + this.dataName,true) :
15983 if(!node || node.parentNode == el){
15986 var p = node.parentNode;
15987 while(p && p != el){
15988 if(p.parentNode == el){
15997 onClick : function(e){
15998 var item = this.findItemFromChild(e.getTarget());
16000 var index = this.indexOf(item);
16001 if(this.onItemClick(item, index, e) !== false){
16002 this.fireEvent("click", this, index, item, e);
16005 this.clearSelections();
16010 onContextMenu : function(e){
16011 var item = this.findItemFromChild(e.getTarget());
16013 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16018 onDblClick : function(e){
16019 var item = this.findItemFromChild(e.getTarget());
16021 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16025 onItemClick : function(item, index, e)
16027 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16030 if (this.toggleSelect) {
16031 var m = this.isSelected(item) ? 'unselect' : 'select';
16034 _t[m](item, true, false);
16037 if(this.multiSelect || this.singleSelect){
16038 if(this.multiSelect && e.shiftKey && this.lastSelection){
16039 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16041 this.select(item, this.multiSelect && e.ctrlKey);
16042 this.lastSelection = item;
16045 if(!this.tickable){
16046 e.preventDefault();
16054 * Get the number of selected nodes.
16057 getSelectionCount : function(){
16058 return this.selections.length;
16062 * Get the currently selected nodes.
16063 * @return {Array} An array of HTMLElements
16065 getSelectedNodes : function(){
16066 return this.selections;
16070 * Get the indexes of the selected nodes.
16073 getSelectedIndexes : function(){
16074 var indexes = [], s = this.selections;
16075 for(var i = 0, len = s.length; i < len; i++){
16076 indexes.push(s[i].nodeIndex);
16082 * Clear all selections
16083 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16085 clearSelections : function(suppressEvent){
16086 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16087 this.cmp.elements = this.selections;
16088 this.cmp.removeClass(this.selectedClass);
16089 this.selections = [];
16090 if(!suppressEvent){
16091 this.fireEvent("selectionchange", this, this.selections);
16097 * Returns true if the passed node is selected
16098 * @param {HTMLElement/Number} node The node or node index
16099 * @return {Boolean}
16101 isSelected : function(node){
16102 var s = this.selections;
16106 node = this.getNode(node);
16107 return s.indexOf(node) !== -1;
16112 * @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
16113 * @param {Boolean} keepExisting (optional) true to keep existing selections
16114 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16116 select : function(nodeInfo, keepExisting, suppressEvent){
16117 if(nodeInfo instanceof Array){
16119 this.clearSelections(true);
16121 for(var i = 0, len = nodeInfo.length; i < len; i++){
16122 this.select(nodeInfo[i], true, true);
16126 var node = this.getNode(nodeInfo);
16127 if(!node || this.isSelected(node)){
16128 return; // already selected.
16131 this.clearSelections(true);
16134 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16135 Roo.fly(node).addClass(this.selectedClass);
16136 this.selections.push(node);
16137 if(!suppressEvent){
16138 this.fireEvent("selectionchange", this, this.selections);
16146 * @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
16147 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16148 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16150 unselect : function(nodeInfo, keepExisting, suppressEvent)
16152 if(nodeInfo instanceof Array){
16153 Roo.each(this.selections, function(s) {
16154 this.unselect(s, nodeInfo);
16158 var node = this.getNode(nodeInfo);
16159 if(!node || !this.isSelected(node)){
16160 //Roo.log("not selected");
16161 return; // not selected.
16165 Roo.each(this.selections, function(s) {
16167 Roo.fly(node).removeClass(this.selectedClass);
16174 this.selections= ns;
16175 this.fireEvent("selectionchange", this, this.selections);
16179 * Gets a template node.
16180 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16181 * @return {HTMLElement} The node or null if it wasn't found
16183 getNode : function(nodeInfo){
16184 if(typeof nodeInfo == "string"){
16185 return document.getElementById(nodeInfo);
16186 }else if(typeof nodeInfo == "number"){
16187 return this.nodes[nodeInfo];
16193 * Gets a range template nodes.
16194 * @param {Number} startIndex
16195 * @param {Number} endIndex
16196 * @return {Array} An array of nodes
16198 getNodes : function(start, end){
16199 var ns = this.nodes;
16200 start = start || 0;
16201 end = typeof end == "undefined" ? ns.length - 1 : end;
16204 for(var i = start; i <= end; i++){
16208 for(var i = start; i >= end; i--){
16216 * Finds the index of the passed node
16217 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16218 * @return {Number} The index of the node or -1
16220 indexOf : function(node){
16221 node = this.getNode(node);
16222 if(typeof node.nodeIndex == "number"){
16223 return node.nodeIndex;
16225 var ns = this.nodes;
16226 for(var i = 0, len = ns.length; i < len; i++){
16237 * based on jquery fullcalendar
16241 Roo.bootstrap = Roo.bootstrap || {};
16243 * @class Roo.bootstrap.Calendar
16244 * @extends Roo.bootstrap.Component
16245 * Bootstrap Calendar class
16246 * @cfg {Boolean} loadMask (true|false) default false
16247 * @cfg {Object} header generate the user specific header of the calendar, default false
16250 * Create a new Container
16251 * @param {Object} config The config object
16256 Roo.bootstrap.Calendar = function(config){
16257 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16261 * Fires when a date is selected
16262 * @param {DatePicker} this
16263 * @param {Date} date The selected date
16267 * @event monthchange
16268 * Fires when the displayed month changes
16269 * @param {DatePicker} this
16270 * @param {Date} date The selected month
16272 'monthchange': true,
16274 * @event evententer
16275 * Fires when mouse over an event
16276 * @param {Calendar} this
16277 * @param {event} Event
16279 'evententer': true,
16281 * @event eventleave
16282 * Fires when the mouse leaves an
16283 * @param {Calendar} this
16286 'eventleave': true,
16288 * @event eventclick
16289 * Fires when the mouse click an
16290 * @param {Calendar} this
16299 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16302 * @cfg {Number} startDay
16303 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16311 getAutoCreate : function(){
16314 var fc_button = function(name, corner, style, content ) {
16315 return Roo.apply({},{
16317 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16319 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16322 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16333 style : 'width:100%',
16340 cls : 'fc-header-left',
16342 fc_button('prev', 'left', 'arrow', '‹' ),
16343 fc_button('next', 'right', 'arrow', '›' ),
16344 { tag: 'span', cls: 'fc-header-space' },
16345 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16353 cls : 'fc-header-center',
16357 cls: 'fc-header-title',
16360 html : 'month / year'
16368 cls : 'fc-header-right',
16370 /* fc_button('month', 'left', '', 'month' ),
16371 fc_button('week', '', '', 'week' ),
16372 fc_button('day', 'right', '', 'day' )
16384 header = this.header;
16387 var cal_heads = function() {
16389 // fixme - handle this.
16391 for (var i =0; i < Date.dayNames.length; i++) {
16392 var d = Date.dayNames[i];
16395 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16396 html : d.substring(0,3)
16400 ret[0].cls += ' fc-first';
16401 ret[6].cls += ' fc-last';
16404 var cal_cell = function(n) {
16407 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16412 cls: 'fc-day-number',
16416 cls: 'fc-day-content',
16420 style: 'position: relative;' // height: 17px;
16432 var cal_rows = function() {
16435 for (var r = 0; r < 6; r++) {
16442 for (var i =0; i < Date.dayNames.length; i++) {
16443 var d = Date.dayNames[i];
16444 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16447 row.cn[0].cls+=' fc-first';
16448 row.cn[0].cn[0].style = 'min-height:90px';
16449 row.cn[6].cls+=' fc-last';
16453 ret[0].cls += ' fc-first';
16454 ret[4].cls += ' fc-prev-last';
16455 ret[5].cls += ' fc-last';
16462 cls: 'fc-border-separate',
16463 style : 'width:100%',
16471 cls : 'fc-first fc-last',
16489 cls : 'fc-content',
16490 style : "position: relative;",
16493 cls : 'fc-view fc-view-month fc-grid',
16494 style : 'position: relative',
16495 unselectable : 'on',
16498 cls : 'fc-event-container',
16499 style : 'position:absolute;z-index:8;top:0;left:0;'
16517 initEvents : function()
16520 throw "can not find store for calendar";
16526 style: "text-align:center",
16530 style: "background-color:white;width:50%;margin:250 auto",
16534 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16545 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16547 var size = this.el.select('.fc-content', true).first().getSize();
16548 this.maskEl.setSize(size.width, size.height);
16549 this.maskEl.enableDisplayMode("block");
16550 if(!this.loadMask){
16551 this.maskEl.hide();
16554 this.store = Roo.factory(this.store, Roo.data);
16555 this.store.on('load', this.onLoad, this);
16556 this.store.on('beforeload', this.onBeforeLoad, this);
16560 this.cells = this.el.select('.fc-day',true);
16561 //Roo.log(this.cells);
16562 this.textNodes = this.el.query('.fc-day-number');
16563 this.cells.addClassOnOver('fc-state-hover');
16565 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16566 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16567 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16568 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16570 this.on('monthchange', this.onMonthChange, this);
16572 this.update(new Date().clearTime());
16575 resize : function() {
16576 var sz = this.el.getSize();
16578 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16579 this.el.select('.fc-day-content div',true).setHeight(34);
16584 showPrevMonth : function(e){
16585 this.update(this.activeDate.add("mo", -1));
16587 showToday : function(e){
16588 this.update(new Date().clearTime());
16591 showNextMonth : function(e){
16592 this.update(this.activeDate.add("mo", 1));
16596 showPrevYear : function(){
16597 this.update(this.activeDate.add("y", -1));
16601 showNextYear : function(){
16602 this.update(this.activeDate.add("y", 1));
16607 update : function(date)
16609 var vd = this.activeDate;
16610 this.activeDate = date;
16611 // if(vd && this.el){
16612 // var t = date.getTime();
16613 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16614 // Roo.log('using add remove');
16616 // this.fireEvent('monthchange', this, date);
16618 // this.cells.removeClass("fc-state-highlight");
16619 // this.cells.each(function(c){
16620 // if(c.dateValue == t){
16621 // c.addClass("fc-state-highlight");
16622 // setTimeout(function(){
16623 // try{c.dom.firstChild.focus();}catch(e){}
16633 var days = date.getDaysInMonth();
16635 var firstOfMonth = date.getFirstDateOfMonth();
16636 var startingPos = firstOfMonth.getDay()-this.startDay;
16638 if(startingPos < this.startDay){
16642 var pm = date.add(Date.MONTH, -1);
16643 var prevStart = pm.getDaysInMonth()-startingPos;
16645 this.cells = this.el.select('.fc-day',true);
16646 this.textNodes = this.el.query('.fc-day-number');
16647 this.cells.addClassOnOver('fc-state-hover');
16649 var cells = this.cells.elements;
16650 var textEls = this.textNodes;
16652 Roo.each(cells, function(cell){
16653 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16656 days += startingPos;
16658 // convert everything to numbers so it's fast
16659 var day = 86400000;
16660 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16663 //Roo.log(prevStart);
16665 var today = new Date().clearTime().getTime();
16666 var sel = date.clearTime().getTime();
16667 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16668 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16669 var ddMatch = this.disabledDatesRE;
16670 var ddText = this.disabledDatesText;
16671 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16672 var ddaysText = this.disabledDaysText;
16673 var format = this.format;
16675 var setCellClass = function(cal, cell){
16679 //Roo.log('set Cell Class');
16681 var t = d.getTime();
16685 cell.dateValue = t;
16687 cell.className += " fc-today";
16688 cell.className += " fc-state-highlight";
16689 cell.title = cal.todayText;
16692 // disable highlight in other month..
16693 //cell.className += " fc-state-highlight";
16698 cell.className = " fc-state-disabled";
16699 cell.title = cal.minText;
16703 cell.className = " fc-state-disabled";
16704 cell.title = cal.maxText;
16708 if(ddays.indexOf(d.getDay()) != -1){
16709 cell.title = ddaysText;
16710 cell.className = " fc-state-disabled";
16713 if(ddMatch && format){
16714 var fvalue = d.dateFormat(format);
16715 if(ddMatch.test(fvalue)){
16716 cell.title = ddText.replace("%0", fvalue);
16717 cell.className = " fc-state-disabled";
16721 if (!cell.initialClassName) {
16722 cell.initialClassName = cell.dom.className;
16725 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16730 for(; i < startingPos; i++) {
16731 textEls[i].innerHTML = (++prevStart);
16732 d.setDate(d.getDate()+1);
16734 cells[i].className = "fc-past fc-other-month";
16735 setCellClass(this, cells[i]);
16740 for(; i < days; i++){
16741 intDay = i - startingPos + 1;
16742 textEls[i].innerHTML = (intDay);
16743 d.setDate(d.getDate()+1);
16745 cells[i].className = ''; // "x-date-active";
16746 setCellClass(this, cells[i]);
16750 for(; i < 42; i++) {
16751 textEls[i].innerHTML = (++extraDays);
16752 d.setDate(d.getDate()+1);
16754 cells[i].className = "fc-future fc-other-month";
16755 setCellClass(this, cells[i]);
16758 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16760 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16762 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16763 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16765 if(totalRows != 6){
16766 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16767 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16770 this.fireEvent('monthchange', this, date);
16774 if(!this.internalRender){
16775 var main = this.el.dom.firstChild;
16776 var w = main.offsetWidth;
16777 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16778 Roo.fly(main).setWidth(w);
16779 this.internalRender = true;
16780 // opera does not respect the auto grow header center column
16781 // then, after it gets a width opera refuses to recalculate
16782 // without a second pass
16783 if(Roo.isOpera && !this.secondPass){
16784 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16785 this.secondPass = true;
16786 this.update.defer(10, this, [date]);
16793 findCell : function(dt) {
16794 dt = dt.clearTime().getTime();
16796 this.cells.each(function(c){
16797 //Roo.log("check " +c.dateValue + '?=' + dt);
16798 if(c.dateValue == dt){
16808 findCells : function(ev) {
16809 var s = ev.start.clone().clearTime().getTime();
16811 var e= ev.end.clone().clearTime().getTime();
16814 this.cells.each(function(c){
16815 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16817 if(c.dateValue > e){
16820 if(c.dateValue < s){
16829 // findBestRow: function(cells)
16833 // for (var i =0 ; i < cells.length;i++) {
16834 // ret = Math.max(cells[i].rows || 0,ret);
16841 addItem : function(ev)
16843 // look for vertical location slot in
16844 var cells = this.findCells(ev);
16846 // ev.row = this.findBestRow(cells);
16848 // work out the location.
16852 for(var i =0; i < cells.length; i++) {
16854 cells[i].row = cells[0].row;
16857 cells[i].row = cells[i].row + 1;
16867 if (crow.start.getY() == cells[i].getY()) {
16869 crow.end = cells[i];
16886 cells[0].events.push(ev);
16888 this.calevents.push(ev);
16891 clearEvents: function() {
16893 if(!this.calevents){
16897 Roo.each(this.cells.elements, function(c){
16903 Roo.each(this.calevents, function(e) {
16904 Roo.each(e.els, function(el) {
16905 el.un('mouseenter' ,this.onEventEnter, this);
16906 el.un('mouseleave' ,this.onEventLeave, this);
16911 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16917 renderEvents: function()
16921 this.cells.each(function(c) {
16930 if(c.row != c.events.length){
16931 r = 4 - (4 - (c.row - c.events.length));
16934 c.events = ev.slice(0, r);
16935 c.more = ev.slice(r);
16937 if(c.more.length && c.more.length == 1){
16938 c.events.push(c.more.pop());
16941 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16945 this.cells.each(function(c) {
16947 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16950 for (var e = 0; e < c.events.length; e++){
16951 var ev = c.events[e];
16952 var rows = ev.rows;
16954 for(var i = 0; i < rows.length; i++) {
16956 // how many rows should it span..
16959 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16960 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16962 unselectable : "on",
16965 cls: 'fc-event-inner',
16969 // cls: 'fc-event-time',
16970 // html : cells.length > 1 ? '' : ev.time
16974 cls: 'fc-event-title',
16975 html : String.format('{0}', ev.title)
16982 cls: 'ui-resizable-handle ui-resizable-e',
16983 html : '  '
16990 cfg.cls += ' fc-event-start';
16992 if ((i+1) == rows.length) {
16993 cfg.cls += ' fc-event-end';
16996 var ctr = _this.el.select('.fc-event-container',true).first();
16997 var cg = ctr.createChild(cfg);
16999 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17000 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17002 var r = (c.more.length) ? 1 : 0;
17003 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17004 cg.setWidth(ebox.right - sbox.x -2);
17006 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17007 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17008 cg.on('click', _this.onEventClick, _this, ev);
17019 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17020 style : 'position: absolute',
17021 unselectable : "on",
17024 cls: 'fc-event-inner',
17028 cls: 'fc-event-title',
17036 cls: 'ui-resizable-handle ui-resizable-e',
17037 html : '  '
17043 var ctr = _this.el.select('.fc-event-container',true).first();
17044 var cg = ctr.createChild(cfg);
17046 var sbox = c.select('.fc-day-content',true).first().getBox();
17047 var ebox = c.select('.fc-day-content',true).first().getBox();
17049 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17050 cg.setWidth(ebox.right - sbox.x -2);
17052 cg.on('click', _this.onMoreEventClick, _this, c.more);
17062 onEventEnter: function (e, el,event,d) {
17063 this.fireEvent('evententer', this, el, event);
17066 onEventLeave: function (e, el,event,d) {
17067 this.fireEvent('eventleave', this, el, event);
17070 onEventClick: function (e, el,event,d) {
17071 this.fireEvent('eventclick', this, el, event);
17074 onMonthChange: function () {
17078 onMoreEventClick: function(e, el, more)
17082 this.calpopover.placement = 'right';
17083 this.calpopover.setTitle('More');
17085 this.calpopover.setContent('');
17087 var ctr = this.calpopover.el.select('.popover-content', true).first();
17089 Roo.each(more, function(m){
17091 cls : 'fc-event-hori fc-event-draggable',
17094 var cg = ctr.createChild(cfg);
17096 cg.on('click', _this.onEventClick, _this, m);
17099 this.calpopover.show(el);
17104 onLoad: function ()
17106 this.calevents = [];
17109 if(this.store.getCount() > 0){
17110 this.store.data.each(function(d){
17113 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17114 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17115 time : d.data.start_time,
17116 title : d.data.title,
17117 description : d.data.description,
17118 venue : d.data.venue
17123 this.renderEvents();
17125 if(this.calevents.length && this.loadMask){
17126 this.maskEl.hide();
17130 onBeforeLoad: function()
17132 this.clearEvents();
17134 this.maskEl.show();
17148 * @class Roo.bootstrap.Popover
17149 * @extends Roo.bootstrap.Component
17150 * Bootstrap Popover class
17151 * @cfg {String} html contents of the popover (or false to use children..)
17152 * @cfg {String} title of popover (or false to hide)
17153 * @cfg {String} placement how it is placed
17154 * @cfg {String} trigger click || hover (or false to trigger manually)
17155 * @cfg {String} over what (parent or false to trigger manually.)
17156 * @cfg {Number} delay - delay before showing
17159 * Create a new Popover
17160 * @param {Object} config The config object
17163 Roo.bootstrap.Popover = function(config){
17164 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17170 * After the popover show
17172 * @param {Roo.bootstrap.Popover} this
17177 * After the popover hide
17179 * @param {Roo.bootstrap.Popover} this
17185 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17187 title: 'Fill in a title',
17190 placement : 'right',
17191 trigger : 'hover', // hover
17197 can_build_overlaid : false,
17199 getChildContainer : function()
17201 return this.el.select('.popover-content',true).first();
17204 getAutoCreate : function(){
17207 cls : 'popover roo-dynamic',
17208 style: 'display:block',
17214 cls : 'popover-inner',
17218 cls: 'popover-title',
17222 cls : 'popover-content',
17233 setTitle: function(str)
17236 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17238 setContent: function(str)
17241 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17243 // as it get's added to the bottom of the page.
17244 onRender : function(ct, position)
17246 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17248 var cfg = Roo.apply({}, this.getAutoCreate());
17252 cfg.cls += ' ' + this.cls;
17255 cfg.style = this.style;
17257 //Roo.log("adding to ");
17258 this.el = Roo.get(document.body).createChild(cfg, position);
17259 // Roo.log(this.el);
17264 initEvents : function()
17266 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17267 this.el.enableDisplayMode('block');
17269 if (this.over === false) {
17272 if (this.triggers === false) {
17275 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17276 var triggers = this.trigger ? this.trigger.split(' ') : [];
17277 Roo.each(triggers, function(trigger) {
17279 if (trigger == 'click') {
17280 on_el.on('click', this.toggle, this);
17281 } else if (trigger != 'manual') {
17282 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17283 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17285 on_el.on(eventIn ,this.enter, this);
17286 on_el.on(eventOut, this.leave, this);
17297 toggle : function () {
17298 this.hoverState == 'in' ? this.leave() : this.enter();
17301 enter : function () {
17303 clearTimeout(this.timeout);
17305 this.hoverState = 'in';
17307 if (!this.delay || !this.delay.show) {
17312 this.timeout = setTimeout(function () {
17313 if (_t.hoverState == 'in') {
17316 }, this.delay.show)
17319 leave : function() {
17320 clearTimeout(this.timeout);
17322 this.hoverState = 'out';
17324 if (!this.delay || !this.delay.hide) {
17329 this.timeout = setTimeout(function () {
17330 if (_t.hoverState == 'out') {
17333 }, this.delay.hide)
17336 show : function (on_el)
17339 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17343 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17344 if (this.html !== false) {
17345 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17347 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17348 if (!this.title.length) {
17349 this.el.select('.popover-title',true).hide();
17352 var placement = typeof this.placement == 'function' ?
17353 this.placement.call(this, this.el, on_el) :
17356 var autoToken = /\s?auto?\s?/i;
17357 var autoPlace = autoToken.test(placement);
17359 placement = placement.replace(autoToken, '') || 'top';
17363 //this.el.setXY([0,0]);
17365 this.el.dom.style.display='block';
17366 this.el.addClass(placement);
17368 //this.el.appendTo(on_el);
17370 var p = this.getPosition();
17371 var box = this.el.getBox();
17376 var align = Roo.bootstrap.Popover.alignment[placement];
17379 this.el.alignTo(on_el, align[0],align[1]);
17380 //var arrow = this.el.select('.arrow',true).first();
17381 //arrow.set(align[2],
17383 this.el.addClass('in');
17386 if (this.el.hasClass('fade')) {
17390 this.hoverState = 'in';
17392 this.fireEvent('show', this);
17397 this.el.setXY([0,0]);
17398 this.el.removeClass('in');
17400 this.hoverState = null;
17402 this.fireEvent('hide', this);
17407 Roo.bootstrap.Popover.alignment = {
17408 'left' : ['r-l', [-10,0], 'right'],
17409 'right' : ['l-r', [10,0], 'left'],
17410 'bottom' : ['t-b', [0,10], 'top'],
17411 'top' : [ 'b-t', [0,-10], 'bottom']
17422 * @class Roo.bootstrap.Progress
17423 * @extends Roo.bootstrap.Component
17424 * Bootstrap Progress class
17425 * @cfg {Boolean} striped striped of the progress bar
17426 * @cfg {Boolean} active animated of the progress bar
17430 * Create a new Progress
17431 * @param {Object} config The config object
17434 Roo.bootstrap.Progress = function(config){
17435 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17438 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17443 getAutoCreate : function(){
17451 cfg.cls += ' progress-striped';
17455 cfg.cls += ' active';
17474 * @class Roo.bootstrap.ProgressBar
17475 * @extends Roo.bootstrap.Component
17476 * Bootstrap ProgressBar class
17477 * @cfg {Number} aria_valuenow aria-value now
17478 * @cfg {Number} aria_valuemin aria-value min
17479 * @cfg {Number} aria_valuemax aria-value max
17480 * @cfg {String} label label for the progress bar
17481 * @cfg {String} panel (success | info | warning | danger )
17482 * @cfg {String} role role of the progress bar
17483 * @cfg {String} sr_only text
17487 * Create a new ProgressBar
17488 * @param {Object} config The config object
17491 Roo.bootstrap.ProgressBar = function(config){
17492 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17495 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17499 aria_valuemax : 100,
17505 getAutoCreate : function()
17510 cls: 'progress-bar',
17511 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17523 cfg.role = this.role;
17526 if(this.aria_valuenow){
17527 cfg['aria-valuenow'] = this.aria_valuenow;
17530 if(this.aria_valuemin){
17531 cfg['aria-valuemin'] = this.aria_valuemin;
17534 if(this.aria_valuemax){
17535 cfg['aria-valuemax'] = this.aria_valuemax;
17538 if(this.label && !this.sr_only){
17539 cfg.html = this.label;
17543 cfg.cls += ' progress-bar-' + this.panel;
17549 update : function(aria_valuenow)
17551 this.aria_valuenow = aria_valuenow;
17553 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17568 * @class Roo.bootstrap.TabGroup
17569 * @extends Roo.bootstrap.Column
17570 * Bootstrap Column class
17571 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17572 * @cfg {Boolean} carousel true to make the group behave like a carousel
17573 * @cfg {Boolean} bullets show bullets for the panels
17574 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17575 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17576 * @cfg {Boolean} showarrow (true|false) show arrow default true
17579 * Create a new TabGroup
17580 * @param {Object} config The config object
17583 Roo.bootstrap.TabGroup = function(config){
17584 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17586 this.navId = Roo.id();
17589 Roo.bootstrap.TabGroup.register(this);
17593 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17596 transition : false,
17601 slideOnTouch : false,
17604 getAutoCreate : function()
17606 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17608 cfg.cls += ' tab-content';
17610 if (this.carousel) {
17611 cfg.cls += ' carousel slide';
17614 cls : 'carousel-inner',
17618 if(this.bullets && !Roo.isTouch){
17621 cls : 'carousel-bullets',
17625 if(this.bullets_cls){
17626 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17633 cfg.cn[0].cn.push(bullets);
17636 if(this.showarrow){
17637 cfg.cn[0].cn.push({
17639 class : 'carousel-arrow',
17643 class : 'carousel-prev',
17647 class : 'fa fa-chevron-left'
17653 class : 'carousel-next',
17657 class : 'fa fa-chevron-right'
17670 initEvents: function()
17672 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17673 // this.el.on("touchstart", this.onTouchStart, this);
17676 if(this.autoslide){
17679 this.slideFn = window.setInterval(function() {
17680 _this.showPanelNext();
17684 if(this.showarrow){
17685 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17686 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17692 // onTouchStart : function(e, el, o)
17694 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17698 // this.showPanelNext();
17702 getChildContainer : function()
17704 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17708 * register a Navigation item
17709 * @param {Roo.bootstrap.NavItem} the navitem to add
17711 register : function(item)
17713 this.tabs.push( item);
17714 item.navId = this.navId; // not really needed..
17719 getActivePanel : function()
17722 Roo.each(this.tabs, function(t) {
17732 getPanelByName : function(n)
17735 Roo.each(this.tabs, function(t) {
17736 if (t.tabId == n) {
17744 indexOfPanel : function(p)
17747 Roo.each(this.tabs, function(t,i) {
17748 if (t.tabId == p.tabId) {
17757 * show a specific panel
17758 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17759 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17761 showPanel : function (pan)
17763 if(this.transition || typeof(pan) == 'undefined'){
17764 Roo.log("waiting for the transitionend");
17768 if (typeof(pan) == 'number') {
17769 pan = this.tabs[pan];
17772 if (typeof(pan) == 'string') {
17773 pan = this.getPanelByName(pan);
17776 var cur = this.getActivePanel();
17779 Roo.log('pan or acitve pan is undefined');
17783 if (pan.tabId == this.getActivePanel().tabId) {
17787 if (false === cur.fireEvent('beforedeactivate')) {
17791 if(this.bullets > 0 && !Roo.isTouch){
17792 this.setActiveBullet(this.indexOfPanel(pan));
17795 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17797 this.transition = true;
17798 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17799 var lr = dir == 'next' ? 'left' : 'right';
17800 pan.el.addClass(dir); // or prev
17801 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17802 cur.el.addClass(lr); // or right
17803 pan.el.addClass(lr);
17806 cur.el.on('transitionend', function() {
17807 Roo.log("trans end?");
17809 pan.el.removeClass([lr,dir]);
17810 pan.setActive(true);
17812 cur.el.removeClass([lr]);
17813 cur.setActive(false);
17815 _this.transition = false;
17817 }, this, { single: true } );
17822 cur.setActive(false);
17823 pan.setActive(true);
17828 showPanelNext : function()
17830 var i = this.indexOfPanel(this.getActivePanel());
17832 if (i >= this.tabs.length - 1 && !this.autoslide) {
17836 if (i >= this.tabs.length - 1 && this.autoslide) {
17840 this.showPanel(this.tabs[i+1]);
17843 showPanelPrev : function()
17845 var i = this.indexOfPanel(this.getActivePanel());
17847 if (i < 1 && !this.autoslide) {
17851 if (i < 1 && this.autoslide) {
17852 i = this.tabs.length;
17855 this.showPanel(this.tabs[i-1]);
17859 addBullet: function()
17861 if(!this.bullets || Roo.isTouch){
17864 var ctr = this.el.select('.carousel-bullets',true).first();
17865 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17866 var bullet = ctr.createChild({
17867 cls : 'bullet bullet-' + i
17868 },ctr.dom.lastChild);
17873 bullet.on('click', (function(e, el, o, ii, t){
17875 e.preventDefault();
17877 this.showPanel(ii);
17879 if(this.autoslide && this.slideFn){
17880 clearInterval(this.slideFn);
17881 this.slideFn = window.setInterval(function() {
17882 _this.showPanelNext();
17886 }).createDelegate(this, [i, bullet], true));
17891 setActiveBullet : function(i)
17897 Roo.each(this.el.select('.bullet', true).elements, function(el){
17898 el.removeClass('selected');
17901 var bullet = this.el.select('.bullet-' + i, true).first();
17907 bullet.addClass('selected');
17918 Roo.apply(Roo.bootstrap.TabGroup, {
17922 * register a Navigation Group
17923 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17925 register : function(navgrp)
17927 this.groups[navgrp.navId] = navgrp;
17931 * fetch a Navigation Group based on the navigation ID
17932 * if one does not exist , it will get created.
17933 * @param {string} the navgroup to add
17934 * @returns {Roo.bootstrap.NavGroup} the navgroup
17936 get: function(navId) {
17937 if (typeof(this.groups[navId]) == 'undefined') {
17938 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17940 return this.groups[navId] ;
17955 * @class Roo.bootstrap.TabPanel
17956 * @extends Roo.bootstrap.Component
17957 * Bootstrap TabPanel class
17958 * @cfg {Boolean} active panel active
17959 * @cfg {String} html panel content
17960 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17961 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17962 * @cfg {String} href click to link..
17966 * Create a new TabPanel
17967 * @param {Object} config The config object
17970 Roo.bootstrap.TabPanel = function(config){
17971 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17975 * Fires when the active status changes
17976 * @param {Roo.bootstrap.TabPanel} this
17977 * @param {Boolean} state the new state
17982 * @event beforedeactivate
17983 * Fires before a tab is de-activated - can be used to do validation on a form.
17984 * @param {Roo.bootstrap.TabPanel} this
17985 * @return {Boolean} false if there is an error
17988 'beforedeactivate': true
17991 this.tabId = this.tabId || Roo.id();
17995 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18003 getAutoCreate : function(){
18006 // item is needed for carousel - not sure if it has any effect otherwise
18007 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18008 html: this.html || ''
18012 cfg.cls += ' active';
18016 cfg.tabId = this.tabId;
18023 initEvents: function()
18025 var p = this.parent();
18027 this.navId = this.navId || p.navId;
18029 if (typeof(this.navId) != 'undefined') {
18030 // not really needed.. but just in case.. parent should be a NavGroup.
18031 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18035 var i = tg.tabs.length - 1;
18037 if(this.active && tg.bullets > 0 && i < tg.bullets){
18038 tg.setActiveBullet(i);
18042 this.el.on('click', this.onClick, this);
18045 this.el.on("touchstart", this.onTouchStart, this);
18046 this.el.on("touchmove", this.onTouchMove, this);
18047 this.el.on("touchend", this.onTouchEnd, this);
18052 onRender : function(ct, position)
18054 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18057 setActive : function(state)
18059 Roo.log("panel - set active " + this.tabId + "=" + state);
18061 this.active = state;
18063 this.el.removeClass('active');
18065 } else if (!this.el.hasClass('active')) {
18066 this.el.addClass('active');
18069 this.fireEvent('changed', this, state);
18072 onClick : function(e)
18074 e.preventDefault();
18076 if(!this.href.length){
18080 window.location.href = this.href;
18089 onTouchStart : function(e)
18091 this.swiping = false;
18093 this.startX = e.browserEvent.touches[0].clientX;
18094 this.startY = e.browserEvent.touches[0].clientY;
18097 onTouchMove : function(e)
18099 this.swiping = true;
18101 this.endX = e.browserEvent.touches[0].clientX;
18102 this.endY = e.browserEvent.touches[0].clientY;
18105 onTouchEnd : function(e)
18112 var tabGroup = this.parent();
18114 if(this.endX > this.startX){ // swiping right
18115 tabGroup.showPanelPrev();
18119 if(this.startX > this.endX){ // swiping left
18120 tabGroup.showPanelNext();
18139 * @class Roo.bootstrap.DateField
18140 * @extends Roo.bootstrap.Input
18141 * Bootstrap DateField class
18142 * @cfg {Number} weekStart default 0
18143 * @cfg {String} viewMode default empty, (months|years)
18144 * @cfg {String} minViewMode default empty, (months|years)
18145 * @cfg {Number} startDate default -Infinity
18146 * @cfg {Number} endDate default Infinity
18147 * @cfg {Boolean} todayHighlight default false
18148 * @cfg {Boolean} todayBtn default false
18149 * @cfg {Boolean} calendarWeeks default false
18150 * @cfg {Object} daysOfWeekDisabled default empty
18151 * @cfg {Boolean} singleMode default false (true | false)
18153 * @cfg {Boolean} keyboardNavigation default true
18154 * @cfg {String} language default en
18157 * Create a new DateField
18158 * @param {Object} config The config object
18161 Roo.bootstrap.DateField = function(config){
18162 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18166 * Fires when this field show.
18167 * @param {Roo.bootstrap.DateField} this
18168 * @param {Mixed} date The date value
18173 * Fires when this field hide.
18174 * @param {Roo.bootstrap.DateField} this
18175 * @param {Mixed} date The date value
18180 * Fires when select a date.
18181 * @param {Roo.bootstrap.DateField} this
18182 * @param {Mixed} date The date value
18186 * @event beforeselect
18187 * Fires when before select a date.
18188 * @param {Roo.bootstrap.DateField} this
18189 * @param {Mixed} date The date value
18191 beforeselect : true
18195 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18198 * @cfg {String} format
18199 * The default date format string which can be overriden for localization support. The format must be
18200 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18204 * @cfg {String} altFormats
18205 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18206 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18208 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18216 todayHighlight : false,
18222 keyboardNavigation: true,
18224 calendarWeeks: false,
18226 startDate: -Infinity,
18230 daysOfWeekDisabled: [],
18234 singleMode : false,
18236 UTCDate: function()
18238 return new Date(Date.UTC.apply(Date, arguments));
18241 UTCToday: function()
18243 var today = new Date();
18244 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18247 getDate: function() {
18248 var d = this.getUTCDate();
18249 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18252 getUTCDate: function() {
18256 setDate: function(d) {
18257 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18260 setUTCDate: function(d) {
18262 this.setValue(this.formatDate(this.date));
18265 onRender: function(ct, position)
18268 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18270 this.language = this.language || 'en';
18271 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18272 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18274 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18275 this.format = this.format || 'm/d/y';
18276 this.isInline = false;
18277 this.isInput = true;
18278 this.component = this.el.select('.add-on', true).first() || false;
18279 this.component = (this.component && this.component.length === 0) ? false : this.component;
18280 this.hasInput = this.component && this.inputEl().length;
18282 if (typeof(this.minViewMode === 'string')) {
18283 switch (this.minViewMode) {
18285 this.minViewMode = 1;
18288 this.minViewMode = 2;
18291 this.minViewMode = 0;
18296 if (typeof(this.viewMode === 'string')) {
18297 switch (this.viewMode) {
18310 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18312 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18314 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18316 this.picker().on('mousedown', this.onMousedown, this);
18317 this.picker().on('click', this.onClick, this);
18319 this.picker().addClass('datepicker-dropdown');
18321 this.startViewMode = this.viewMode;
18323 if(this.singleMode){
18324 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18325 v.setVisibilityMode(Roo.Element.DISPLAY);
18329 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18330 v.setStyle('width', '189px');
18334 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18335 if(!this.calendarWeeks){
18340 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18341 v.attr('colspan', function(i, val){
18342 return parseInt(val) + 1;
18347 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18349 this.setStartDate(this.startDate);
18350 this.setEndDate(this.endDate);
18352 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18359 if(this.isInline) {
18364 picker : function()
18366 return this.pickerEl;
18367 // return this.el.select('.datepicker', true).first();
18370 fillDow: function()
18372 var dowCnt = this.weekStart;
18381 if(this.calendarWeeks){
18389 while (dowCnt < this.weekStart + 7) {
18393 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18397 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18400 fillMonths: function()
18403 var months = this.picker().select('>.datepicker-months td', true).first();
18405 months.dom.innerHTML = '';
18411 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18414 months.createChild(month);
18421 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;
18423 if (this.date < this.startDate) {
18424 this.viewDate = new Date(this.startDate);
18425 } else if (this.date > this.endDate) {
18426 this.viewDate = new Date(this.endDate);
18428 this.viewDate = new Date(this.date);
18436 var d = new Date(this.viewDate),
18437 year = d.getUTCFullYear(),
18438 month = d.getUTCMonth(),
18439 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18440 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18441 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18442 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18443 currentDate = this.date && this.date.valueOf(),
18444 today = this.UTCToday();
18446 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18448 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18450 // this.picker.select('>tfoot th.today').
18451 // .text(dates[this.language].today)
18452 // .toggle(this.todayBtn !== false);
18454 this.updateNavArrows();
18457 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18459 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18461 prevMonth.setUTCDate(day);
18463 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18465 var nextMonth = new Date(prevMonth);
18467 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18469 nextMonth = nextMonth.valueOf();
18471 var fillMonths = false;
18473 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18475 while(prevMonth.valueOf() < nextMonth) {
18478 if (prevMonth.getUTCDay() === this.weekStart) {
18480 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18488 if(this.calendarWeeks){
18489 // ISO 8601: First week contains first thursday.
18490 // ISO also states week starts on Monday, but we can be more abstract here.
18492 // Start of current week: based on weekstart/current date
18493 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18494 // Thursday of this week
18495 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18496 // First Thursday of year, year from thursday
18497 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18498 // Calendar week: ms between thursdays, div ms per day, div 7 days
18499 calWeek = (th - yth) / 864e5 / 7 + 1;
18501 fillMonths.cn.push({
18509 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18511 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18514 if (this.todayHighlight &&
18515 prevMonth.getUTCFullYear() == today.getFullYear() &&
18516 prevMonth.getUTCMonth() == today.getMonth() &&
18517 prevMonth.getUTCDate() == today.getDate()) {
18518 clsName += ' today';
18521 if (currentDate && prevMonth.valueOf() === currentDate) {
18522 clsName += ' active';
18525 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18526 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18527 clsName += ' disabled';
18530 fillMonths.cn.push({
18532 cls: 'day ' + clsName,
18533 html: prevMonth.getDate()
18536 prevMonth.setDate(prevMonth.getDate()+1);
18539 var currentYear = this.date && this.date.getUTCFullYear();
18540 var currentMonth = this.date && this.date.getUTCMonth();
18542 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18544 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18545 v.removeClass('active');
18547 if(currentYear === year && k === currentMonth){
18548 v.addClass('active');
18551 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18552 v.addClass('disabled');
18558 year = parseInt(year/10, 10) * 10;
18560 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18562 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18565 for (var i = -1; i < 11; i++) {
18566 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18568 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18576 showMode: function(dir)
18579 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18582 Roo.each(this.picker().select('>div',true).elements, function(v){
18583 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18586 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18591 if(this.isInline) {
18595 this.picker().removeClass(['bottom', 'top']);
18597 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18599 * place to the top of element!
18603 this.picker().addClass('top');
18604 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18609 this.picker().addClass('bottom');
18611 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18614 parseDate : function(value)
18616 if(!value || value instanceof Date){
18619 var v = Date.parseDate(value, this.format);
18620 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18621 v = Date.parseDate(value, 'Y-m-d');
18623 if(!v && this.altFormats){
18624 if(!this.altFormatsArray){
18625 this.altFormatsArray = this.altFormats.split("|");
18627 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18628 v = Date.parseDate(value, this.altFormatsArray[i]);
18634 formatDate : function(date, fmt)
18636 return (!date || !(date instanceof Date)) ?
18637 date : date.dateFormat(fmt || this.format);
18640 onFocus : function()
18642 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18646 onBlur : function()
18648 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18650 var d = this.inputEl().getValue();
18659 this.picker().show();
18663 this.fireEvent('show', this, this.date);
18668 if(this.isInline) {
18671 this.picker().hide();
18672 this.viewMode = this.startViewMode;
18675 this.fireEvent('hide', this, this.date);
18679 onMousedown: function(e)
18681 e.stopPropagation();
18682 e.preventDefault();
18687 Roo.bootstrap.DateField.superclass.keyup.call(this);
18691 setValue: function(v)
18693 if(this.fireEvent('beforeselect', this, v) !== false){
18694 var d = new Date(this.parseDate(v) ).clearTime();
18696 if(isNaN(d.getTime())){
18697 this.date = this.viewDate = '';
18698 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18702 v = this.formatDate(d);
18704 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18706 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18710 this.fireEvent('select', this, this.date);
18714 getValue: function()
18716 return this.formatDate(this.date);
18719 fireKey: function(e)
18721 if (!this.picker().isVisible()){
18722 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18728 var dateChanged = false,
18730 newDate, newViewDate;
18735 e.preventDefault();
18739 if (!this.keyboardNavigation) {
18742 dir = e.keyCode == 37 ? -1 : 1;
18745 newDate = this.moveYear(this.date, dir);
18746 newViewDate = this.moveYear(this.viewDate, dir);
18747 } else if (e.shiftKey){
18748 newDate = this.moveMonth(this.date, dir);
18749 newViewDate = this.moveMonth(this.viewDate, dir);
18751 newDate = new Date(this.date);
18752 newDate.setUTCDate(this.date.getUTCDate() + dir);
18753 newViewDate = new Date(this.viewDate);
18754 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18756 if (this.dateWithinRange(newDate)){
18757 this.date = newDate;
18758 this.viewDate = newViewDate;
18759 this.setValue(this.formatDate(this.date));
18761 e.preventDefault();
18762 dateChanged = true;
18767 if (!this.keyboardNavigation) {
18770 dir = e.keyCode == 38 ? -1 : 1;
18772 newDate = this.moveYear(this.date, dir);
18773 newViewDate = this.moveYear(this.viewDate, dir);
18774 } else if (e.shiftKey){
18775 newDate = this.moveMonth(this.date, dir);
18776 newViewDate = this.moveMonth(this.viewDate, dir);
18778 newDate = new Date(this.date);
18779 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18780 newViewDate = new Date(this.viewDate);
18781 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18783 if (this.dateWithinRange(newDate)){
18784 this.date = newDate;
18785 this.viewDate = newViewDate;
18786 this.setValue(this.formatDate(this.date));
18788 e.preventDefault();
18789 dateChanged = true;
18793 this.setValue(this.formatDate(this.date));
18795 e.preventDefault();
18798 this.setValue(this.formatDate(this.date));
18812 onClick: function(e)
18814 e.stopPropagation();
18815 e.preventDefault();
18817 var target = e.getTarget();
18819 if(target.nodeName.toLowerCase() === 'i'){
18820 target = Roo.get(target).dom.parentNode;
18823 var nodeName = target.nodeName;
18824 var className = target.className;
18825 var html = target.innerHTML;
18826 //Roo.log(nodeName);
18828 switch(nodeName.toLowerCase()) {
18830 switch(className) {
18836 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18837 switch(this.viewMode){
18839 this.viewDate = this.moveMonth(this.viewDate, dir);
18843 this.viewDate = this.moveYear(this.viewDate, dir);
18849 var date = new Date();
18850 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18852 this.setValue(this.formatDate(this.date));
18859 if (className.indexOf('disabled') < 0) {
18860 this.viewDate.setUTCDate(1);
18861 if (className.indexOf('month') > -1) {
18862 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18864 var year = parseInt(html, 10) || 0;
18865 this.viewDate.setUTCFullYear(year);
18869 if(this.singleMode){
18870 this.setValue(this.formatDate(this.viewDate));
18881 //Roo.log(className);
18882 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18883 var day = parseInt(html, 10) || 1;
18884 var year = this.viewDate.getUTCFullYear(),
18885 month = this.viewDate.getUTCMonth();
18887 if (className.indexOf('old') > -1) {
18894 } else if (className.indexOf('new') > -1) {
18902 //Roo.log([year,month,day]);
18903 this.date = this.UTCDate(year, month, day,0,0,0,0);
18904 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18906 //Roo.log(this.formatDate(this.date));
18907 this.setValue(this.formatDate(this.date));
18914 setStartDate: function(startDate)
18916 this.startDate = startDate || -Infinity;
18917 if (this.startDate !== -Infinity) {
18918 this.startDate = this.parseDate(this.startDate);
18921 this.updateNavArrows();
18924 setEndDate: function(endDate)
18926 this.endDate = endDate || Infinity;
18927 if (this.endDate !== Infinity) {
18928 this.endDate = this.parseDate(this.endDate);
18931 this.updateNavArrows();
18934 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18936 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18937 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18938 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18940 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18941 return parseInt(d, 10);
18944 this.updateNavArrows();
18947 updateNavArrows: function()
18949 if(this.singleMode){
18953 var d = new Date(this.viewDate),
18954 year = d.getUTCFullYear(),
18955 month = d.getUTCMonth();
18957 Roo.each(this.picker().select('.prev', true).elements, function(v){
18959 switch (this.viewMode) {
18962 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18968 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18975 Roo.each(this.picker().select('.next', true).elements, function(v){
18977 switch (this.viewMode) {
18980 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18986 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18994 moveMonth: function(date, dir)
18999 var new_date = new Date(date.valueOf()),
19000 day = new_date.getUTCDate(),
19001 month = new_date.getUTCMonth(),
19002 mag = Math.abs(dir),
19004 dir = dir > 0 ? 1 : -1;
19007 // If going back one month, make sure month is not current month
19008 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19010 return new_date.getUTCMonth() == month;
19012 // If going forward one month, make sure month is as expected
19013 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19015 return new_date.getUTCMonth() != new_month;
19017 new_month = month + dir;
19018 new_date.setUTCMonth(new_month);
19019 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19020 if (new_month < 0 || new_month > 11) {
19021 new_month = (new_month + 12) % 12;
19024 // For magnitudes >1, move one month at a time...
19025 for (var i=0; i<mag; i++) {
19026 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19027 new_date = this.moveMonth(new_date, dir);
19029 // ...then reset the day, keeping it in the new month
19030 new_month = new_date.getUTCMonth();
19031 new_date.setUTCDate(day);
19033 return new_month != new_date.getUTCMonth();
19036 // Common date-resetting loop -- if date is beyond end of month, make it
19039 new_date.setUTCDate(--day);
19040 new_date.setUTCMonth(new_month);
19045 moveYear: function(date, dir)
19047 return this.moveMonth(date, dir*12);
19050 dateWithinRange: function(date)
19052 return date >= this.startDate && date <= this.endDate;
19058 this.picker().remove();
19061 validateValue : function(value)
19063 if(value.length < 1) {
19064 if(this.allowBlank){
19070 if(value.length < this.minLength){
19073 if(value.length > this.maxLength){
19077 var vt = Roo.form.VTypes;
19078 if(!vt[this.vtype](value, this)){
19082 if(typeof this.validator == "function"){
19083 var msg = this.validator(value);
19089 if(this.regex && !this.regex.test(value)){
19093 if(typeof(this.parseDate(value)) == 'undefined'){
19097 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19101 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19111 Roo.apply(Roo.bootstrap.DateField, {
19122 html: '<i class="fa fa-arrow-left"/>'
19132 html: '<i class="fa fa-arrow-right"/>'
19174 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19175 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19176 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19177 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19178 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19191 navFnc: 'FullYear',
19196 navFnc: 'FullYear',
19201 Roo.apply(Roo.bootstrap.DateField, {
19205 cls: 'datepicker dropdown-menu roo-dynamic',
19209 cls: 'datepicker-days',
19213 cls: 'table-condensed',
19215 Roo.bootstrap.DateField.head,
19219 Roo.bootstrap.DateField.footer
19226 cls: 'datepicker-months',
19230 cls: 'table-condensed',
19232 Roo.bootstrap.DateField.head,
19233 Roo.bootstrap.DateField.content,
19234 Roo.bootstrap.DateField.footer
19241 cls: 'datepicker-years',
19245 cls: 'table-condensed',
19247 Roo.bootstrap.DateField.head,
19248 Roo.bootstrap.DateField.content,
19249 Roo.bootstrap.DateField.footer
19268 * @class Roo.bootstrap.TimeField
19269 * @extends Roo.bootstrap.Input
19270 * Bootstrap DateField class
19274 * Create a new TimeField
19275 * @param {Object} config The config object
19278 Roo.bootstrap.TimeField = function(config){
19279 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19283 * Fires when this field show.
19284 * @param {Roo.bootstrap.DateField} thisthis
19285 * @param {Mixed} date The date value
19290 * Fires when this field hide.
19291 * @param {Roo.bootstrap.DateField} this
19292 * @param {Mixed} date The date value
19297 * Fires when select a date.
19298 * @param {Roo.bootstrap.DateField} this
19299 * @param {Mixed} date The date value
19305 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19308 * @cfg {String} format
19309 * The default time format string which can be overriden for localization support. The format must be
19310 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19314 onRender: function(ct, position)
19317 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19319 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19321 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19323 this.pop = this.picker().select('>.datepicker-time',true).first();
19324 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19326 this.picker().on('mousedown', this.onMousedown, this);
19327 this.picker().on('click', this.onClick, this);
19329 this.picker().addClass('datepicker-dropdown');
19334 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19335 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19336 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19337 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19338 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19339 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19343 fireKey: function(e){
19344 if (!this.picker().isVisible()){
19345 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19351 e.preventDefault();
19359 this.onTogglePeriod();
19362 this.onIncrementMinutes();
19365 this.onDecrementMinutes();
19374 onClick: function(e) {
19375 e.stopPropagation();
19376 e.preventDefault();
19379 picker : function()
19381 return this.el.select('.datepicker', true).first();
19384 fillTime: function()
19386 var time = this.pop.select('tbody', true).first();
19388 time.dom.innerHTML = '';
19403 cls: 'hours-up glyphicon glyphicon-chevron-up'
19423 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19444 cls: 'timepicker-hour',
19459 cls: 'timepicker-minute',
19474 cls: 'btn btn-primary period',
19496 cls: 'hours-down glyphicon glyphicon-chevron-down'
19516 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19534 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19541 var hours = this.time.getHours();
19542 var minutes = this.time.getMinutes();
19555 hours = hours - 12;
19559 hours = '0' + hours;
19563 minutes = '0' + minutes;
19566 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19567 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19568 this.pop.select('button', true).first().dom.innerHTML = period;
19574 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19576 var cls = ['bottom'];
19578 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19585 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19590 this.picker().addClass(cls.join('-'));
19594 Roo.each(cls, function(c){
19596 _this.picker().setTop(_this.inputEl().getHeight());
19600 _this.picker().setTop(0 - _this.picker().getHeight());
19605 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19609 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19616 onFocus : function()
19618 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19622 onBlur : function()
19624 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19630 this.picker().show();
19635 this.fireEvent('show', this, this.date);
19640 this.picker().hide();
19643 this.fireEvent('hide', this, this.date);
19646 setTime : function()
19649 this.setValue(this.time.format(this.format));
19651 this.fireEvent('select', this, this.date);
19656 onMousedown: function(e){
19657 e.stopPropagation();
19658 e.preventDefault();
19661 onIncrementHours: function()
19663 Roo.log('onIncrementHours');
19664 this.time = this.time.add(Date.HOUR, 1);
19669 onDecrementHours: function()
19671 Roo.log('onDecrementHours');
19672 this.time = this.time.add(Date.HOUR, -1);
19676 onIncrementMinutes: function()
19678 Roo.log('onIncrementMinutes');
19679 this.time = this.time.add(Date.MINUTE, 1);
19683 onDecrementMinutes: function()
19685 Roo.log('onDecrementMinutes');
19686 this.time = this.time.add(Date.MINUTE, -1);
19690 onTogglePeriod: function()
19692 Roo.log('onTogglePeriod');
19693 this.time = this.time.add(Date.HOUR, 12);
19700 Roo.apply(Roo.bootstrap.TimeField, {
19730 cls: 'btn btn-info ok',
19742 Roo.apply(Roo.bootstrap.TimeField, {
19746 cls: 'datepicker dropdown-menu',
19750 cls: 'datepicker-time',
19754 cls: 'table-condensed',
19756 Roo.bootstrap.TimeField.content,
19757 Roo.bootstrap.TimeField.footer
19776 * @class Roo.bootstrap.MonthField
19777 * @extends Roo.bootstrap.Input
19778 * Bootstrap MonthField class
19780 * @cfg {String} language default en
19783 * Create a new MonthField
19784 * @param {Object} config The config object
19787 Roo.bootstrap.MonthField = function(config){
19788 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19793 * Fires when this field show.
19794 * @param {Roo.bootstrap.MonthField} this
19795 * @param {Mixed} date The date value
19800 * Fires when this field hide.
19801 * @param {Roo.bootstrap.MonthField} this
19802 * @param {Mixed} date The date value
19807 * Fires when select a date.
19808 * @param {Roo.bootstrap.MonthField} this
19809 * @param {String} oldvalue The old value
19810 * @param {String} newvalue The new value
19816 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
19818 onRender: function(ct, position)
19821 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19823 this.language = this.language || 'en';
19824 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19825 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19827 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19828 this.isInline = false;
19829 this.isInput = true;
19830 this.component = this.el.select('.add-on', true).first() || false;
19831 this.component = (this.component && this.component.length === 0) ? false : this.component;
19832 this.hasInput = this.component && this.inputEL().length;
19834 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19836 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19838 this.picker().on('mousedown', this.onMousedown, this);
19839 this.picker().on('click', this.onClick, this);
19841 this.picker().addClass('datepicker-dropdown');
19843 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19844 v.setStyle('width', '189px');
19851 if(this.isInline) {
19857 setValue: function(v, suppressEvent)
19859 var o = this.getValue();
19861 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19865 if(suppressEvent !== true){
19866 this.fireEvent('select', this, o, v);
19871 getValue: function()
19876 onClick: function(e)
19878 e.stopPropagation();
19879 e.preventDefault();
19881 var target = e.getTarget();
19883 if(target.nodeName.toLowerCase() === 'i'){
19884 target = Roo.get(target).dom.parentNode;
19887 var nodeName = target.nodeName;
19888 var className = target.className;
19889 var html = target.innerHTML;
19891 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19895 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19897 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19903 picker : function()
19905 return this.pickerEl;
19908 fillMonths: function()
19911 var months = this.picker().select('>.datepicker-months td', true).first();
19913 months.dom.innerHTML = '';
19919 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19922 months.createChild(month);
19931 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19932 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19935 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19936 e.removeClass('active');
19938 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19939 e.addClass('active');
19946 if(this.isInline) {
19950 this.picker().removeClass(['bottom', 'top']);
19952 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19954 * place to the top of element!
19958 this.picker().addClass('top');
19959 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19964 this.picker().addClass('bottom');
19966 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19969 onFocus : function()
19971 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19975 onBlur : function()
19977 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19979 var d = this.inputEl().getValue();
19988 this.picker().show();
19989 this.picker().select('>.datepicker-months', true).first().show();
19993 this.fireEvent('show', this, this.date);
19998 if(this.isInline) {
20001 this.picker().hide();
20002 this.fireEvent('hide', this, this.date);
20006 onMousedown: function(e)
20008 e.stopPropagation();
20009 e.preventDefault();
20014 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20018 fireKey: function(e)
20020 if (!this.picker().isVisible()){
20021 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20032 e.preventDefault();
20036 dir = e.keyCode == 37 ? -1 : 1;
20038 this.vIndex = this.vIndex + dir;
20040 if(this.vIndex < 0){
20044 if(this.vIndex > 11){
20048 if(isNaN(this.vIndex)){
20052 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20058 dir = e.keyCode == 38 ? -1 : 1;
20060 this.vIndex = this.vIndex + dir * 4;
20062 if(this.vIndex < 0){
20066 if(this.vIndex > 11){
20070 if(isNaN(this.vIndex)){
20074 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20079 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20080 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20084 e.preventDefault();
20087 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20088 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20104 this.picker().remove();
20109 Roo.apply(Roo.bootstrap.MonthField, {
20128 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20129 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20134 Roo.apply(Roo.bootstrap.MonthField, {
20138 cls: 'datepicker dropdown-menu roo-dynamic',
20142 cls: 'datepicker-months',
20146 cls: 'table-condensed',
20148 Roo.bootstrap.DateField.content
20168 * @class Roo.bootstrap.CheckBox
20169 * @extends Roo.bootstrap.Input
20170 * Bootstrap CheckBox class
20172 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20173 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20174 * @cfg {String} boxLabel The text that appears beside the checkbox
20175 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20176 * @cfg {Boolean} checked initnal the element
20177 * @cfg {Boolean} inline inline the element (default false)
20178 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20179 * @cfg {String} tooltip label tooltip
20182 * Create a new CheckBox
20183 * @param {Object} config The config object
20186 Roo.bootstrap.CheckBox = function(config){
20187 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20192 * Fires when the element is checked or unchecked.
20193 * @param {Roo.bootstrap.CheckBox} this This input
20194 * @param {Boolean} checked The new checked value
20199 * Fires when the element is click.
20200 * @param {Roo.bootstrap.CheckBox} this This input
20207 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20209 inputType: 'checkbox',
20218 getAutoCreate : function()
20220 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20226 cfg.cls = 'form-group ' + this.inputType; //input-group
20229 cfg.cls += ' ' + this.inputType + '-inline';
20235 type : this.inputType,
20236 value : this.inputValue,
20237 cls : 'roo-' + this.inputType, //'form-box',
20238 placeholder : this.placeholder || ''
20242 if(this.inputType != 'radio'){
20246 cls : 'roo-hidden-value',
20247 value : this.checked ? this.inputValue : this.valueOff
20252 if (this.weight) { // Validity check?
20253 cfg.cls += " " + this.inputType + "-" + this.weight;
20256 if (this.disabled) {
20257 input.disabled=true;
20261 input.checked = this.checked;
20266 input.name = this.name;
20268 if(this.inputType != 'radio'){
20269 hidden.name = this.name;
20270 input.name = '_hidden_' + this.name;
20275 input.cls += ' input-' + this.size;
20280 ['xs','sm','md','lg'].map(function(size){
20281 if (settings[size]) {
20282 cfg.cls += ' col-' + size + '-' + settings[size];
20286 var inputblock = input;
20288 if (this.before || this.after) {
20291 cls : 'input-group',
20296 inputblock.cn.push({
20298 cls : 'input-group-addon',
20303 inputblock.cn.push(input);
20305 if(this.inputType != 'radio'){
20306 inputblock.cn.push(hidden);
20310 inputblock.cn.push({
20312 cls : 'input-group-addon',
20319 if (align ==='left' && this.fieldLabel.length) {
20320 // Roo.log("left and has label");
20325 cls : 'control-label',
20326 html : this.fieldLabel
20336 if(this.labelWidth > 12){
20337 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20340 if(this.labelWidth < 13 && this.labelmd == 0){
20341 this.labelmd = this.labelWidth;
20344 if(this.labellg > 0){
20345 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20346 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20349 if(this.labelmd > 0){
20350 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20351 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20354 if(this.labelsm > 0){
20355 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20356 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20359 if(this.labelxs > 0){
20360 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20361 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20364 } else if ( this.fieldLabel.length) {
20365 // Roo.log(" label");
20369 tag: this.boxLabel ? 'span' : 'label',
20371 cls: 'control-label box-input-label',
20372 //cls : 'input-group-addon',
20373 html : this.fieldLabel
20382 // Roo.log(" no label && no align");
20383 cfg.cn = [ inputblock ] ;
20389 var boxLabelCfg = {
20391 //'for': id, // box label is handled by onclick - so no for...
20393 html: this.boxLabel
20397 boxLabelCfg.tooltip = this.tooltip;
20400 cfg.cn.push(boxLabelCfg);
20403 if(this.inputType != 'radio'){
20404 cfg.cn.push(hidden);
20412 * return the real input element.
20414 inputEl: function ()
20416 return this.el.select('input.roo-' + this.inputType,true).first();
20418 hiddenEl: function ()
20420 return this.el.select('input.roo-hidden-value',true).first();
20423 labelEl: function()
20425 return this.el.select('label.control-label',true).first();
20427 /* depricated... */
20431 return this.labelEl();
20434 boxLabelEl: function()
20436 return this.el.select('label.box-label',true).first();
20439 initEvents : function()
20441 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20443 this.inputEl().on('click', this.onClick, this);
20445 if (this.boxLabel) {
20446 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20449 this.startValue = this.getValue();
20452 Roo.bootstrap.CheckBox.register(this);
20456 onClick : function(e)
20458 if(this.fireEvent('click', this, e) !== false){
20459 this.setChecked(!this.checked);
20464 setChecked : function(state,suppressEvent)
20466 this.startValue = this.getValue();
20468 if(this.inputType == 'radio'){
20470 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20471 e.dom.checked = false;
20474 this.inputEl().dom.checked = true;
20476 this.inputEl().dom.value = this.inputValue;
20478 if(suppressEvent !== true){
20479 this.fireEvent('check', this, true);
20487 this.checked = state;
20489 this.inputEl().dom.checked = state;
20492 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20494 if(suppressEvent !== true){
20495 this.fireEvent('check', this, state);
20501 getValue : function()
20503 if(this.inputType == 'radio'){
20504 return this.getGroupValue();
20507 return this.hiddenEl().dom.value;
20511 getGroupValue : function()
20513 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20517 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20520 setValue : function(v,suppressEvent)
20522 if(this.inputType == 'radio'){
20523 this.setGroupValue(v, suppressEvent);
20527 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20532 setGroupValue : function(v, suppressEvent)
20534 this.startValue = this.getValue();
20536 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20537 e.dom.checked = false;
20539 if(e.dom.value == v){
20540 e.dom.checked = true;
20544 if(suppressEvent !== true){
20545 this.fireEvent('check', this, true);
20553 validate : function()
20557 (this.inputType == 'radio' && this.validateRadio()) ||
20558 (this.inputType == 'checkbox' && this.validateCheckbox())
20564 this.markInvalid();
20568 validateRadio : function()
20570 if(this.allowBlank){
20576 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20577 if(!e.dom.checked){
20589 validateCheckbox : function()
20592 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20593 //return (this.getValue() == this.inputValue) ? true : false;
20596 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20604 for(var i in group){
20605 if(group[i].el.isVisible(true)){
20613 for(var i in group){
20618 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20625 * Mark this field as valid
20627 markValid : function()
20631 this.fireEvent('valid', this);
20633 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20636 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20643 if(this.inputType == 'radio'){
20644 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20645 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20646 e.findParent('.form-group', false, true).addClass(_this.validClass);
20653 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20654 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20658 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20664 for(var i in group){
20665 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20666 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20671 * Mark this field as invalid
20672 * @param {String} msg The validation message
20674 markInvalid : function(msg)
20676 if(this.allowBlank){
20682 this.fireEvent('invalid', this, msg);
20684 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20687 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20691 label.markInvalid();
20694 if(this.inputType == 'radio'){
20695 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20696 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20697 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20704 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20705 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20709 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20715 for(var i in group){
20716 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20717 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20722 clearInvalid : function()
20724 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20726 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20728 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20730 if (label && label.iconEl) {
20731 label.iconEl.removeClass(label.validClass);
20732 label.iconEl.removeClass(label.invalidClass);
20736 disable : function()
20738 if(this.inputType != 'radio'){
20739 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20746 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20747 _this.getActionEl().addClass(this.disabledClass);
20748 e.dom.disabled = true;
20752 this.disabled = true;
20753 this.fireEvent("disable", this);
20757 enable : function()
20759 if(this.inputType != 'radio'){
20760 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20767 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20768 _this.getActionEl().removeClass(this.disabledClass);
20769 e.dom.disabled = false;
20773 this.disabled = false;
20774 this.fireEvent("enable", this);
20778 setBoxLabel : function(v)
20783 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20789 Roo.apply(Roo.bootstrap.CheckBox, {
20794 * register a CheckBox Group
20795 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20797 register : function(checkbox)
20799 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20800 this.groups[checkbox.groupId] = {};
20803 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20807 this.groups[checkbox.groupId][checkbox.name] = checkbox;
20811 * fetch a CheckBox Group based on the group ID
20812 * @param {string} the group ID
20813 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20815 get: function(groupId) {
20816 if (typeof(this.groups[groupId]) == 'undefined') {
20820 return this.groups[groupId] ;
20833 * @class Roo.bootstrap.Radio
20834 * @extends Roo.bootstrap.Component
20835 * Bootstrap Radio class
20836 * @cfg {String} boxLabel - the label associated
20837 * @cfg {String} value - the value of radio
20840 * Create a new Radio
20841 * @param {Object} config The config object
20843 Roo.bootstrap.Radio = function(config){
20844 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20848 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20854 getAutoCreate : function()
20858 cls : 'form-group radio',
20863 html : this.boxLabel
20871 initEvents : function()
20873 this.parent().register(this);
20875 this.el.on('click', this.onClick, this);
20879 onClick : function()
20881 this.setChecked(true);
20884 setChecked : function(state, suppressEvent)
20886 this.parent().setValue(this.value, suppressEvent);
20890 setBoxLabel : function(v)
20895 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20910 * @class Roo.bootstrap.SecurePass
20911 * @extends Roo.bootstrap.Input
20912 * Bootstrap SecurePass class
20916 * Create a new SecurePass
20917 * @param {Object} config The config object
20920 Roo.bootstrap.SecurePass = function (config) {
20921 // these go here, so the translation tool can replace them..
20923 PwdEmpty: "Please type a password, and then retype it to confirm.",
20924 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20925 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20926 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20927 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20928 FNInPwd: "Your password can't contain your first name. Please type a different password.",
20929 LNInPwd: "Your password can't contain your last name. Please type a different password.",
20930 TooWeak: "Your password is Too Weak."
20932 this.meterLabel = "Password strength:";
20933 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20934 this.meterClass = [
20935 "roo-password-meter-tooweak",
20936 "roo-password-meter-weak",
20937 "roo-password-meter-medium",
20938 "roo-password-meter-strong",
20939 "roo-password-meter-grey"
20944 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20947 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20949 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20951 * PwdEmpty: "Please type a password, and then retype it to confirm.",
20952 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20953 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20954 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20955 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20956 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
20957 * LNInPwd: "Your password can't contain your last name. Please type a different password."
20967 * @cfg {String/Object} Label for the strength meter (defaults to
20968 * 'Password strength:')
20973 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20974 * ['Weak', 'Medium', 'Strong'])
20977 pwdStrengths: false,
20990 initEvents: function ()
20992 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20994 if (this.el.is('input[type=password]') && Roo.isSafari) {
20995 this.el.on('keydown', this.SafariOnKeyDown, this);
20998 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21001 onRender: function (ct, position)
21003 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21004 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21005 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21007 this.trigger.createChild({
21012 cls: 'roo-password-meter-grey col-xs-12',
21015 //width: this.meterWidth + 'px'
21019 cls: 'roo-password-meter-text'
21025 if (this.hideTrigger) {
21026 this.trigger.setDisplayed(false);
21028 this.setSize(this.width || '', this.height || '');
21031 onDestroy: function ()
21033 if (this.trigger) {
21034 this.trigger.removeAllListeners();
21035 this.trigger.remove();
21038 this.wrap.remove();
21040 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21043 checkStrength: function ()
21045 var pwd = this.inputEl().getValue();
21046 if (pwd == this._lastPwd) {
21051 if (this.ClientSideStrongPassword(pwd)) {
21053 } else if (this.ClientSideMediumPassword(pwd)) {
21055 } else if (this.ClientSideWeakPassword(pwd)) {
21061 Roo.log('strength1: ' + strength);
21063 //var pm = this.trigger.child('div/div/div').dom;
21064 var pm = this.trigger.child('div/div');
21065 pm.removeClass(this.meterClass);
21066 pm.addClass(this.meterClass[strength]);
21069 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21071 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21073 this._lastPwd = pwd;
21077 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21079 this._lastPwd = '';
21081 var pm = this.trigger.child('div/div');
21082 pm.removeClass(this.meterClass);
21083 pm.addClass('roo-password-meter-grey');
21086 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21089 this.inputEl().dom.type='password';
21092 validateValue: function (value)
21095 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21098 if (value.length == 0) {
21099 if (this.allowBlank) {
21100 this.clearInvalid();
21104 this.markInvalid(this.errors.PwdEmpty);
21105 this.errorMsg = this.errors.PwdEmpty;
21113 if ('[\x21-\x7e]*'.match(value)) {
21114 this.markInvalid(this.errors.PwdBadChar);
21115 this.errorMsg = this.errors.PwdBadChar;
21118 if (value.length < 6) {
21119 this.markInvalid(this.errors.PwdShort);
21120 this.errorMsg = this.errors.PwdShort;
21123 if (value.length > 16) {
21124 this.markInvalid(this.errors.PwdLong);
21125 this.errorMsg = this.errors.PwdLong;
21129 if (this.ClientSideStrongPassword(value)) {
21131 } else if (this.ClientSideMediumPassword(value)) {
21133 } else if (this.ClientSideWeakPassword(value)) {
21140 if (strength < 2) {
21141 //this.markInvalid(this.errors.TooWeak);
21142 this.errorMsg = this.errors.TooWeak;
21147 console.log('strength2: ' + strength);
21149 //var pm = this.trigger.child('div/div/div').dom;
21151 var pm = this.trigger.child('div/div');
21152 pm.removeClass(this.meterClass);
21153 pm.addClass(this.meterClass[strength]);
21155 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21157 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21159 this.errorMsg = '';
21163 CharacterSetChecks: function (type)
21166 this.fResult = false;
21169 isctype: function (character, type)
21172 case this.kCapitalLetter:
21173 if (character >= 'A' && character <= 'Z') {
21178 case this.kSmallLetter:
21179 if (character >= 'a' && character <= 'z') {
21185 if (character >= '0' && character <= '9') {
21190 case this.kPunctuation:
21191 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21202 IsLongEnough: function (pwd, size)
21204 return !(pwd == null || isNaN(size) || pwd.length < size);
21207 SpansEnoughCharacterSets: function (word, nb)
21209 if (!this.IsLongEnough(word, nb))
21214 var characterSetChecks = new Array(
21215 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21216 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21219 for (var index = 0; index < word.length; ++index) {
21220 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21221 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21222 characterSetChecks[nCharSet].fResult = true;
21229 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21230 if (characterSetChecks[nCharSet].fResult) {
21235 if (nCharSets < nb) {
21241 ClientSideStrongPassword: function (pwd)
21243 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21246 ClientSideMediumPassword: function (pwd)
21248 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21251 ClientSideWeakPassword: function (pwd)
21253 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21256 })//<script type="text/javascript">
21259 * Based Ext JS Library 1.1.1
21260 * Copyright(c) 2006-2007, Ext JS, LLC.
21266 * @class Roo.HtmlEditorCore
21267 * @extends Roo.Component
21268 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21270 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21273 Roo.HtmlEditorCore = function(config){
21276 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21281 * @event initialize
21282 * Fires when the editor is fully initialized (including the iframe)
21283 * @param {Roo.HtmlEditorCore} this
21288 * Fires when the editor is first receives the focus. Any insertion must wait
21289 * until after this event.
21290 * @param {Roo.HtmlEditorCore} this
21294 * @event beforesync
21295 * Fires before the textarea is updated with content from the editor iframe. Return false
21296 * to cancel the sync.
21297 * @param {Roo.HtmlEditorCore} this
21298 * @param {String} html
21302 * @event beforepush
21303 * Fires before the iframe editor is updated with content from the textarea. Return false
21304 * to cancel the push.
21305 * @param {Roo.HtmlEditorCore} this
21306 * @param {String} html
21311 * Fires when the textarea is updated with content from the editor iframe.
21312 * @param {Roo.HtmlEditorCore} this
21313 * @param {String} html
21318 * Fires when the iframe editor is updated with content from the textarea.
21319 * @param {Roo.HtmlEditorCore} this
21320 * @param {String} html
21325 * @event editorevent
21326 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21327 * @param {Roo.HtmlEditorCore} this
21333 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21335 // defaults : white / black...
21336 this.applyBlacklists();
21343 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21347 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21353 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21358 * @cfg {Number} height (in pixels)
21362 * @cfg {Number} width (in pixels)
21367 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21370 stylesheets: false,
21375 // private properties
21376 validationEvent : false,
21378 initialized : false,
21380 sourceEditMode : false,
21381 onFocus : Roo.emptyFn,
21383 hideMode:'offsets',
21387 // blacklist + whitelisted elements..
21394 * Protected method that will not generally be called directly. It
21395 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21396 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21398 getDocMarkup : function(){
21402 // inherit styels from page...??
21403 if (this.stylesheets === false) {
21405 Roo.get(document.head).select('style').each(function(node) {
21406 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21409 Roo.get(document.head).select('link').each(function(node) {
21410 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21413 } else if (!this.stylesheets.length) {
21415 st = '<style type="text/css">' +
21416 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21419 st = '<style type="text/css">' +
21424 st += '<style type="text/css">' +
21425 'IMG { cursor: pointer } ' +
21428 var cls = 'roo-htmleditor-body';
21430 if(this.bodyCls.length){
21431 cls += ' ' + this.bodyCls;
21434 return '<html><head>' + st +
21435 //<style type="text/css">' +
21436 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21438 ' </head><body class="' + cls + '"></body></html>';
21442 onRender : function(ct, position)
21445 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21446 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21449 this.el.dom.style.border = '0 none';
21450 this.el.dom.setAttribute('tabIndex', -1);
21451 this.el.addClass('x-hidden hide');
21455 if(Roo.isIE){ // fix IE 1px bogus margin
21456 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21460 this.frameId = Roo.id();
21464 var iframe = this.owner.wrap.createChild({
21466 cls: 'form-control', // bootstrap..
21468 name: this.frameId,
21469 frameBorder : 'no',
21470 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21475 this.iframe = iframe.dom;
21477 this.assignDocWin();
21479 this.doc.designMode = 'on';
21482 this.doc.write(this.getDocMarkup());
21486 var task = { // must defer to wait for browser to be ready
21488 //console.log("run task?" + this.doc.readyState);
21489 this.assignDocWin();
21490 if(this.doc.body || this.doc.readyState == 'complete'){
21492 this.doc.designMode="on";
21496 Roo.TaskMgr.stop(task);
21497 this.initEditor.defer(10, this);
21504 Roo.TaskMgr.start(task);
21509 onResize : function(w, h)
21511 Roo.log('resize: ' +w + ',' + h );
21512 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21516 if(typeof w == 'number'){
21518 this.iframe.style.width = w + 'px';
21520 if(typeof h == 'number'){
21522 this.iframe.style.height = h + 'px';
21524 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21531 * Toggles the editor between standard and source edit mode.
21532 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21534 toggleSourceEdit : function(sourceEditMode){
21536 this.sourceEditMode = sourceEditMode === true;
21538 if(this.sourceEditMode){
21540 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21543 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21544 //this.iframe.className = '';
21547 //this.setSize(this.owner.wrap.getSize());
21548 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21555 * Protected method that will not generally be called directly. If you need/want
21556 * custom HTML cleanup, this is the method you should override.
21557 * @param {String} html The HTML to be cleaned
21558 * return {String} The cleaned HTML
21560 cleanHtml : function(html){
21561 html = String(html);
21562 if(html.length > 5){
21563 if(Roo.isSafari){ // strip safari nonsense
21564 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21567 if(html == ' '){
21574 * HTML Editor -> Textarea
21575 * Protected method that will not generally be called directly. Syncs the contents
21576 * of the editor iframe with the textarea.
21578 syncValue : function(){
21579 if(this.initialized){
21580 var bd = (this.doc.body || this.doc.documentElement);
21581 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21582 var html = bd.innerHTML;
21584 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21585 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21587 html = '<div style="'+m[0]+'">' + html + '</div>';
21590 html = this.cleanHtml(html);
21591 // fix up the special chars.. normaly like back quotes in word...
21592 // however we do not want to do this with chinese..
21593 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21594 var cc = b.charCodeAt();
21596 (cc >= 0x4E00 && cc < 0xA000 ) ||
21597 (cc >= 0x3400 && cc < 0x4E00 ) ||
21598 (cc >= 0xf900 && cc < 0xfb00 )
21604 if(this.owner.fireEvent('beforesync', this, html) !== false){
21605 this.el.dom.value = html;
21606 this.owner.fireEvent('sync', this, html);
21612 * Protected method that will not generally be called directly. Pushes the value of the textarea
21613 * into the iframe editor.
21615 pushValue : function(){
21616 if(this.initialized){
21617 var v = this.el.dom.value.trim();
21619 // if(v.length < 1){
21623 if(this.owner.fireEvent('beforepush', this, v) !== false){
21624 var d = (this.doc.body || this.doc.documentElement);
21626 this.cleanUpPaste();
21627 this.el.dom.value = d.innerHTML;
21628 this.owner.fireEvent('push', this, v);
21634 deferFocus : function(){
21635 this.focus.defer(10, this);
21639 focus : function(){
21640 if(this.win && !this.sourceEditMode){
21647 assignDocWin: function()
21649 var iframe = this.iframe;
21652 this.doc = iframe.contentWindow.document;
21653 this.win = iframe.contentWindow;
21655 // if (!Roo.get(this.frameId)) {
21658 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21659 // this.win = Roo.get(this.frameId).dom.contentWindow;
21661 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21665 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21666 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21671 initEditor : function(){
21672 //console.log("INIT EDITOR");
21673 this.assignDocWin();
21677 this.doc.designMode="on";
21679 this.doc.write(this.getDocMarkup());
21682 var dbody = (this.doc.body || this.doc.documentElement);
21683 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21684 // this copies styles from the containing element into thsi one..
21685 // not sure why we need all of this..
21686 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21688 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21689 //ss['background-attachment'] = 'fixed'; // w3c
21690 dbody.bgProperties = 'fixed'; // ie
21691 //Roo.DomHelper.applyStyles(dbody, ss);
21692 Roo.EventManager.on(this.doc, {
21693 //'mousedown': this.onEditorEvent,
21694 'mouseup': this.onEditorEvent,
21695 'dblclick': this.onEditorEvent,
21696 'click': this.onEditorEvent,
21697 'keyup': this.onEditorEvent,
21702 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21704 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21705 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21707 this.initialized = true;
21709 this.owner.fireEvent('initialize', this);
21714 onDestroy : function(){
21720 //for (var i =0; i < this.toolbars.length;i++) {
21721 // // fixme - ask toolbars for heights?
21722 // this.toolbars[i].onDestroy();
21725 //this.wrap.dom.innerHTML = '';
21726 //this.wrap.remove();
21731 onFirstFocus : function(){
21733 this.assignDocWin();
21736 this.activated = true;
21739 if(Roo.isGecko){ // prevent silly gecko errors
21741 var s = this.win.getSelection();
21742 if(!s.focusNode || s.focusNode.nodeType != 3){
21743 var r = s.getRangeAt(0);
21744 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21749 this.execCmd('useCSS', true);
21750 this.execCmd('styleWithCSS', false);
21753 this.owner.fireEvent('activate', this);
21757 adjustFont: function(btn){
21758 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21759 //if(Roo.isSafari){ // safari
21762 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21763 if(Roo.isSafari){ // safari
21764 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21765 v = (v < 10) ? 10 : v;
21766 v = (v > 48) ? 48 : v;
21767 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21772 v = Math.max(1, v+adjust);
21774 this.execCmd('FontSize', v );
21777 onEditorEvent : function(e)
21779 this.owner.fireEvent('editorevent', this, e);
21780 // this.updateToolbar();
21781 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21784 insertTag : function(tg)
21786 // could be a bit smarter... -> wrap the current selected tRoo..
21787 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21789 range = this.createRange(this.getSelection());
21790 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21791 wrappingNode.appendChild(range.extractContents());
21792 range.insertNode(wrappingNode);
21799 this.execCmd("formatblock", tg);
21803 insertText : function(txt)
21807 var range = this.createRange();
21808 range.deleteContents();
21809 //alert(Sender.getAttribute('label'));
21811 range.insertNode(this.doc.createTextNode(txt));
21817 * Executes a Midas editor command on the editor document and performs necessary focus and
21818 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21819 * @param {String} cmd The Midas command
21820 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21822 relayCmd : function(cmd, value){
21824 this.execCmd(cmd, value);
21825 this.owner.fireEvent('editorevent', this);
21826 //this.updateToolbar();
21827 this.owner.deferFocus();
21831 * Executes a Midas editor command directly on the editor document.
21832 * For visual commands, you should use {@link #relayCmd} instead.
21833 * <b>This should only be called after the editor is initialized.</b>
21834 * @param {String} cmd The Midas command
21835 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21837 execCmd : function(cmd, value){
21838 this.doc.execCommand(cmd, false, value === undefined ? null : value);
21845 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21847 * @param {String} text | dom node..
21849 insertAtCursor : function(text)
21852 if(!this.activated){
21858 var r = this.doc.selection.createRange();
21869 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21873 // from jquery ui (MIT licenced)
21875 var win = this.win;
21877 if (win.getSelection && win.getSelection().getRangeAt) {
21878 range = win.getSelection().getRangeAt(0);
21879 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21880 range.insertNode(node);
21881 } else if (win.document.selection && win.document.selection.createRange) {
21882 // no firefox support
21883 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21884 win.document.selection.createRange().pasteHTML(txt);
21886 // no firefox support
21887 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21888 this.execCmd('InsertHTML', txt);
21897 mozKeyPress : function(e){
21899 var c = e.getCharCode(), cmd;
21902 c = String.fromCharCode(c).toLowerCase();
21916 this.cleanUpPaste.defer(100, this);
21924 e.preventDefault();
21932 fixKeys : function(){ // load time branching for fastest keydown performance
21934 return function(e){
21935 var k = e.getKey(), r;
21938 r = this.doc.selection.createRange();
21941 r.pasteHTML('    ');
21948 r = this.doc.selection.createRange();
21950 var target = r.parentElement();
21951 if(!target || target.tagName.toLowerCase() != 'li'){
21953 r.pasteHTML('<br />');
21959 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21960 this.cleanUpPaste.defer(100, this);
21966 }else if(Roo.isOpera){
21967 return function(e){
21968 var k = e.getKey();
21972 this.execCmd('InsertHTML','    ');
21975 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21976 this.cleanUpPaste.defer(100, this);
21981 }else if(Roo.isSafari){
21982 return function(e){
21983 var k = e.getKey();
21987 this.execCmd('InsertText','\t');
21991 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21992 this.cleanUpPaste.defer(100, this);
22000 getAllAncestors: function()
22002 var p = this.getSelectedNode();
22005 a.push(p); // push blank onto stack..
22006 p = this.getParentElement();
22010 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22014 a.push(this.doc.body);
22018 lastSelNode : false,
22021 getSelection : function()
22023 this.assignDocWin();
22024 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22027 getSelectedNode: function()
22029 // this may only work on Gecko!!!
22031 // should we cache this!!!!
22036 var range = this.createRange(this.getSelection()).cloneRange();
22039 var parent = range.parentElement();
22041 var testRange = range.duplicate();
22042 testRange.moveToElementText(parent);
22043 if (testRange.inRange(range)) {
22046 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22049 parent = parent.parentElement;
22054 // is ancestor a text element.
22055 var ac = range.commonAncestorContainer;
22056 if (ac.nodeType == 3) {
22057 ac = ac.parentNode;
22060 var ar = ac.childNodes;
22063 var other_nodes = [];
22064 var has_other_nodes = false;
22065 for (var i=0;i<ar.length;i++) {
22066 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22069 // fullly contained node.
22071 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22076 // probably selected..
22077 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22078 other_nodes.push(ar[i]);
22082 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22087 has_other_nodes = true;
22089 if (!nodes.length && other_nodes.length) {
22090 nodes= other_nodes;
22092 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22098 createRange: function(sel)
22100 // this has strange effects when using with
22101 // top toolbar - not sure if it's a great idea.
22102 //this.editor.contentWindow.focus();
22103 if (typeof sel != "undefined") {
22105 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22107 return this.doc.createRange();
22110 return this.doc.createRange();
22113 getParentElement: function()
22116 this.assignDocWin();
22117 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22119 var range = this.createRange(sel);
22122 var p = range.commonAncestorContainer;
22123 while (p.nodeType == 3) { // text node
22134 * Range intersection.. the hard stuff...
22138 * [ -- selected range --- ]
22142 * if end is before start or hits it. fail.
22143 * if start is after end or hits it fail.
22145 * if either hits (but other is outside. - then it's not
22151 // @see http://www.thismuchiknow.co.uk/?p=64.
22152 rangeIntersectsNode : function(range, node)
22154 var nodeRange = node.ownerDocument.createRange();
22156 nodeRange.selectNode(node);
22158 nodeRange.selectNodeContents(node);
22161 var rangeStartRange = range.cloneRange();
22162 rangeStartRange.collapse(true);
22164 var rangeEndRange = range.cloneRange();
22165 rangeEndRange.collapse(false);
22167 var nodeStartRange = nodeRange.cloneRange();
22168 nodeStartRange.collapse(true);
22170 var nodeEndRange = nodeRange.cloneRange();
22171 nodeEndRange.collapse(false);
22173 return rangeStartRange.compareBoundaryPoints(
22174 Range.START_TO_START, nodeEndRange) == -1 &&
22175 rangeEndRange.compareBoundaryPoints(
22176 Range.START_TO_START, nodeStartRange) == 1;
22180 rangeCompareNode : function(range, node)
22182 var nodeRange = node.ownerDocument.createRange();
22184 nodeRange.selectNode(node);
22186 nodeRange.selectNodeContents(node);
22190 range.collapse(true);
22192 nodeRange.collapse(true);
22194 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22195 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22197 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22199 var nodeIsBefore = ss == 1;
22200 var nodeIsAfter = ee == -1;
22202 if (nodeIsBefore && nodeIsAfter) {
22205 if (!nodeIsBefore && nodeIsAfter) {
22206 return 1; //right trailed.
22209 if (nodeIsBefore && !nodeIsAfter) {
22210 return 2; // left trailed.
22216 // private? - in a new class?
22217 cleanUpPaste : function()
22219 // cleans up the whole document..
22220 Roo.log('cleanuppaste');
22222 this.cleanUpChildren(this.doc.body);
22223 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22224 if (clean != this.doc.body.innerHTML) {
22225 this.doc.body.innerHTML = clean;
22230 cleanWordChars : function(input) {// change the chars to hex code
22231 var he = Roo.HtmlEditorCore;
22233 var output = input;
22234 Roo.each(he.swapCodes, function(sw) {
22235 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22237 output = output.replace(swapper, sw[1]);
22244 cleanUpChildren : function (n)
22246 if (!n.childNodes.length) {
22249 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22250 this.cleanUpChild(n.childNodes[i]);
22257 cleanUpChild : function (node)
22260 //console.log(node);
22261 if (node.nodeName == "#text") {
22262 // clean up silly Windows -- stuff?
22265 if (node.nodeName == "#comment") {
22266 node.parentNode.removeChild(node);
22267 // clean up silly Windows -- stuff?
22270 var lcname = node.tagName.toLowerCase();
22271 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22272 // whitelist of tags..
22274 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22276 node.parentNode.removeChild(node);
22281 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22283 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22284 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22286 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22287 // remove_keep_children = true;
22290 if (remove_keep_children) {
22291 this.cleanUpChildren(node);
22292 // inserts everything just before this node...
22293 while (node.childNodes.length) {
22294 var cn = node.childNodes[0];
22295 node.removeChild(cn);
22296 node.parentNode.insertBefore(cn, node);
22298 node.parentNode.removeChild(node);
22302 if (!node.attributes || !node.attributes.length) {
22303 this.cleanUpChildren(node);
22307 function cleanAttr(n,v)
22310 if (v.match(/^\./) || v.match(/^\//)) {
22313 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22316 if (v.match(/^#/)) {
22319 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22320 node.removeAttribute(n);
22324 var cwhite = this.cwhite;
22325 var cblack = this.cblack;
22327 function cleanStyle(n,v)
22329 if (v.match(/expression/)) { //XSS?? should we even bother..
22330 node.removeAttribute(n);
22334 var parts = v.split(/;/);
22337 Roo.each(parts, function(p) {
22338 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22342 var l = p.split(':').shift().replace(/\s+/g,'');
22343 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22345 if ( cwhite.length && cblack.indexOf(l) > -1) {
22346 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22347 //node.removeAttribute(n);
22351 // only allow 'c whitelisted system attributes'
22352 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22353 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22354 //node.removeAttribute(n);
22364 if (clean.length) {
22365 node.setAttribute(n, clean.join(';'));
22367 node.removeAttribute(n);
22373 for (var i = node.attributes.length-1; i > -1 ; i--) {
22374 var a = node.attributes[i];
22377 if (a.name.toLowerCase().substr(0,2)=='on') {
22378 node.removeAttribute(a.name);
22381 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22382 node.removeAttribute(a.name);
22385 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22386 cleanAttr(a.name,a.value); // fixme..
22389 if (a.name == 'style') {
22390 cleanStyle(a.name,a.value);
22393 /// clean up MS crap..
22394 // tecnically this should be a list of valid class'es..
22397 if (a.name == 'class') {
22398 if (a.value.match(/^Mso/)) {
22399 node.className = '';
22402 if (a.value.match(/^body$/)) {
22403 node.className = '';
22414 this.cleanUpChildren(node);
22420 * Clean up MS wordisms...
22422 cleanWord : function(node)
22427 this.cleanWord(this.doc.body);
22430 if (node.nodeName == "#text") {
22431 // clean up silly Windows -- stuff?
22434 if (node.nodeName == "#comment") {
22435 node.parentNode.removeChild(node);
22436 // clean up silly Windows -- stuff?
22440 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22441 node.parentNode.removeChild(node);
22445 // remove - but keep children..
22446 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22447 while (node.childNodes.length) {
22448 var cn = node.childNodes[0];
22449 node.removeChild(cn);
22450 node.parentNode.insertBefore(cn, node);
22452 node.parentNode.removeChild(node);
22453 this.iterateChildren(node, this.cleanWord);
22457 if (node.className.length) {
22459 var cn = node.className.split(/\W+/);
22461 Roo.each(cn, function(cls) {
22462 if (cls.match(/Mso[a-zA-Z]+/)) {
22467 node.className = cna.length ? cna.join(' ') : '';
22469 node.removeAttribute("class");
22473 if (node.hasAttribute("lang")) {
22474 node.removeAttribute("lang");
22477 if (node.hasAttribute("style")) {
22479 var styles = node.getAttribute("style").split(";");
22481 Roo.each(styles, function(s) {
22482 if (!s.match(/:/)) {
22485 var kv = s.split(":");
22486 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22489 // what ever is left... we allow.
22492 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22493 if (!nstyle.length) {
22494 node.removeAttribute('style');
22497 this.iterateChildren(node, this.cleanWord);
22503 * iterateChildren of a Node, calling fn each time, using this as the scole..
22504 * @param {DomNode} node node to iterate children of.
22505 * @param {Function} fn method of this class to call on each item.
22507 iterateChildren : function(node, fn)
22509 if (!node.childNodes.length) {
22512 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22513 fn.call(this, node.childNodes[i])
22519 * cleanTableWidths.
22521 * Quite often pasting from word etc.. results in tables with column and widths.
22522 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22525 cleanTableWidths : function(node)
22530 this.cleanTableWidths(this.doc.body);
22535 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22538 Roo.log(node.tagName);
22539 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22540 this.iterateChildren(node, this.cleanTableWidths);
22543 if (node.hasAttribute('width')) {
22544 node.removeAttribute('width');
22548 if (node.hasAttribute("style")) {
22551 var styles = node.getAttribute("style").split(";");
22553 Roo.each(styles, function(s) {
22554 if (!s.match(/:/)) {
22557 var kv = s.split(":");
22558 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22561 // what ever is left... we allow.
22564 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22565 if (!nstyle.length) {
22566 node.removeAttribute('style');
22570 this.iterateChildren(node, this.cleanTableWidths);
22578 domToHTML : function(currentElement, depth, nopadtext) {
22580 depth = depth || 0;
22581 nopadtext = nopadtext || false;
22583 if (!currentElement) {
22584 return this.domToHTML(this.doc.body);
22587 //Roo.log(currentElement);
22589 var allText = false;
22590 var nodeName = currentElement.nodeName;
22591 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22593 if (nodeName == '#text') {
22595 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22600 if (nodeName != 'BODY') {
22603 // Prints the node tagName, such as <A>, <IMG>, etc
22606 for(i = 0; i < currentElement.attributes.length;i++) {
22608 var aname = currentElement.attributes.item(i).name;
22609 if (!currentElement.attributes.item(i).value.length) {
22612 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22615 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22624 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22627 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22632 // Traverse the tree
22634 var currentElementChild = currentElement.childNodes.item(i);
22635 var allText = true;
22636 var innerHTML = '';
22638 while (currentElementChild) {
22639 // Formatting code (indent the tree so it looks nice on the screen)
22640 var nopad = nopadtext;
22641 if (lastnode == 'SPAN') {
22645 if (currentElementChild.nodeName == '#text') {
22646 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22647 toadd = nopadtext ? toadd : toadd.trim();
22648 if (!nopad && toadd.length > 80) {
22649 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22651 innerHTML += toadd;
22654 currentElementChild = currentElement.childNodes.item(i);
22660 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22662 // Recursively traverse the tree structure of the child node
22663 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22664 lastnode = currentElementChild.nodeName;
22666 currentElementChild=currentElement.childNodes.item(i);
22672 // The remaining code is mostly for formatting the tree
22673 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22678 ret+= "</"+tagName+">";
22684 applyBlacklists : function()
22686 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22687 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22691 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22692 if (b.indexOf(tag) > -1) {
22695 this.white.push(tag);
22699 Roo.each(w, function(tag) {
22700 if (b.indexOf(tag) > -1) {
22703 if (this.white.indexOf(tag) > -1) {
22706 this.white.push(tag);
22711 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22712 if (w.indexOf(tag) > -1) {
22715 this.black.push(tag);
22719 Roo.each(b, function(tag) {
22720 if (w.indexOf(tag) > -1) {
22723 if (this.black.indexOf(tag) > -1) {
22726 this.black.push(tag);
22731 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22732 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22736 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22737 if (b.indexOf(tag) > -1) {
22740 this.cwhite.push(tag);
22744 Roo.each(w, function(tag) {
22745 if (b.indexOf(tag) > -1) {
22748 if (this.cwhite.indexOf(tag) > -1) {
22751 this.cwhite.push(tag);
22756 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22757 if (w.indexOf(tag) > -1) {
22760 this.cblack.push(tag);
22764 Roo.each(b, function(tag) {
22765 if (w.indexOf(tag) > -1) {
22768 if (this.cblack.indexOf(tag) > -1) {
22771 this.cblack.push(tag);
22776 setStylesheets : function(stylesheets)
22778 if(typeof(stylesheets) == 'string'){
22779 Roo.get(this.iframe.contentDocument.head).createChild({
22781 rel : 'stylesheet',
22790 Roo.each(stylesheets, function(s) {
22795 Roo.get(_this.iframe.contentDocument.head).createChild({
22797 rel : 'stylesheet',
22806 removeStylesheets : function()
22810 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22815 setStyle : function(style)
22817 Roo.get(this.iframe.contentDocument.head).createChild({
22826 // hide stuff that is not compatible
22840 * @event specialkey
22844 * @cfg {String} fieldClass @hide
22847 * @cfg {String} focusClass @hide
22850 * @cfg {String} autoCreate @hide
22853 * @cfg {String} inputType @hide
22856 * @cfg {String} invalidClass @hide
22859 * @cfg {String} invalidText @hide
22862 * @cfg {String} msgFx @hide
22865 * @cfg {String} validateOnBlur @hide
22869 Roo.HtmlEditorCore.white = [
22870 'area', 'br', 'img', 'input', 'hr', 'wbr',
22872 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
22873 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
22874 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
22875 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
22876 'table', 'ul', 'xmp',
22878 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
22881 'dir', 'menu', 'ol', 'ul', 'dl',
22887 Roo.HtmlEditorCore.black = [
22888 // 'embed', 'object', // enable - backend responsiblity to clean thiese
22890 'base', 'basefont', 'bgsound', 'blink', 'body',
22891 'frame', 'frameset', 'head', 'html', 'ilayer',
22892 'iframe', 'layer', 'link', 'meta', 'object',
22893 'script', 'style' ,'title', 'xml' // clean later..
22895 Roo.HtmlEditorCore.clean = [
22896 'script', 'style', 'title', 'xml'
22898 Roo.HtmlEditorCore.remove = [
22903 Roo.HtmlEditorCore.ablack = [
22907 Roo.HtmlEditorCore.aclean = [
22908 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
22912 Roo.HtmlEditorCore.pwhite= [
22913 'http', 'https', 'mailto'
22916 // white listed style attributes.
22917 Roo.HtmlEditorCore.cwhite= [
22918 // 'text-align', /// default is to allow most things..
22924 // black listed style attributes.
22925 Roo.HtmlEditorCore.cblack= [
22926 // 'font-size' -- this can be set by the project
22930 Roo.HtmlEditorCore.swapCodes =[
22949 * @class Roo.bootstrap.HtmlEditor
22950 * @extends Roo.bootstrap.TextArea
22951 * Bootstrap HtmlEditor class
22954 * Create a new HtmlEditor
22955 * @param {Object} config The config object
22958 Roo.bootstrap.HtmlEditor = function(config){
22959 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22960 if (!this.toolbars) {
22961 this.toolbars = [];
22964 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22967 * @event initialize
22968 * Fires when the editor is fully initialized (including the iframe)
22969 * @param {HtmlEditor} this
22974 * Fires when the editor is first receives the focus. Any insertion must wait
22975 * until after this event.
22976 * @param {HtmlEditor} this
22980 * @event beforesync
22981 * Fires before the textarea is updated with content from the editor iframe. Return false
22982 * to cancel the sync.
22983 * @param {HtmlEditor} this
22984 * @param {String} html
22988 * @event beforepush
22989 * Fires before the iframe editor is updated with content from the textarea. Return false
22990 * to cancel the push.
22991 * @param {HtmlEditor} this
22992 * @param {String} html
22997 * Fires when the textarea is updated with content from the editor iframe.
22998 * @param {HtmlEditor} this
22999 * @param {String} html
23004 * Fires when the iframe editor is updated with content from the textarea.
23005 * @param {HtmlEditor} this
23006 * @param {String} html
23010 * @event editmodechange
23011 * Fires when the editor switches edit modes
23012 * @param {HtmlEditor} this
23013 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23015 editmodechange: true,
23017 * @event editorevent
23018 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23019 * @param {HtmlEditor} this
23023 * @event firstfocus
23024 * Fires when on first focus - needed by toolbars..
23025 * @param {HtmlEditor} this
23030 * Auto save the htmlEditor value as a file into Events
23031 * @param {HtmlEditor} this
23035 * @event savedpreview
23036 * preview the saved version of htmlEditor
23037 * @param {HtmlEditor} this
23044 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23048 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23053 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23058 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23063 * @cfg {Number} height (in pixels)
23067 * @cfg {Number} width (in pixels)
23072 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23075 stylesheets: false,
23080 // private properties
23081 validationEvent : false,
23083 initialized : false,
23086 onFocus : Roo.emptyFn,
23088 hideMode:'offsets',
23090 tbContainer : false,
23094 toolbarContainer :function() {
23095 return this.wrap.select('.x-html-editor-tb',true).first();
23099 * Protected method that will not generally be called directly. It
23100 * is called when the editor creates its toolbar. Override this method if you need to
23101 * add custom toolbar buttons.
23102 * @param {HtmlEditor} editor
23104 createToolbar : function(){
23105 Roo.log('renewing');
23106 Roo.log("create toolbars");
23108 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23109 this.toolbars[0].render(this.toolbarContainer());
23113 // if (!editor.toolbars || !editor.toolbars.length) {
23114 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23117 // for (var i =0 ; i < editor.toolbars.length;i++) {
23118 // editor.toolbars[i] = Roo.factory(
23119 // typeof(editor.toolbars[i]) == 'string' ?
23120 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23121 // Roo.bootstrap.HtmlEditor);
23122 // editor.toolbars[i].init(editor);
23128 onRender : function(ct, position)
23130 // Roo.log("Call onRender: " + this.xtype);
23132 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23134 this.wrap = this.inputEl().wrap({
23135 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23138 this.editorcore.onRender(ct, position);
23140 if (this.resizable) {
23141 this.resizeEl = new Roo.Resizable(this.wrap, {
23145 minHeight : this.height,
23146 height: this.height,
23147 handles : this.resizable,
23150 resize : function(r, w, h) {
23151 _t.onResize(w,h); // -something
23157 this.createToolbar(this);
23160 if(!this.width && this.resizable){
23161 this.setSize(this.wrap.getSize());
23163 if (this.resizeEl) {
23164 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23165 // should trigger onReize..
23171 onResize : function(w, h)
23173 Roo.log('resize: ' +w + ',' + h );
23174 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23178 if(this.inputEl() ){
23179 if(typeof w == 'number'){
23180 var aw = w - this.wrap.getFrameWidth('lr');
23181 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23184 if(typeof h == 'number'){
23185 var tbh = -11; // fixme it needs to tool bar size!
23186 for (var i =0; i < this.toolbars.length;i++) {
23187 // fixme - ask toolbars for heights?
23188 tbh += this.toolbars[i].el.getHeight();
23189 //if (this.toolbars[i].footer) {
23190 // tbh += this.toolbars[i].footer.el.getHeight();
23198 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23199 ah -= 5; // knock a few pixes off for look..
23200 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23204 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23205 this.editorcore.onResize(ew,eh);
23210 * Toggles the editor between standard and source edit mode.
23211 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23213 toggleSourceEdit : function(sourceEditMode)
23215 this.editorcore.toggleSourceEdit(sourceEditMode);
23217 if(this.editorcore.sourceEditMode){
23218 Roo.log('editor - showing textarea');
23221 // Roo.log(this.syncValue());
23223 this.inputEl().removeClass(['hide', 'x-hidden']);
23224 this.inputEl().dom.removeAttribute('tabIndex');
23225 this.inputEl().focus();
23227 Roo.log('editor - hiding textarea');
23229 // Roo.log(this.pushValue());
23232 this.inputEl().addClass(['hide', 'x-hidden']);
23233 this.inputEl().dom.setAttribute('tabIndex', -1);
23234 //this.deferFocus();
23237 if(this.resizable){
23238 this.setSize(this.wrap.getSize());
23241 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23244 // private (for BoxComponent)
23245 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23247 // private (for BoxComponent)
23248 getResizeEl : function(){
23252 // private (for BoxComponent)
23253 getPositionEl : function(){
23258 initEvents : function(){
23259 this.originalValue = this.getValue();
23263 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23266 // markInvalid : Roo.emptyFn,
23268 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23271 // clearInvalid : Roo.emptyFn,
23273 setValue : function(v){
23274 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23275 this.editorcore.pushValue();
23280 deferFocus : function(){
23281 this.focus.defer(10, this);
23285 focus : function(){
23286 this.editorcore.focus();
23292 onDestroy : function(){
23298 for (var i =0; i < this.toolbars.length;i++) {
23299 // fixme - ask toolbars for heights?
23300 this.toolbars[i].onDestroy();
23303 this.wrap.dom.innerHTML = '';
23304 this.wrap.remove();
23309 onFirstFocus : function(){
23310 //Roo.log("onFirstFocus");
23311 this.editorcore.onFirstFocus();
23312 for (var i =0; i < this.toolbars.length;i++) {
23313 this.toolbars[i].onFirstFocus();
23319 syncValue : function()
23321 this.editorcore.syncValue();
23324 pushValue : function()
23326 this.editorcore.pushValue();
23330 // hide stuff that is not compatible
23344 * @event specialkey
23348 * @cfg {String} fieldClass @hide
23351 * @cfg {String} focusClass @hide
23354 * @cfg {String} autoCreate @hide
23357 * @cfg {String} inputType @hide
23360 * @cfg {String} invalidClass @hide
23363 * @cfg {String} invalidText @hide
23366 * @cfg {String} msgFx @hide
23369 * @cfg {String} validateOnBlur @hide
23378 Roo.namespace('Roo.bootstrap.htmleditor');
23380 * @class Roo.bootstrap.HtmlEditorToolbar1
23385 new Roo.bootstrap.HtmlEditor({
23388 new Roo.bootstrap.HtmlEditorToolbar1({
23389 disable : { fonts: 1 , format: 1, ..., ... , ...],
23395 * @cfg {Object} disable List of elements to disable..
23396 * @cfg {Array} btns List of additional buttons.
23400 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23403 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23406 Roo.apply(this, config);
23408 // default disabled, based on 'good practice'..
23409 this.disable = this.disable || {};
23410 Roo.applyIf(this.disable, {
23413 specialElements : true
23415 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23417 this.editor = config.editor;
23418 this.editorcore = config.editor.editorcore;
23420 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23422 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23423 // dont call parent... till later.
23425 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23430 editorcore : false,
23435 "h1","h2","h3","h4","h5","h6",
23437 "abbr", "acronym", "address", "cite", "samp", "var",
23441 onRender : function(ct, position)
23443 // Roo.log("Call onRender: " + this.xtype);
23445 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23447 this.el.dom.style.marginBottom = '0';
23449 var editorcore = this.editorcore;
23450 var editor= this.editor;
23453 var btn = function(id,cmd , toggle, handler, html){
23455 var event = toggle ? 'toggle' : 'click';
23460 xns: Roo.bootstrap,
23463 enableToggle:toggle !== false,
23465 pressed : toggle ? false : null,
23468 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23469 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23475 // var cb_box = function...
23480 xns: Roo.bootstrap,
23481 glyphicon : 'font',
23485 xns: Roo.bootstrap,
23489 Roo.each(this.formats, function(f) {
23490 style.menu.items.push({
23492 xns: Roo.bootstrap,
23493 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23498 editorcore.insertTag(this.tagname);
23505 children.push(style);
23507 btn('bold',false,true);
23508 btn('italic',false,true);
23509 btn('align-left', 'justifyleft',true);
23510 btn('align-center', 'justifycenter',true);
23511 btn('align-right' , 'justifyright',true);
23512 btn('link', false, false, function(btn) {
23513 //Roo.log("create link?");
23514 var url = prompt(this.createLinkText, this.defaultLinkValue);
23515 if(url && url != 'http:/'+'/'){
23516 this.editorcore.relayCmd('createlink', url);
23519 btn('list','insertunorderedlist',true);
23520 btn('pencil', false,true, function(btn){
23522 this.toggleSourceEdit(btn.pressed);
23525 if (this.editor.btns.length > 0) {
23526 for (var i = 0; i<this.editor.btns.length; i++) {
23527 children.push(this.editor.btns[i]);
23535 xns: Roo.bootstrap,
23540 xns: Roo.bootstrap,
23545 cog.menu.items.push({
23547 xns: Roo.bootstrap,
23548 html : Clean styles,
23553 editorcore.insertTag(this.tagname);
23562 this.xtype = 'NavSimplebar';
23564 for(var i=0;i< children.length;i++) {
23566 this.buttons.add(this.addxtypeChild(children[i]));
23570 editor.on('editorevent', this.updateToolbar, this);
23572 onBtnClick : function(id)
23574 this.editorcore.relayCmd(id);
23575 this.editorcore.focus();
23579 * Protected method that will not generally be called directly. It triggers
23580 * a toolbar update by reading the markup state of the current selection in the editor.
23582 updateToolbar: function(){
23584 if(!this.editorcore.activated){
23585 this.editor.onFirstFocus(); // is this neeed?
23589 var btns = this.buttons;
23590 var doc = this.editorcore.doc;
23591 btns.get('bold').setActive(doc.queryCommandState('bold'));
23592 btns.get('italic').setActive(doc.queryCommandState('italic'));
23593 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23595 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23596 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23597 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23599 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23600 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23603 var ans = this.editorcore.getAllAncestors();
23604 if (this.formatCombo) {
23607 var store = this.formatCombo.store;
23608 this.formatCombo.setValue("");
23609 for (var i =0; i < ans.length;i++) {
23610 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23612 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23620 // hides menus... - so this cant be on a menu...
23621 Roo.bootstrap.MenuMgr.hideAll();
23623 Roo.bootstrap.MenuMgr.hideAll();
23624 //this.editorsyncValue();
23626 onFirstFocus: function() {
23627 this.buttons.each(function(item){
23631 toggleSourceEdit : function(sourceEditMode){
23634 if(sourceEditMode){
23635 Roo.log("disabling buttons");
23636 this.buttons.each( function(item){
23637 if(item.cmd != 'pencil'){
23643 Roo.log("enabling buttons");
23644 if(this.editorcore.initialized){
23645 this.buttons.each( function(item){
23651 Roo.log("calling toggole on editor");
23652 // tell the editor that it's been pressed..
23653 this.editor.toggleSourceEdit(sourceEditMode);
23663 * @class Roo.bootstrap.Table.AbstractSelectionModel
23664 * @extends Roo.util.Observable
23665 * Abstract base class for grid SelectionModels. It provides the interface that should be
23666 * implemented by descendant classes. This class should not be directly instantiated.
23669 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23670 this.locked = false;
23671 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23675 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23676 /** @ignore Called by the grid automatically. Do not call directly. */
23677 init : function(grid){
23683 * Locks the selections.
23686 this.locked = true;
23690 * Unlocks the selections.
23692 unlock : function(){
23693 this.locked = false;
23697 * Returns true if the selections are locked.
23698 * @return {Boolean}
23700 isLocked : function(){
23701 return this.locked;
23705 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23706 * @class Roo.bootstrap.Table.RowSelectionModel
23707 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23708 * It supports multiple selections and keyboard selection/navigation.
23710 * @param {Object} config
23713 Roo.bootstrap.Table.RowSelectionModel = function(config){
23714 Roo.apply(this, config);
23715 this.selections = new Roo.util.MixedCollection(false, function(o){
23720 this.lastActive = false;
23724 * @event selectionchange
23725 * Fires when the selection changes
23726 * @param {SelectionModel} this
23728 "selectionchange" : true,
23730 * @event afterselectionchange
23731 * Fires after the selection changes (eg. by key press or clicking)
23732 * @param {SelectionModel} this
23734 "afterselectionchange" : true,
23736 * @event beforerowselect
23737 * Fires when a row is selected being selected, return false to cancel.
23738 * @param {SelectionModel} this
23739 * @param {Number} rowIndex The selected index
23740 * @param {Boolean} keepExisting False if other selections will be cleared
23742 "beforerowselect" : true,
23745 * Fires when a row is selected.
23746 * @param {SelectionModel} this
23747 * @param {Number} rowIndex The selected index
23748 * @param {Roo.data.Record} r The record
23750 "rowselect" : true,
23752 * @event rowdeselect
23753 * Fires when a row is deselected.
23754 * @param {SelectionModel} this
23755 * @param {Number} rowIndex The selected index
23757 "rowdeselect" : true
23759 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23760 this.locked = false;
23763 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
23765 * @cfg {Boolean} singleSelect
23766 * True to allow selection of only one row at a time (defaults to false)
23768 singleSelect : false,
23771 initEvents : function()
23774 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23775 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
23776 //}else{ // allow click to work like normal
23777 // this.grid.on("rowclick", this.handleDragableRowClick, this);
23779 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23780 this.grid.on("rowclick", this.handleMouseDown, this);
23782 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23783 "up" : function(e){
23785 this.selectPrevious(e.shiftKey);
23786 }else if(this.last !== false && this.lastActive !== false){
23787 var last = this.last;
23788 this.selectRange(this.last, this.lastActive-1);
23789 this.grid.getView().focusRow(this.lastActive);
23790 if(last !== false){
23794 this.selectFirstRow();
23796 this.fireEvent("afterselectionchange", this);
23798 "down" : function(e){
23800 this.selectNext(e.shiftKey);
23801 }else if(this.last !== false && this.lastActive !== false){
23802 var last = this.last;
23803 this.selectRange(this.last, this.lastActive+1);
23804 this.grid.getView().focusRow(this.lastActive);
23805 if(last !== false){
23809 this.selectFirstRow();
23811 this.fireEvent("afterselectionchange", this);
23815 this.grid.store.on('load', function(){
23816 this.selections.clear();
23819 var view = this.grid.view;
23820 view.on("refresh", this.onRefresh, this);
23821 view.on("rowupdated", this.onRowUpdated, this);
23822 view.on("rowremoved", this.onRemove, this);
23827 onRefresh : function()
23829 var ds = this.grid.store, i, v = this.grid.view;
23830 var s = this.selections;
23831 s.each(function(r){
23832 if((i = ds.indexOfId(r.id)) != -1){
23841 onRemove : function(v, index, r){
23842 this.selections.remove(r);
23846 onRowUpdated : function(v, index, r){
23847 if(this.isSelected(r)){
23848 v.onRowSelect(index);
23854 * @param {Array} records The records to select
23855 * @param {Boolean} keepExisting (optional) True to keep existing selections
23857 selectRecords : function(records, keepExisting)
23860 this.clearSelections();
23862 var ds = this.grid.store;
23863 for(var i = 0, len = records.length; i < len; i++){
23864 this.selectRow(ds.indexOf(records[i]), true);
23869 * Gets the number of selected rows.
23872 getCount : function(){
23873 return this.selections.length;
23877 * Selects the first row in the grid.
23879 selectFirstRow : function(){
23884 * Select the last row.
23885 * @param {Boolean} keepExisting (optional) True to keep existing selections
23887 selectLastRow : function(keepExisting){
23888 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23889 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23893 * Selects the row immediately following the last selected row.
23894 * @param {Boolean} keepExisting (optional) True to keep existing selections
23896 selectNext : function(keepExisting)
23898 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23899 this.selectRow(this.last+1, keepExisting);
23900 this.grid.getView().focusRow(this.last);
23905 * Selects the row that precedes the last selected row.
23906 * @param {Boolean} keepExisting (optional) True to keep existing selections
23908 selectPrevious : function(keepExisting){
23910 this.selectRow(this.last-1, keepExisting);
23911 this.grid.getView().focusRow(this.last);
23916 * Returns the selected records
23917 * @return {Array} Array of selected records
23919 getSelections : function(){
23920 return [].concat(this.selections.items);
23924 * Returns the first selected record.
23927 getSelected : function(){
23928 return this.selections.itemAt(0);
23933 * Clears all selections.
23935 clearSelections : function(fast)
23941 var ds = this.grid.store;
23942 var s = this.selections;
23943 s.each(function(r){
23944 this.deselectRow(ds.indexOfId(r.id));
23948 this.selections.clear();
23955 * Selects all rows.
23957 selectAll : function(){
23961 this.selections.clear();
23962 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23963 this.selectRow(i, true);
23968 * Returns True if there is a selection.
23969 * @return {Boolean}
23971 hasSelection : function(){
23972 return this.selections.length > 0;
23976 * Returns True if the specified row is selected.
23977 * @param {Number/Record} record The record or index of the record to check
23978 * @return {Boolean}
23980 isSelected : function(index){
23981 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23982 return (r && this.selections.key(r.id) ? true : false);
23986 * Returns True if the specified record id is selected.
23987 * @param {String} id The id of record to check
23988 * @return {Boolean}
23990 isIdSelected : function(id){
23991 return (this.selections.key(id) ? true : false);
23996 handleMouseDBClick : function(e, t){
24000 handleMouseDown : function(e, t)
24002 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24003 if(this.isLocked() || rowIndex < 0 ){
24006 if(e.shiftKey && this.last !== false){
24007 var last = this.last;
24008 this.selectRange(last, rowIndex, e.ctrlKey);
24009 this.last = last; // reset the last
24013 var isSelected = this.isSelected(rowIndex);
24014 //Roo.log("select row:" + rowIndex);
24016 this.deselectRow(rowIndex);
24018 this.selectRow(rowIndex, true);
24022 if(e.button !== 0 && isSelected){
24023 alert('rowIndex 2: ' + rowIndex);
24024 view.focusRow(rowIndex);
24025 }else if(e.ctrlKey && isSelected){
24026 this.deselectRow(rowIndex);
24027 }else if(!isSelected){
24028 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24029 view.focusRow(rowIndex);
24033 this.fireEvent("afterselectionchange", this);
24036 handleDragableRowClick : function(grid, rowIndex, e)
24038 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24039 this.selectRow(rowIndex, false);
24040 grid.view.focusRow(rowIndex);
24041 this.fireEvent("afterselectionchange", this);
24046 * Selects multiple rows.
24047 * @param {Array} rows Array of the indexes of the row to select
24048 * @param {Boolean} keepExisting (optional) True to keep existing selections
24050 selectRows : function(rows, keepExisting){
24052 this.clearSelections();
24054 for(var i = 0, len = rows.length; i < len; i++){
24055 this.selectRow(rows[i], true);
24060 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24061 * @param {Number} startRow The index of the first row in the range
24062 * @param {Number} endRow The index of the last row in the range
24063 * @param {Boolean} keepExisting (optional) True to retain existing selections
24065 selectRange : function(startRow, endRow, keepExisting){
24070 this.clearSelections();
24072 if(startRow <= endRow){
24073 for(var i = startRow; i <= endRow; i++){
24074 this.selectRow(i, true);
24077 for(var i = startRow; i >= endRow; i--){
24078 this.selectRow(i, true);
24084 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24085 * @param {Number} startRow The index of the first row in the range
24086 * @param {Number} endRow The index of the last row in the range
24088 deselectRange : function(startRow, endRow, preventViewNotify){
24092 for(var i = startRow; i <= endRow; i++){
24093 this.deselectRow(i, preventViewNotify);
24099 * @param {Number} row The index of the row to select
24100 * @param {Boolean} keepExisting (optional) True to keep existing selections
24102 selectRow : function(index, keepExisting, preventViewNotify)
24104 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24107 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24108 if(!keepExisting || this.singleSelect){
24109 this.clearSelections();
24112 var r = this.grid.store.getAt(index);
24113 //console.log('selectRow - record id :' + r.id);
24115 this.selections.add(r);
24116 this.last = this.lastActive = index;
24117 if(!preventViewNotify){
24118 var proxy = new Roo.Element(
24119 this.grid.getRowDom(index)
24121 proxy.addClass('bg-info info');
24123 this.fireEvent("rowselect", this, index, r);
24124 this.fireEvent("selectionchange", this);
24130 * @param {Number} row The index of the row to deselect
24132 deselectRow : function(index, preventViewNotify)
24137 if(this.last == index){
24140 if(this.lastActive == index){
24141 this.lastActive = false;
24144 var r = this.grid.store.getAt(index);
24149 this.selections.remove(r);
24150 //.console.log('deselectRow - record id :' + r.id);
24151 if(!preventViewNotify){
24153 var proxy = new Roo.Element(
24154 this.grid.getRowDom(index)
24156 proxy.removeClass('bg-info info');
24158 this.fireEvent("rowdeselect", this, index);
24159 this.fireEvent("selectionchange", this);
24163 restoreLast : function(){
24165 this.last = this._last;
24170 acceptsNav : function(row, col, cm){
24171 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24175 onEditorKey : function(field, e){
24176 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24181 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24183 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24185 }else if(k == e.ENTER && !e.ctrlKey){
24189 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24191 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24193 }else if(k == e.ESC){
24197 g.startEditing(newCell[0], newCell[1]);
24203 * Ext JS Library 1.1.1
24204 * Copyright(c) 2006-2007, Ext JS, LLC.
24206 * Originally Released Under LGPL - original licence link has changed is not relivant.
24209 * <script type="text/javascript">
24213 * @class Roo.bootstrap.PagingToolbar
24214 * @extends Roo.bootstrap.NavSimplebar
24215 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24217 * Create a new PagingToolbar
24218 * @param {Object} config The config object
24219 * @param {Roo.data.Store} store
24221 Roo.bootstrap.PagingToolbar = function(config)
24223 // old args format still supported... - xtype is prefered..
24224 // created from xtype...
24226 this.ds = config.dataSource;
24228 if (config.store && !this.ds) {
24229 this.store= Roo.factory(config.store, Roo.data);
24230 this.ds = this.store;
24231 this.ds.xmodule = this.xmodule || false;
24234 this.toolbarItems = [];
24235 if (config.items) {
24236 this.toolbarItems = config.items;
24239 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24244 this.bind(this.ds);
24247 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24251 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24253 * @cfg {Roo.data.Store} dataSource
24254 * The underlying data store providing the paged data
24257 * @cfg {String/HTMLElement/Element} container
24258 * container The id or element that will contain the toolbar
24261 * @cfg {Boolean} displayInfo
24262 * True to display the displayMsg (defaults to false)
24265 * @cfg {Number} pageSize
24266 * The number of records to display per page (defaults to 20)
24270 * @cfg {String} displayMsg
24271 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24273 displayMsg : 'Displaying {0} - {1} of {2}',
24275 * @cfg {String} emptyMsg
24276 * The message to display when no records are found (defaults to "No data to display")
24278 emptyMsg : 'No data to display',
24280 * Customizable piece of the default paging text (defaults to "Page")
24283 beforePageText : "Page",
24285 * Customizable piece of the default paging text (defaults to "of %0")
24288 afterPageText : "of {0}",
24290 * Customizable piece of the default paging text (defaults to "First Page")
24293 firstText : "First Page",
24295 * Customizable piece of the default paging text (defaults to "Previous Page")
24298 prevText : "Previous Page",
24300 * Customizable piece of the default paging text (defaults to "Next Page")
24303 nextText : "Next Page",
24305 * Customizable piece of the default paging text (defaults to "Last Page")
24308 lastText : "Last Page",
24310 * Customizable piece of the default paging text (defaults to "Refresh")
24313 refreshText : "Refresh",
24317 onRender : function(ct, position)
24319 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24320 this.navgroup.parentId = this.id;
24321 this.navgroup.onRender(this.el, null);
24322 // add the buttons to the navgroup
24324 if(this.displayInfo){
24325 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24326 this.displayEl = this.el.select('.x-paging-info', true).first();
24327 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24328 // this.displayEl = navel.el.select('span',true).first();
24334 Roo.each(_this.buttons, function(e){ // this might need to use render????
24335 Roo.factory(e).onRender(_this.el, null);
24339 Roo.each(_this.toolbarItems, function(e) {
24340 _this.navgroup.addItem(e);
24344 this.first = this.navgroup.addItem({
24345 tooltip: this.firstText,
24347 icon : 'fa fa-backward',
24349 preventDefault: true,
24350 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24353 this.prev = this.navgroup.addItem({
24354 tooltip: this.prevText,
24356 icon : 'fa fa-step-backward',
24358 preventDefault: true,
24359 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24361 //this.addSeparator();
24364 var field = this.navgroup.addItem( {
24366 cls : 'x-paging-position',
24368 html : this.beforePageText +
24369 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24370 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24373 this.field = field.el.select('input', true).first();
24374 this.field.on("keydown", this.onPagingKeydown, this);
24375 this.field.on("focus", function(){this.dom.select();});
24378 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24379 //this.field.setHeight(18);
24380 //this.addSeparator();
24381 this.next = this.navgroup.addItem({
24382 tooltip: this.nextText,
24384 html : ' <i class="fa fa-step-forward">',
24386 preventDefault: true,
24387 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24389 this.last = this.navgroup.addItem({
24390 tooltip: this.lastText,
24391 icon : 'fa fa-forward',
24394 preventDefault: true,
24395 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24397 //this.addSeparator();
24398 this.loading = this.navgroup.addItem({
24399 tooltip: this.refreshText,
24400 icon: 'fa fa-refresh',
24401 preventDefault: true,
24402 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24408 updateInfo : function(){
24409 if(this.displayEl){
24410 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24411 var msg = count == 0 ?
24415 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24417 this.displayEl.update(msg);
24422 onLoad : function(ds, r, o)
24424 this.cursor = o.params ? o.params.start : 0;
24425 var d = this.getPageData(),
24430 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24431 this.field.dom.value = ap;
24432 this.first.setDisabled(ap == 1);
24433 this.prev.setDisabled(ap == 1);
24434 this.next.setDisabled(ap == ps);
24435 this.last.setDisabled(ap == ps);
24436 this.loading.enable();
24441 getPageData : function(){
24442 var total = this.ds.getTotalCount();
24445 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24446 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24451 onLoadError : function(){
24452 this.loading.enable();
24456 onPagingKeydown : function(e){
24457 var k = e.getKey();
24458 var d = this.getPageData();
24460 var v = this.field.dom.value, pageNum;
24461 if(!v || isNaN(pageNum = parseInt(v, 10))){
24462 this.field.dom.value = d.activePage;
24465 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24466 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24469 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))
24471 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24472 this.field.dom.value = pageNum;
24473 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24476 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24478 var v = this.field.dom.value, pageNum;
24479 var increment = (e.shiftKey) ? 10 : 1;
24480 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24483 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24484 this.field.dom.value = d.activePage;
24487 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24489 this.field.dom.value = parseInt(v, 10) + increment;
24490 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24491 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24498 beforeLoad : function(){
24500 this.loading.disable();
24505 onClick : function(which){
24514 ds.load({params:{start: 0, limit: this.pageSize}});
24517 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24520 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24523 var total = ds.getTotalCount();
24524 var extra = total % this.pageSize;
24525 var lastStart = extra ? (total - extra) : total-this.pageSize;
24526 ds.load({params:{start: lastStart, limit: this.pageSize}});
24529 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24535 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24536 * @param {Roo.data.Store} store The data store to unbind
24538 unbind : function(ds){
24539 ds.un("beforeload", this.beforeLoad, this);
24540 ds.un("load", this.onLoad, this);
24541 ds.un("loadexception", this.onLoadError, this);
24542 ds.un("remove", this.updateInfo, this);
24543 ds.un("add", this.updateInfo, this);
24544 this.ds = undefined;
24548 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24549 * @param {Roo.data.Store} store The data store to bind
24551 bind : function(ds){
24552 ds.on("beforeload", this.beforeLoad, this);
24553 ds.on("load", this.onLoad, this);
24554 ds.on("loadexception", this.onLoadError, this);
24555 ds.on("remove", this.updateInfo, this);
24556 ds.on("add", this.updateInfo, this);
24567 * @class Roo.bootstrap.MessageBar
24568 * @extends Roo.bootstrap.Component
24569 * Bootstrap MessageBar class
24570 * @cfg {String} html contents of the MessageBar
24571 * @cfg {String} weight (info | success | warning | danger) default info
24572 * @cfg {String} beforeClass insert the bar before the given class
24573 * @cfg {Boolean} closable (true | false) default false
24574 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24577 * Create a new Element
24578 * @param {Object} config The config object
24581 Roo.bootstrap.MessageBar = function(config){
24582 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24585 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24591 beforeClass: 'bootstrap-sticky-wrap',
24593 getAutoCreate : function(){
24597 cls: 'alert alert-dismissable alert-' + this.weight,
24602 html: this.html || ''
24608 cfg.cls += ' alert-messages-fixed';
24622 onRender : function(ct, position)
24624 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24627 var cfg = Roo.apply({}, this.getAutoCreate());
24631 cfg.cls += ' ' + this.cls;
24634 cfg.style = this.style;
24636 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24638 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24641 this.el.select('>button.close').on('click', this.hide, this);
24647 if (!this.rendered) {
24653 this.fireEvent('show', this);
24659 if (!this.rendered) {
24665 this.fireEvent('hide', this);
24668 update : function()
24670 // var e = this.el.dom.firstChild;
24672 // if(this.closable){
24673 // e = e.nextSibling;
24676 // e.data = this.html || '';
24678 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24694 * @class Roo.bootstrap.Graph
24695 * @extends Roo.bootstrap.Component
24696 * Bootstrap Graph class
24700 @cfg {String} graphtype bar | vbar | pie
24701 @cfg {number} g_x coodinator | centre x (pie)
24702 @cfg {number} g_y coodinator | centre y (pie)
24703 @cfg {number} g_r radius (pie)
24704 @cfg {number} g_height height of the chart (respected by all elements in the set)
24705 @cfg {number} g_width width of the chart (respected by all elements in the set)
24706 @cfg {Object} title The title of the chart
24709 -opts (object) options for the chart
24711 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24712 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24714 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.
24715 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24717 o stretch (boolean)
24719 -opts (object) options for the pie
24722 o startAngle (number)
24723 o endAngle (number)
24727 * Create a new Input
24728 * @param {Object} config The config object
24731 Roo.bootstrap.Graph = function(config){
24732 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24738 * The img click event for the img.
24739 * @param {Roo.EventObject} e
24745 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24756 //g_colors: this.colors,
24763 getAutoCreate : function(){
24774 onRender : function(ct,position){
24777 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24779 if (typeof(Raphael) == 'undefined') {
24780 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24784 this.raphael = Raphael(this.el.dom);
24786 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24787 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24788 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24789 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24791 r.text(160, 10, "Single Series Chart").attr(txtattr);
24792 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24793 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24794 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24796 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24797 r.barchart(330, 10, 300, 220, data1);
24798 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24799 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24802 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24803 // r.barchart(30, 30, 560, 250, xdata, {
24804 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24805 // axis : "0 0 1 1",
24806 // axisxlabels : xdata
24807 // //yvalues : cols,
24810 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24812 // this.load(null,xdata,{
24813 // axis : "0 0 1 1",
24814 // axisxlabels : xdata
24819 load : function(graphtype,xdata,opts)
24821 this.raphael.clear();
24823 graphtype = this.graphtype;
24828 var r = this.raphael,
24829 fin = function () {
24830 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24832 fout = function () {
24833 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24835 pfin = function() {
24836 this.sector.stop();
24837 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24840 this.label[0].stop();
24841 this.label[0].attr({ r: 7.5 });
24842 this.label[1].attr({ "font-weight": 800 });
24845 pfout = function() {
24846 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24849 this.label[0].animate({ r: 5 }, 500, "bounce");
24850 this.label[1].attr({ "font-weight": 400 });
24856 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24859 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24862 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
24863 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24865 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24872 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24877 setTitle: function(o)
24882 initEvents: function() {
24885 this.el.on('click', this.onClick, this);
24889 onClick : function(e)
24891 Roo.log('img onclick');
24892 this.fireEvent('click', this, e);
24904 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24907 * @class Roo.bootstrap.dash.NumberBox
24908 * @extends Roo.bootstrap.Component
24909 * Bootstrap NumberBox class
24910 * @cfg {String} headline Box headline
24911 * @cfg {String} content Box content
24912 * @cfg {String} icon Box icon
24913 * @cfg {String} footer Footer text
24914 * @cfg {String} fhref Footer href
24917 * Create a new NumberBox
24918 * @param {Object} config The config object
24922 Roo.bootstrap.dash.NumberBox = function(config){
24923 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24927 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
24936 getAutoCreate : function(){
24940 cls : 'small-box ',
24948 cls : 'roo-headline',
24949 html : this.headline
24953 cls : 'roo-content',
24954 html : this.content
24968 cls : 'ion ' + this.icon
24977 cls : 'small-box-footer',
24978 href : this.fhref || '#',
24982 cfg.cn.push(footer);
24989 onRender : function(ct,position){
24990 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24997 setHeadline: function (value)
24999 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25002 setFooter: function (value, href)
25004 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25007 this.el.select('a.small-box-footer',true).first().attr('href', href);
25012 setContent: function (value)
25014 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25017 initEvents: function()
25031 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25034 * @class Roo.bootstrap.dash.TabBox
25035 * @extends Roo.bootstrap.Component
25036 * Bootstrap TabBox class
25037 * @cfg {String} title Title of the TabBox
25038 * @cfg {String} icon Icon of the TabBox
25039 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25040 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25043 * Create a new TabBox
25044 * @param {Object} config The config object
25048 Roo.bootstrap.dash.TabBox = function(config){
25049 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25054 * When a pane is added
25055 * @param {Roo.bootstrap.dash.TabPane} pane
25059 * @event activatepane
25060 * When a pane is activated
25061 * @param {Roo.bootstrap.dash.TabPane} pane
25063 "activatepane" : true
25071 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25076 tabScrollable : false,
25078 getChildContainer : function()
25080 return this.el.select('.tab-content', true).first();
25083 getAutoCreate : function(){
25087 cls: 'pull-left header',
25095 cls: 'fa ' + this.icon
25101 cls: 'nav nav-tabs pull-right',
25107 if(this.tabScrollable){
25114 cls: 'nav nav-tabs pull-right',
25125 cls: 'nav-tabs-custom',
25130 cls: 'tab-content no-padding',
25138 initEvents : function()
25140 //Roo.log('add add pane handler');
25141 this.on('addpane', this.onAddPane, this);
25144 * Updates the box title
25145 * @param {String} html to set the title to.
25147 setTitle : function(value)
25149 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25151 onAddPane : function(pane)
25153 this.panes.push(pane);
25154 //Roo.log('addpane');
25156 // tabs are rendere left to right..
25157 if(!this.showtabs){
25161 var ctr = this.el.select('.nav-tabs', true).first();
25164 var existing = ctr.select('.nav-tab',true);
25165 var qty = existing.getCount();;
25168 var tab = ctr.createChild({
25170 cls : 'nav-tab' + (qty ? '' : ' active'),
25178 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25181 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25183 pane.el.addClass('active');
25188 onTabClick : function(ev,un,ob,pane)
25190 //Roo.log('tab - prev default');
25191 ev.preventDefault();
25194 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25195 pane.tab.addClass('active');
25196 //Roo.log(pane.title);
25197 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25198 // technically we should have a deactivate event.. but maybe add later.
25199 // and it should not de-activate the selected tab...
25200 this.fireEvent('activatepane', pane);
25201 pane.el.addClass('active');
25202 pane.fireEvent('activate');
25207 getActivePane : function()
25210 Roo.each(this.panes, function(p) {
25211 if(p.el.hasClass('active')){
25232 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25234 * @class Roo.bootstrap.TabPane
25235 * @extends Roo.bootstrap.Component
25236 * Bootstrap TabPane class
25237 * @cfg {Boolean} active (false | true) Default false
25238 * @cfg {String} title title of panel
25242 * Create a new TabPane
25243 * @param {Object} config The config object
25246 Roo.bootstrap.dash.TabPane = function(config){
25247 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25253 * When a pane is activated
25254 * @param {Roo.bootstrap.dash.TabPane} pane
25261 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25266 // the tabBox that this is attached to.
25269 getAutoCreate : function()
25277 cfg.cls += ' active';
25282 initEvents : function()
25284 //Roo.log('trigger add pane handler');
25285 this.parent().fireEvent('addpane', this)
25289 * Updates the tab title
25290 * @param {String} html to set the title to.
25292 setTitle: function(str)
25298 this.tab.select('a', true).first().dom.innerHTML = str;
25315 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25318 * @class Roo.bootstrap.menu.Menu
25319 * @extends Roo.bootstrap.Component
25320 * Bootstrap Menu class - container for Menu
25321 * @cfg {String} html Text of the menu
25322 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25323 * @cfg {String} icon Font awesome icon
25324 * @cfg {String} pos Menu align to (top | bottom) default bottom
25328 * Create a new Menu
25329 * @param {Object} config The config object
25333 Roo.bootstrap.menu.Menu = function(config){
25334 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25338 * @event beforeshow
25339 * Fires before this menu is displayed
25340 * @param {Roo.bootstrap.menu.Menu} this
25344 * @event beforehide
25345 * Fires before this menu is hidden
25346 * @param {Roo.bootstrap.menu.Menu} this
25351 * Fires after this menu is displayed
25352 * @param {Roo.bootstrap.menu.Menu} this
25357 * Fires after this menu is hidden
25358 * @param {Roo.bootstrap.menu.Menu} this
25363 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25364 * @param {Roo.bootstrap.menu.Menu} this
25365 * @param {Roo.EventObject} e
25372 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25376 weight : 'default',
25381 getChildContainer : function() {
25382 if(this.isSubMenu){
25386 return this.el.select('ul.dropdown-menu', true).first();
25389 getAutoCreate : function()
25394 cls : 'roo-menu-text',
25402 cls : 'fa ' + this.icon
25413 cls : 'dropdown-button btn btn-' + this.weight,
25418 cls : 'dropdown-toggle btn btn-' + this.weight,
25428 cls : 'dropdown-menu'
25434 if(this.pos == 'top'){
25435 cfg.cls += ' dropup';
25438 if(this.isSubMenu){
25441 cls : 'dropdown-menu'
25448 onRender : function(ct, position)
25450 this.isSubMenu = ct.hasClass('dropdown-submenu');
25452 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25455 initEvents : function()
25457 if(this.isSubMenu){
25461 this.hidden = true;
25463 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25464 this.triggerEl.on('click', this.onTriggerPress, this);
25466 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25467 this.buttonEl.on('click', this.onClick, this);
25473 if(this.isSubMenu){
25477 return this.el.select('ul.dropdown-menu', true).first();
25480 onClick : function(e)
25482 this.fireEvent("click", this, e);
25485 onTriggerPress : function(e)
25487 if (this.isVisible()) {
25494 isVisible : function(){
25495 return !this.hidden;
25500 this.fireEvent("beforeshow", this);
25502 this.hidden = false;
25503 this.el.addClass('open');
25505 Roo.get(document).on("mouseup", this.onMouseUp, this);
25507 this.fireEvent("show", this);
25514 this.fireEvent("beforehide", this);
25516 this.hidden = true;
25517 this.el.removeClass('open');
25519 Roo.get(document).un("mouseup", this.onMouseUp);
25521 this.fireEvent("hide", this);
25524 onMouseUp : function()
25538 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25541 * @class Roo.bootstrap.menu.Item
25542 * @extends Roo.bootstrap.Component
25543 * Bootstrap MenuItem class
25544 * @cfg {Boolean} submenu (true | false) default false
25545 * @cfg {String} html text of the item
25546 * @cfg {String} href the link
25547 * @cfg {Boolean} disable (true | false) default false
25548 * @cfg {Boolean} preventDefault (true | false) default true
25549 * @cfg {String} icon Font awesome icon
25550 * @cfg {String} pos Submenu align to (left | right) default right
25554 * Create a new Item
25555 * @param {Object} config The config object
25559 Roo.bootstrap.menu.Item = function(config){
25560 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25564 * Fires when the mouse is hovering over this menu
25565 * @param {Roo.bootstrap.menu.Item} this
25566 * @param {Roo.EventObject} e
25571 * Fires when the mouse exits this menu
25572 * @param {Roo.bootstrap.menu.Item} this
25573 * @param {Roo.EventObject} e
25579 * The raw click event for the entire grid.
25580 * @param {Roo.EventObject} e
25586 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25591 preventDefault: true,
25596 getAutoCreate : function()
25601 cls : 'roo-menu-item-text',
25609 cls : 'fa ' + this.icon
25618 href : this.href || '#',
25625 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25629 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25631 if(this.pos == 'left'){
25632 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25639 initEvents : function()
25641 this.el.on('mouseover', this.onMouseOver, this);
25642 this.el.on('mouseout', this.onMouseOut, this);
25644 this.el.select('a', true).first().on('click', this.onClick, this);
25648 onClick : function(e)
25650 if(this.preventDefault){
25651 e.preventDefault();
25654 this.fireEvent("click", this, e);
25657 onMouseOver : function(e)
25659 if(this.submenu && this.pos == 'left'){
25660 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25663 this.fireEvent("mouseover", this, e);
25666 onMouseOut : function(e)
25668 this.fireEvent("mouseout", this, e);
25680 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25683 * @class Roo.bootstrap.menu.Separator
25684 * @extends Roo.bootstrap.Component
25685 * Bootstrap Separator class
25688 * Create a new Separator
25689 * @param {Object} config The config object
25693 Roo.bootstrap.menu.Separator = function(config){
25694 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25697 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25699 getAutoCreate : function(){
25720 * @class Roo.bootstrap.Tooltip
25721 * Bootstrap Tooltip class
25722 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25723 * to determine which dom element triggers the tooltip.
25725 * It needs to add support for additional attributes like tooltip-position
25728 * Create a new Toolti
25729 * @param {Object} config The config object
25732 Roo.bootstrap.Tooltip = function(config){
25733 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25735 this.alignment = Roo.bootstrap.Tooltip.alignment;
25737 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25738 this.alignment = config.alignment;
25743 Roo.apply(Roo.bootstrap.Tooltip, {
25745 * @function init initialize tooltip monitoring.
25749 currentTip : false,
25750 currentRegion : false,
25756 Roo.get(document).on('mouseover', this.enter ,this);
25757 Roo.get(document).on('mouseout', this.leave, this);
25760 this.currentTip = new Roo.bootstrap.Tooltip();
25763 enter : function(ev)
25765 var dom = ev.getTarget();
25767 //Roo.log(['enter',dom]);
25768 var el = Roo.fly(dom);
25769 if (this.currentEl) {
25771 //Roo.log(this.currentEl);
25772 //Roo.log(this.currentEl.contains(dom));
25773 if (this.currentEl == el) {
25776 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25782 if (this.currentTip.el) {
25783 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25787 if(!el || el.dom == document){
25793 // you can not look for children, as if el is the body.. then everythign is the child..
25794 if (!el.attr('tooltip')) { //
25795 if (!el.select("[tooltip]").elements.length) {
25798 // is the mouse over this child...?
25799 bindEl = el.select("[tooltip]").first();
25800 var xy = ev.getXY();
25801 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25802 //Roo.log("not in region.");
25805 //Roo.log("child element over..");
25808 this.currentEl = bindEl;
25809 this.currentTip.bind(bindEl);
25810 this.currentRegion = Roo.lib.Region.getRegion(dom);
25811 this.currentTip.enter();
25814 leave : function(ev)
25816 var dom = ev.getTarget();
25817 //Roo.log(['leave',dom]);
25818 if (!this.currentEl) {
25823 if (dom != this.currentEl.dom) {
25826 var xy = ev.getXY();
25827 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
25830 // only activate leave if mouse cursor is outside... bounding box..
25835 if (this.currentTip) {
25836 this.currentTip.leave();
25838 //Roo.log('clear currentEl');
25839 this.currentEl = false;
25844 'left' : ['r-l', [-2,0], 'right'],
25845 'right' : ['l-r', [2,0], 'left'],
25846 'bottom' : ['t-b', [0,2], 'top'],
25847 'top' : [ 'b-t', [0,-2], 'bottom']
25853 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
25858 delay : null, // can be { show : 300 , hide: 500}
25862 hoverState : null, //???
25864 placement : 'bottom',
25868 getAutoCreate : function(){
25875 cls : 'tooltip-arrow'
25878 cls : 'tooltip-inner'
25885 bind : function(el)
25891 enter : function () {
25893 if (this.timeout != null) {
25894 clearTimeout(this.timeout);
25897 this.hoverState = 'in';
25898 //Roo.log("enter - show");
25899 if (!this.delay || !this.delay.show) {
25904 this.timeout = setTimeout(function () {
25905 if (_t.hoverState == 'in') {
25908 }, this.delay.show);
25912 clearTimeout(this.timeout);
25914 this.hoverState = 'out';
25915 if (!this.delay || !this.delay.hide) {
25921 this.timeout = setTimeout(function () {
25922 //Roo.log("leave - timeout");
25924 if (_t.hoverState == 'out') {
25926 Roo.bootstrap.Tooltip.currentEl = false;
25931 show : function (msg)
25934 this.render(document.body);
25937 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25939 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25941 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25943 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25945 var placement = typeof this.placement == 'function' ?
25946 this.placement.call(this, this.el, on_el) :
25949 var autoToken = /\s?auto?\s?/i;
25950 var autoPlace = autoToken.test(placement);
25952 placement = placement.replace(autoToken, '') || 'top';
25956 //this.el.setXY([0,0]);
25958 //this.el.dom.style.display='block';
25960 //this.el.appendTo(on_el);
25962 var p = this.getPosition();
25963 var box = this.el.getBox();
25969 var align = this.alignment[placement];
25971 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25973 if(placement == 'top' || placement == 'bottom'){
25975 placement = 'right';
25978 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25979 placement = 'left';
25982 var scroll = Roo.select('body', true).first().getScroll();
25984 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25990 this.el.alignTo(this.bindEl, align[0],align[1]);
25991 //var arrow = this.el.select('.arrow',true).first();
25992 //arrow.set(align[2],
25994 this.el.addClass(placement);
25996 this.el.addClass('in fade');
25998 this.hoverState = null;
26000 if (this.el.hasClass('fade')) {
26011 //this.el.setXY([0,0]);
26012 this.el.removeClass('in');
26028 * @class Roo.bootstrap.LocationPicker
26029 * @extends Roo.bootstrap.Component
26030 * Bootstrap LocationPicker class
26031 * @cfg {Number} latitude Position when init default 0
26032 * @cfg {Number} longitude Position when init default 0
26033 * @cfg {Number} zoom default 15
26034 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26035 * @cfg {Boolean} mapTypeControl default false
26036 * @cfg {Boolean} disableDoubleClickZoom default false
26037 * @cfg {Boolean} scrollwheel default true
26038 * @cfg {Boolean} streetViewControl default false
26039 * @cfg {Number} radius default 0
26040 * @cfg {String} locationName
26041 * @cfg {Boolean} draggable default true
26042 * @cfg {Boolean} enableAutocomplete default false
26043 * @cfg {Boolean} enableReverseGeocode default true
26044 * @cfg {String} markerTitle
26047 * Create a new LocationPicker
26048 * @param {Object} config The config object
26052 Roo.bootstrap.LocationPicker = function(config){
26054 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26059 * Fires when the picker initialized.
26060 * @param {Roo.bootstrap.LocationPicker} this
26061 * @param {Google Location} location
26065 * @event positionchanged
26066 * Fires when the picker position changed.
26067 * @param {Roo.bootstrap.LocationPicker} this
26068 * @param {Google Location} location
26070 positionchanged : true,
26073 * Fires when the map resize.
26074 * @param {Roo.bootstrap.LocationPicker} this
26079 * Fires when the map show.
26080 * @param {Roo.bootstrap.LocationPicker} this
26085 * Fires when the map hide.
26086 * @param {Roo.bootstrap.LocationPicker} this
26091 * Fires when click the map.
26092 * @param {Roo.bootstrap.LocationPicker} this
26093 * @param {Map event} e
26097 * @event mapRightClick
26098 * Fires when right click the map.
26099 * @param {Roo.bootstrap.LocationPicker} this
26100 * @param {Map event} e
26102 mapRightClick : true,
26104 * @event markerClick
26105 * Fires when click the marker.
26106 * @param {Roo.bootstrap.LocationPicker} this
26107 * @param {Map event} e
26109 markerClick : true,
26111 * @event markerRightClick
26112 * Fires when right click the marker.
26113 * @param {Roo.bootstrap.LocationPicker} this
26114 * @param {Map event} e
26116 markerRightClick : true,
26118 * @event OverlayViewDraw
26119 * Fires when OverlayView Draw
26120 * @param {Roo.bootstrap.LocationPicker} this
26122 OverlayViewDraw : true,
26124 * @event OverlayViewOnAdd
26125 * Fires when OverlayView Draw
26126 * @param {Roo.bootstrap.LocationPicker} this
26128 OverlayViewOnAdd : true,
26130 * @event OverlayViewOnRemove
26131 * Fires when OverlayView Draw
26132 * @param {Roo.bootstrap.LocationPicker} this
26134 OverlayViewOnRemove : true,
26136 * @event OverlayViewShow
26137 * Fires when OverlayView Draw
26138 * @param {Roo.bootstrap.LocationPicker} this
26139 * @param {Pixel} cpx
26141 OverlayViewShow : true,
26143 * @event OverlayViewHide
26144 * Fires when OverlayView Draw
26145 * @param {Roo.bootstrap.LocationPicker} this
26147 OverlayViewHide : true,
26149 * @event loadexception
26150 * Fires when load google lib failed.
26151 * @param {Roo.bootstrap.LocationPicker} this
26153 loadexception : true
26158 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26160 gMapContext: false,
26166 mapTypeControl: false,
26167 disableDoubleClickZoom: false,
26169 streetViewControl: false,
26173 enableAutocomplete: false,
26174 enableReverseGeocode: true,
26177 getAutoCreate: function()
26182 cls: 'roo-location-picker'
26188 initEvents: function(ct, position)
26190 if(!this.el.getWidth() || this.isApplied()){
26194 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26199 initial: function()
26201 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26202 this.fireEvent('loadexception', this);
26206 if(!this.mapTypeId){
26207 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26210 this.gMapContext = this.GMapContext();
26212 this.initOverlayView();
26214 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26218 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26219 _this.setPosition(_this.gMapContext.marker.position);
26222 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26223 _this.fireEvent('mapClick', this, event);
26227 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26228 _this.fireEvent('mapRightClick', this, event);
26232 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26233 _this.fireEvent('markerClick', this, event);
26237 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26238 _this.fireEvent('markerRightClick', this, event);
26242 this.setPosition(this.gMapContext.location);
26244 this.fireEvent('initial', this, this.gMapContext.location);
26247 initOverlayView: function()
26251 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26255 _this.fireEvent('OverlayViewDraw', _this);
26260 _this.fireEvent('OverlayViewOnAdd', _this);
26263 onRemove: function()
26265 _this.fireEvent('OverlayViewOnRemove', _this);
26268 show: function(cpx)
26270 _this.fireEvent('OverlayViewShow', _this, cpx);
26275 _this.fireEvent('OverlayViewHide', _this);
26281 fromLatLngToContainerPixel: function(event)
26283 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26286 isApplied: function()
26288 return this.getGmapContext() == false ? false : true;
26291 getGmapContext: function()
26293 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26296 GMapContext: function()
26298 var position = new google.maps.LatLng(this.latitude, this.longitude);
26300 var _map = new google.maps.Map(this.el.dom, {
26303 mapTypeId: this.mapTypeId,
26304 mapTypeControl: this.mapTypeControl,
26305 disableDoubleClickZoom: this.disableDoubleClickZoom,
26306 scrollwheel: this.scrollwheel,
26307 streetViewControl: this.streetViewControl,
26308 locationName: this.locationName,
26309 draggable: this.draggable,
26310 enableAutocomplete: this.enableAutocomplete,
26311 enableReverseGeocode: this.enableReverseGeocode
26314 var _marker = new google.maps.Marker({
26315 position: position,
26317 title: this.markerTitle,
26318 draggable: this.draggable
26325 location: position,
26326 radius: this.radius,
26327 locationName: this.locationName,
26328 addressComponents: {
26329 formatted_address: null,
26330 addressLine1: null,
26331 addressLine2: null,
26333 streetNumber: null,
26337 stateOrProvince: null
26340 domContainer: this.el.dom,
26341 geodecoder: new google.maps.Geocoder()
26345 drawCircle: function(center, radius, options)
26347 if (this.gMapContext.circle != null) {
26348 this.gMapContext.circle.setMap(null);
26352 options = Roo.apply({}, options, {
26353 strokeColor: "#0000FF",
26354 strokeOpacity: .35,
26356 fillColor: "#0000FF",
26360 options.map = this.gMapContext.map;
26361 options.radius = radius;
26362 options.center = center;
26363 this.gMapContext.circle = new google.maps.Circle(options);
26364 return this.gMapContext.circle;
26370 setPosition: function(location)
26372 this.gMapContext.location = location;
26373 this.gMapContext.marker.setPosition(location);
26374 this.gMapContext.map.panTo(location);
26375 this.drawCircle(location, this.gMapContext.radius, {});
26379 if (this.gMapContext.settings.enableReverseGeocode) {
26380 this.gMapContext.geodecoder.geocode({
26381 latLng: this.gMapContext.location
26382 }, function(results, status) {
26384 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26385 _this.gMapContext.locationName = results[0].formatted_address;
26386 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26388 _this.fireEvent('positionchanged', this, location);
26395 this.fireEvent('positionchanged', this, location);
26400 google.maps.event.trigger(this.gMapContext.map, "resize");
26402 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26404 this.fireEvent('resize', this);
26407 setPositionByLatLng: function(latitude, longitude)
26409 this.setPosition(new google.maps.LatLng(latitude, longitude));
26412 getCurrentPosition: function()
26415 latitude: this.gMapContext.location.lat(),
26416 longitude: this.gMapContext.location.lng()
26420 getAddressName: function()
26422 return this.gMapContext.locationName;
26425 getAddressComponents: function()
26427 return this.gMapContext.addressComponents;
26430 address_component_from_google_geocode: function(address_components)
26434 for (var i = 0; i < address_components.length; i++) {
26435 var component = address_components[i];
26436 if (component.types.indexOf("postal_code") >= 0) {
26437 result.postalCode = component.short_name;
26438 } else if (component.types.indexOf("street_number") >= 0) {
26439 result.streetNumber = component.short_name;
26440 } else if (component.types.indexOf("route") >= 0) {
26441 result.streetName = component.short_name;
26442 } else if (component.types.indexOf("neighborhood") >= 0) {
26443 result.city = component.short_name;
26444 } else if (component.types.indexOf("locality") >= 0) {
26445 result.city = component.short_name;
26446 } else if (component.types.indexOf("sublocality") >= 0) {
26447 result.district = component.short_name;
26448 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26449 result.stateOrProvince = component.short_name;
26450 } else if (component.types.indexOf("country") >= 0) {
26451 result.country = component.short_name;
26455 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26456 result.addressLine2 = "";
26460 setZoomLevel: function(zoom)
26462 this.gMapContext.map.setZoom(zoom);
26475 this.fireEvent('show', this);
26486 this.fireEvent('hide', this);
26491 Roo.apply(Roo.bootstrap.LocationPicker, {
26493 OverlayView : function(map, options)
26495 options = options || {};
26509 * @class Roo.bootstrap.Alert
26510 * @extends Roo.bootstrap.Component
26511 * Bootstrap Alert class
26512 * @cfg {String} title The title of alert
26513 * @cfg {String} html The content of alert
26514 * @cfg {String} weight ( success | info | warning | danger )
26515 * @cfg {String} faicon font-awesomeicon
26518 * Create a new alert
26519 * @param {Object} config The config object
26523 Roo.bootstrap.Alert = function(config){
26524 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26528 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26535 getAutoCreate : function()
26544 cls : 'roo-alert-icon'
26549 cls : 'roo-alert-title',
26554 cls : 'roo-alert-text',
26561 cfg.cn[0].cls += ' fa ' + this.faicon;
26565 cfg.cls += ' alert-' + this.weight;
26571 initEvents: function()
26573 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26576 setTitle : function(str)
26578 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26581 setText : function(str)
26583 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26586 setWeight : function(weight)
26589 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26592 this.weight = weight;
26594 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26597 setIcon : function(icon)
26600 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26603 this.faicon = icon;
26605 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26626 * @class Roo.bootstrap.UploadCropbox
26627 * @extends Roo.bootstrap.Component
26628 * Bootstrap UploadCropbox class
26629 * @cfg {String} emptyText show when image has been loaded
26630 * @cfg {String} rotateNotify show when image too small to rotate
26631 * @cfg {Number} errorTimeout default 3000
26632 * @cfg {Number} minWidth default 300
26633 * @cfg {Number} minHeight default 300
26634 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26635 * @cfg {Boolean} isDocument (true|false) default false
26636 * @cfg {String} url action url
26637 * @cfg {String} paramName default 'imageUpload'
26638 * @cfg {String} method default POST
26639 * @cfg {Boolean} loadMask (true|false) default true
26640 * @cfg {Boolean} loadingText default 'Loading...'
26643 * Create a new UploadCropbox
26644 * @param {Object} config The config object
26647 Roo.bootstrap.UploadCropbox = function(config){
26648 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26652 * @event beforeselectfile
26653 * Fire before select file
26654 * @param {Roo.bootstrap.UploadCropbox} this
26656 "beforeselectfile" : true,
26659 * Fire after initEvent
26660 * @param {Roo.bootstrap.UploadCropbox} this
26665 * Fire after initEvent
26666 * @param {Roo.bootstrap.UploadCropbox} this
26667 * @param {String} data
26672 * Fire when preparing the file data
26673 * @param {Roo.bootstrap.UploadCropbox} this
26674 * @param {Object} file
26679 * Fire when get exception
26680 * @param {Roo.bootstrap.UploadCropbox} this
26681 * @param {XMLHttpRequest} xhr
26683 "exception" : true,
26685 * @event beforeloadcanvas
26686 * Fire before load the canvas
26687 * @param {Roo.bootstrap.UploadCropbox} this
26688 * @param {String} src
26690 "beforeloadcanvas" : true,
26693 * Fire when trash image
26694 * @param {Roo.bootstrap.UploadCropbox} this
26699 * Fire when download the image
26700 * @param {Roo.bootstrap.UploadCropbox} this
26704 * @event footerbuttonclick
26705 * Fire when footerbuttonclick
26706 * @param {Roo.bootstrap.UploadCropbox} this
26707 * @param {String} type
26709 "footerbuttonclick" : true,
26713 * @param {Roo.bootstrap.UploadCropbox} this
26718 * Fire when rotate the image
26719 * @param {Roo.bootstrap.UploadCropbox} this
26720 * @param {String} pos
26725 * Fire when inspect the file
26726 * @param {Roo.bootstrap.UploadCropbox} this
26727 * @param {Object} file
26732 * Fire when xhr upload the file
26733 * @param {Roo.bootstrap.UploadCropbox} this
26734 * @param {Object} data
26739 * Fire when arrange the file data
26740 * @param {Roo.bootstrap.UploadCropbox} this
26741 * @param {Object} formData
26746 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26749 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26751 emptyText : 'Click to upload image',
26752 rotateNotify : 'Image is too small to rotate',
26753 errorTimeout : 3000,
26767 cropType : 'image/jpeg',
26769 canvasLoaded : false,
26770 isDocument : false,
26772 paramName : 'imageUpload',
26774 loadingText : 'Loading...',
26777 getAutoCreate : function()
26781 cls : 'roo-upload-cropbox',
26785 cls : 'roo-upload-cropbox-selector',
26790 cls : 'roo-upload-cropbox-body',
26791 style : 'cursor:pointer',
26795 cls : 'roo-upload-cropbox-preview'
26799 cls : 'roo-upload-cropbox-thumb'
26803 cls : 'roo-upload-cropbox-empty-notify',
26804 html : this.emptyText
26808 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26809 html : this.rotateNotify
26815 cls : 'roo-upload-cropbox-footer',
26818 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26828 onRender : function(ct, position)
26830 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26832 if (this.buttons.length) {
26834 Roo.each(this.buttons, function(bb) {
26836 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26838 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26844 this.maskEl = this.el;
26848 initEvents : function()
26850 this.urlAPI = (window.createObjectURL && window) ||
26851 (window.URL && URL.revokeObjectURL && URL) ||
26852 (window.webkitURL && webkitURL);
26854 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26855 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26857 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26858 this.selectorEl.hide();
26860 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26861 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26863 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26864 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26865 this.thumbEl.hide();
26867 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26868 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26870 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26871 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26872 this.errorEl.hide();
26874 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26875 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26876 this.footerEl.hide();
26878 this.setThumbBoxSize();
26884 this.fireEvent('initial', this);
26891 window.addEventListener("resize", function() { _this.resize(); } );
26893 this.bodyEl.on('click', this.beforeSelectFile, this);
26896 this.bodyEl.on('touchstart', this.onTouchStart, this);
26897 this.bodyEl.on('touchmove', this.onTouchMove, this);
26898 this.bodyEl.on('touchend', this.onTouchEnd, this);
26902 this.bodyEl.on('mousedown', this.onMouseDown, this);
26903 this.bodyEl.on('mousemove', this.onMouseMove, this);
26904 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26905 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26906 Roo.get(document).on('mouseup', this.onMouseUp, this);
26909 this.selectorEl.on('change', this.onFileSelected, this);
26915 this.baseScale = 1;
26917 this.baseRotate = 1;
26918 this.dragable = false;
26919 this.pinching = false;
26922 this.cropData = false;
26923 this.notifyEl.dom.innerHTML = this.emptyText;
26925 this.selectorEl.dom.value = '';
26929 resize : function()
26931 if(this.fireEvent('resize', this) != false){
26932 this.setThumbBoxPosition();
26933 this.setCanvasPosition();
26937 onFooterButtonClick : function(e, el, o, type)
26940 case 'rotate-left' :
26941 this.onRotateLeft(e);
26943 case 'rotate-right' :
26944 this.onRotateRight(e);
26947 this.beforeSelectFile(e);
26962 this.fireEvent('footerbuttonclick', this, type);
26965 beforeSelectFile : function(e)
26967 e.preventDefault();
26969 if(this.fireEvent('beforeselectfile', this) != false){
26970 this.selectorEl.dom.click();
26974 onFileSelected : function(e)
26976 e.preventDefault();
26978 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26982 var file = this.selectorEl.dom.files[0];
26984 if(this.fireEvent('inspect', this, file) != false){
26985 this.prepare(file);
26990 trash : function(e)
26992 this.fireEvent('trash', this);
26995 download : function(e)
26997 this.fireEvent('download', this);
27000 loadCanvas : function(src)
27002 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27006 this.imageEl = document.createElement('img');
27010 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27012 this.imageEl.src = src;
27016 onLoadCanvas : function()
27018 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27019 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27021 this.bodyEl.un('click', this.beforeSelectFile, this);
27023 this.notifyEl.hide();
27024 this.thumbEl.show();
27025 this.footerEl.show();
27027 this.baseRotateLevel();
27029 if(this.isDocument){
27030 this.setThumbBoxSize();
27033 this.setThumbBoxPosition();
27035 this.baseScaleLevel();
27041 this.canvasLoaded = true;
27044 this.maskEl.unmask();
27049 setCanvasPosition : function()
27051 if(!this.canvasEl){
27055 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27056 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27058 this.previewEl.setLeft(pw);
27059 this.previewEl.setTop(ph);
27063 onMouseDown : function(e)
27067 this.dragable = true;
27068 this.pinching = false;
27070 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27071 this.dragable = false;
27075 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27076 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27080 onMouseMove : function(e)
27084 if(!this.canvasLoaded){
27088 if (!this.dragable){
27092 var minX = Math.ceil(this.thumbEl.getLeft(true));
27093 var minY = Math.ceil(this.thumbEl.getTop(true));
27095 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27096 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27098 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27099 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27101 x = x - this.mouseX;
27102 y = y - this.mouseY;
27104 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27105 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27107 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27108 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27110 this.previewEl.setLeft(bgX);
27111 this.previewEl.setTop(bgY);
27113 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27114 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27117 onMouseUp : function(e)
27121 this.dragable = false;
27124 onMouseWheel : function(e)
27128 this.startScale = this.scale;
27130 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27132 if(!this.zoomable()){
27133 this.scale = this.startScale;
27142 zoomable : function()
27144 var minScale = this.thumbEl.getWidth() / this.minWidth;
27146 if(this.minWidth < this.minHeight){
27147 minScale = this.thumbEl.getHeight() / this.minHeight;
27150 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27151 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27155 (this.rotate == 0 || this.rotate == 180) &&
27157 width > this.imageEl.OriginWidth ||
27158 height > this.imageEl.OriginHeight ||
27159 (width < this.minWidth && height < this.minHeight)
27167 (this.rotate == 90 || this.rotate == 270) &&
27169 width > this.imageEl.OriginWidth ||
27170 height > this.imageEl.OriginHeight ||
27171 (width < this.minHeight && height < this.minWidth)
27178 !this.isDocument &&
27179 (this.rotate == 0 || this.rotate == 180) &&
27181 width < this.minWidth ||
27182 width > this.imageEl.OriginWidth ||
27183 height < this.minHeight ||
27184 height > this.imageEl.OriginHeight
27191 !this.isDocument &&
27192 (this.rotate == 90 || this.rotate == 270) &&
27194 width < this.minHeight ||
27195 width > this.imageEl.OriginWidth ||
27196 height < this.minWidth ||
27197 height > this.imageEl.OriginHeight
27207 onRotateLeft : function(e)
27209 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27211 var minScale = this.thumbEl.getWidth() / this.minWidth;
27213 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27214 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27216 this.startScale = this.scale;
27218 while (this.getScaleLevel() < minScale){
27220 this.scale = this.scale + 1;
27222 if(!this.zoomable()){
27227 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27228 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27233 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27240 this.scale = this.startScale;
27242 this.onRotateFail();
27247 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27249 if(this.isDocument){
27250 this.setThumbBoxSize();
27251 this.setThumbBoxPosition();
27252 this.setCanvasPosition();
27257 this.fireEvent('rotate', this, 'left');
27261 onRotateRight : function(e)
27263 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27265 var minScale = this.thumbEl.getWidth() / this.minWidth;
27267 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27268 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27270 this.startScale = this.scale;
27272 while (this.getScaleLevel() < minScale){
27274 this.scale = this.scale + 1;
27276 if(!this.zoomable()){
27281 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27282 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27287 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27294 this.scale = this.startScale;
27296 this.onRotateFail();
27301 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27303 if(this.isDocument){
27304 this.setThumbBoxSize();
27305 this.setThumbBoxPosition();
27306 this.setCanvasPosition();
27311 this.fireEvent('rotate', this, 'right');
27314 onRotateFail : function()
27316 this.errorEl.show(true);
27320 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27325 this.previewEl.dom.innerHTML = '';
27327 var canvasEl = document.createElement("canvas");
27329 var contextEl = canvasEl.getContext("2d");
27331 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27332 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27333 var center = this.imageEl.OriginWidth / 2;
27335 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27336 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27337 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27338 center = this.imageEl.OriginHeight / 2;
27341 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27343 contextEl.translate(center, center);
27344 contextEl.rotate(this.rotate * Math.PI / 180);
27346 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27348 this.canvasEl = document.createElement("canvas");
27350 this.contextEl = this.canvasEl.getContext("2d");
27352 switch (this.rotate) {
27355 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27356 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27358 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27363 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27364 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27366 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27367 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);
27371 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27376 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27377 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27379 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27380 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);
27384 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);
27389 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27390 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27392 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27393 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27397 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);
27404 this.previewEl.appendChild(this.canvasEl);
27406 this.setCanvasPosition();
27411 if(!this.canvasLoaded){
27415 var imageCanvas = document.createElement("canvas");
27417 var imageContext = imageCanvas.getContext("2d");
27419 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27420 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27422 var center = imageCanvas.width / 2;
27424 imageContext.translate(center, center);
27426 imageContext.rotate(this.rotate * Math.PI / 180);
27428 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27430 var canvas = document.createElement("canvas");
27432 var context = canvas.getContext("2d");
27434 canvas.width = this.minWidth;
27435 canvas.height = this.minHeight;
27437 switch (this.rotate) {
27440 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27441 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27443 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27444 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27446 var targetWidth = this.minWidth - 2 * x;
27447 var targetHeight = this.minHeight - 2 * y;
27451 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27452 scale = targetWidth / width;
27455 if(x > 0 && y == 0){
27456 scale = targetHeight / height;
27459 if(x > 0 && y > 0){
27460 scale = targetWidth / width;
27462 if(width < height){
27463 scale = targetHeight / height;
27467 context.scale(scale, scale);
27469 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27470 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27472 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27473 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27475 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27480 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27481 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27483 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27484 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27486 var targetWidth = this.minWidth - 2 * x;
27487 var targetHeight = this.minHeight - 2 * y;
27491 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27492 scale = targetWidth / width;
27495 if(x > 0 && y == 0){
27496 scale = targetHeight / height;
27499 if(x > 0 && y > 0){
27500 scale = targetWidth / width;
27502 if(width < height){
27503 scale = targetHeight / height;
27507 context.scale(scale, scale);
27509 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27510 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27512 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27513 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27515 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27517 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27522 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27523 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27525 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27526 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27528 var targetWidth = this.minWidth - 2 * x;
27529 var targetHeight = this.minHeight - 2 * y;
27533 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27534 scale = targetWidth / width;
27537 if(x > 0 && y == 0){
27538 scale = targetHeight / height;
27541 if(x > 0 && y > 0){
27542 scale = targetWidth / width;
27544 if(width < height){
27545 scale = targetHeight / height;
27549 context.scale(scale, scale);
27551 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27552 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27554 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27555 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27557 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27558 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27560 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27565 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27566 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27568 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27569 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27571 var targetWidth = this.minWidth - 2 * x;
27572 var targetHeight = this.minHeight - 2 * y;
27576 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27577 scale = targetWidth / width;
27580 if(x > 0 && y == 0){
27581 scale = targetHeight / height;
27584 if(x > 0 && y > 0){
27585 scale = targetWidth / width;
27587 if(width < height){
27588 scale = targetHeight / height;
27592 context.scale(scale, scale);
27594 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27595 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27597 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27598 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27600 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27602 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27609 this.cropData = canvas.toDataURL(this.cropType);
27611 if(this.fireEvent('crop', this, this.cropData) !== false){
27612 this.process(this.file, this.cropData);
27619 setThumbBoxSize : function()
27623 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27624 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27625 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27627 this.minWidth = width;
27628 this.minHeight = height;
27630 if(this.rotate == 90 || this.rotate == 270){
27631 this.minWidth = height;
27632 this.minHeight = width;
27637 width = Math.ceil(this.minWidth * height / this.minHeight);
27639 if(this.minWidth > this.minHeight){
27641 height = Math.ceil(this.minHeight * width / this.minWidth);
27644 this.thumbEl.setStyle({
27645 width : width + 'px',
27646 height : height + 'px'
27653 setThumbBoxPosition : function()
27655 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27656 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27658 this.thumbEl.setLeft(x);
27659 this.thumbEl.setTop(y);
27663 baseRotateLevel : function()
27665 this.baseRotate = 1;
27668 typeof(this.exif) != 'undefined' &&
27669 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27670 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27672 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27675 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27679 baseScaleLevel : function()
27683 if(this.isDocument){
27685 if(this.baseRotate == 6 || this.baseRotate == 8){
27687 height = this.thumbEl.getHeight();
27688 this.baseScale = height / this.imageEl.OriginWidth;
27690 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27691 width = this.thumbEl.getWidth();
27692 this.baseScale = width / this.imageEl.OriginHeight;
27698 height = this.thumbEl.getHeight();
27699 this.baseScale = height / this.imageEl.OriginHeight;
27701 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27702 width = this.thumbEl.getWidth();
27703 this.baseScale = width / this.imageEl.OriginWidth;
27709 if(this.baseRotate == 6 || this.baseRotate == 8){
27711 width = this.thumbEl.getHeight();
27712 this.baseScale = width / this.imageEl.OriginHeight;
27714 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27715 height = this.thumbEl.getWidth();
27716 this.baseScale = height / this.imageEl.OriginHeight;
27719 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27720 height = this.thumbEl.getWidth();
27721 this.baseScale = height / this.imageEl.OriginHeight;
27723 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27724 width = this.thumbEl.getHeight();
27725 this.baseScale = width / this.imageEl.OriginWidth;
27732 width = this.thumbEl.getWidth();
27733 this.baseScale = width / this.imageEl.OriginWidth;
27735 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27736 height = this.thumbEl.getHeight();
27737 this.baseScale = height / this.imageEl.OriginHeight;
27740 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27742 height = this.thumbEl.getHeight();
27743 this.baseScale = height / this.imageEl.OriginHeight;
27745 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27746 width = this.thumbEl.getWidth();
27747 this.baseScale = width / this.imageEl.OriginWidth;
27755 getScaleLevel : function()
27757 return this.baseScale * Math.pow(1.1, this.scale);
27760 onTouchStart : function(e)
27762 if(!this.canvasLoaded){
27763 this.beforeSelectFile(e);
27767 var touches = e.browserEvent.touches;
27773 if(touches.length == 1){
27774 this.onMouseDown(e);
27778 if(touches.length != 2){
27784 for(var i = 0, finger; finger = touches[i]; i++){
27785 coords.push(finger.pageX, finger.pageY);
27788 var x = Math.pow(coords[0] - coords[2], 2);
27789 var y = Math.pow(coords[1] - coords[3], 2);
27791 this.startDistance = Math.sqrt(x + y);
27793 this.startScale = this.scale;
27795 this.pinching = true;
27796 this.dragable = false;
27800 onTouchMove : function(e)
27802 if(!this.pinching && !this.dragable){
27806 var touches = e.browserEvent.touches;
27813 this.onMouseMove(e);
27819 for(var i = 0, finger; finger = touches[i]; i++){
27820 coords.push(finger.pageX, finger.pageY);
27823 var x = Math.pow(coords[0] - coords[2], 2);
27824 var y = Math.pow(coords[1] - coords[3], 2);
27826 this.endDistance = Math.sqrt(x + y);
27828 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27830 if(!this.zoomable()){
27831 this.scale = this.startScale;
27839 onTouchEnd : function(e)
27841 this.pinching = false;
27842 this.dragable = false;
27846 process : function(file, crop)
27849 this.maskEl.mask(this.loadingText);
27852 this.xhr = new XMLHttpRequest();
27854 file.xhr = this.xhr;
27856 this.xhr.open(this.method, this.url, true);
27859 "Accept": "application/json",
27860 "Cache-Control": "no-cache",
27861 "X-Requested-With": "XMLHttpRequest"
27864 for (var headerName in headers) {
27865 var headerValue = headers[headerName];
27867 this.xhr.setRequestHeader(headerName, headerValue);
27873 this.xhr.onload = function()
27875 _this.xhrOnLoad(_this.xhr);
27878 this.xhr.onerror = function()
27880 _this.xhrOnError(_this.xhr);
27883 var formData = new FormData();
27885 formData.append('returnHTML', 'NO');
27888 formData.append('crop', crop);
27891 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27892 formData.append(this.paramName, file, file.name);
27895 if(typeof(file.filename) != 'undefined'){
27896 formData.append('filename', file.filename);
27899 if(typeof(file.mimetype) != 'undefined'){
27900 formData.append('mimetype', file.mimetype);
27903 if(this.fireEvent('arrange', this, formData) != false){
27904 this.xhr.send(formData);
27908 xhrOnLoad : function(xhr)
27911 this.maskEl.unmask();
27914 if (xhr.readyState !== 4) {
27915 this.fireEvent('exception', this, xhr);
27919 var response = Roo.decode(xhr.responseText);
27921 if(!response.success){
27922 this.fireEvent('exception', this, xhr);
27926 var response = Roo.decode(xhr.responseText);
27928 this.fireEvent('upload', this, response);
27932 xhrOnError : function()
27935 this.maskEl.unmask();
27938 Roo.log('xhr on error');
27940 var response = Roo.decode(xhr.responseText);
27946 prepare : function(file)
27949 this.maskEl.mask(this.loadingText);
27955 if(typeof(file) === 'string'){
27956 this.loadCanvas(file);
27960 if(!file || !this.urlAPI){
27965 this.cropType = file.type;
27969 if(this.fireEvent('prepare', this, this.file) != false){
27971 var reader = new FileReader();
27973 reader.onload = function (e) {
27974 if (e.target.error) {
27975 Roo.log(e.target.error);
27979 var buffer = e.target.result,
27980 dataView = new DataView(buffer),
27982 maxOffset = dataView.byteLength - 4,
27986 if (dataView.getUint16(0) === 0xffd8) {
27987 while (offset < maxOffset) {
27988 markerBytes = dataView.getUint16(offset);
27990 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27991 markerLength = dataView.getUint16(offset + 2) + 2;
27992 if (offset + markerLength > dataView.byteLength) {
27993 Roo.log('Invalid meta data: Invalid segment size.');
27997 if(markerBytes == 0xffe1){
27998 _this.parseExifData(
28005 offset += markerLength;
28015 var url = _this.urlAPI.createObjectURL(_this.file);
28017 _this.loadCanvas(url);
28022 reader.readAsArrayBuffer(this.file);
28028 parseExifData : function(dataView, offset, length)
28030 var tiffOffset = offset + 10,
28034 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28035 // No Exif data, might be XMP data instead
28039 // Check for the ASCII code for "Exif" (0x45786966):
28040 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28041 // No Exif data, might be XMP data instead
28044 if (tiffOffset + 8 > dataView.byteLength) {
28045 Roo.log('Invalid Exif data: Invalid segment size.');
28048 // Check for the two null bytes:
28049 if (dataView.getUint16(offset + 8) !== 0x0000) {
28050 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28053 // Check the byte alignment:
28054 switch (dataView.getUint16(tiffOffset)) {
28056 littleEndian = true;
28059 littleEndian = false;
28062 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28065 // Check for the TIFF tag marker (0x002A):
28066 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28067 Roo.log('Invalid Exif data: Missing TIFF marker.');
28070 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28071 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28073 this.parseExifTags(
28076 tiffOffset + dirOffset,
28081 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28086 if (dirOffset + 6 > dataView.byteLength) {
28087 Roo.log('Invalid Exif data: Invalid directory offset.');
28090 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28091 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28092 if (dirEndOffset + 4 > dataView.byteLength) {
28093 Roo.log('Invalid Exif data: Invalid directory size.');
28096 for (i = 0; i < tagsNumber; i += 1) {
28100 dirOffset + 2 + 12 * i, // tag offset
28104 // Return the offset to the next directory:
28105 return dataView.getUint32(dirEndOffset, littleEndian);
28108 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28110 var tag = dataView.getUint16(offset, littleEndian);
28112 this.exif[tag] = this.getExifValue(
28116 dataView.getUint16(offset + 2, littleEndian), // tag type
28117 dataView.getUint32(offset + 4, littleEndian), // tag length
28122 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28124 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28133 Roo.log('Invalid Exif data: Invalid tag type.');
28137 tagSize = tagType.size * length;
28138 // Determine if the value is contained in the dataOffset bytes,
28139 // or if the value at the dataOffset is a pointer to the actual data:
28140 dataOffset = tagSize > 4 ?
28141 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28142 if (dataOffset + tagSize > dataView.byteLength) {
28143 Roo.log('Invalid Exif data: Invalid data offset.');
28146 if (length === 1) {
28147 return tagType.getValue(dataView, dataOffset, littleEndian);
28150 for (i = 0; i < length; i += 1) {
28151 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28154 if (tagType.ascii) {
28156 // Concatenate the chars:
28157 for (i = 0; i < values.length; i += 1) {
28159 // Ignore the terminating NULL byte(s):
28160 if (c === '\u0000') {
28172 Roo.apply(Roo.bootstrap.UploadCropbox, {
28174 'Orientation': 0x0112
28178 1: 0, //'top-left',
28180 3: 180, //'bottom-right',
28181 // 4: 'bottom-left',
28183 6: 90, //'right-top',
28184 // 7: 'right-bottom',
28185 8: 270 //'left-bottom'
28189 // byte, 8-bit unsigned int:
28191 getValue: function (dataView, dataOffset) {
28192 return dataView.getUint8(dataOffset);
28196 // ascii, 8-bit byte:
28198 getValue: function (dataView, dataOffset) {
28199 return String.fromCharCode(dataView.getUint8(dataOffset));
28204 // short, 16 bit int:
28206 getValue: function (dataView, dataOffset, littleEndian) {
28207 return dataView.getUint16(dataOffset, littleEndian);
28211 // long, 32 bit int:
28213 getValue: function (dataView, dataOffset, littleEndian) {
28214 return dataView.getUint32(dataOffset, littleEndian);
28218 // rational = two long values, first is numerator, second is denominator:
28220 getValue: function (dataView, dataOffset, littleEndian) {
28221 return dataView.getUint32(dataOffset, littleEndian) /
28222 dataView.getUint32(dataOffset + 4, littleEndian);
28226 // slong, 32 bit signed int:
28228 getValue: function (dataView, dataOffset, littleEndian) {
28229 return dataView.getInt32(dataOffset, littleEndian);
28233 // srational, two slongs, first is numerator, second is denominator:
28235 getValue: function (dataView, dataOffset, littleEndian) {
28236 return dataView.getInt32(dataOffset, littleEndian) /
28237 dataView.getInt32(dataOffset + 4, littleEndian);
28247 cls : 'btn-group roo-upload-cropbox-rotate-left',
28248 action : 'rotate-left',
28252 cls : 'btn btn-default',
28253 html : '<i class="fa fa-undo"></i>'
28259 cls : 'btn-group roo-upload-cropbox-picture',
28260 action : 'picture',
28264 cls : 'btn btn-default',
28265 html : '<i class="fa fa-picture-o"></i>'
28271 cls : 'btn-group roo-upload-cropbox-rotate-right',
28272 action : 'rotate-right',
28276 cls : 'btn btn-default',
28277 html : '<i class="fa fa-repeat"></i>'
28285 cls : 'btn-group roo-upload-cropbox-rotate-left',
28286 action : 'rotate-left',
28290 cls : 'btn btn-default',
28291 html : '<i class="fa fa-undo"></i>'
28297 cls : 'btn-group roo-upload-cropbox-download',
28298 action : 'download',
28302 cls : 'btn btn-default',
28303 html : '<i class="fa fa-download"></i>'
28309 cls : 'btn-group roo-upload-cropbox-crop',
28314 cls : 'btn btn-default',
28315 html : '<i class="fa fa-crop"></i>'
28321 cls : 'btn-group roo-upload-cropbox-trash',
28326 cls : 'btn btn-default',
28327 html : '<i class="fa fa-trash"></i>'
28333 cls : 'btn-group roo-upload-cropbox-rotate-right',
28334 action : 'rotate-right',
28338 cls : 'btn btn-default',
28339 html : '<i class="fa fa-repeat"></i>'
28347 cls : 'btn-group roo-upload-cropbox-rotate-left',
28348 action : 'rotate-left',
28352 cls : 'btn btn-default',
28353 html : '<i class="fa fa-undo"></i>'
28359 cls : 'btn-group roo-upload-cropbox-rotate-right',
28360 action : 'rotate-right',
28364 cls : 'btn btn-default',
28365 html : '<i class="fa fa-repeat"></i>'
28378 * @class Roo.bootstrap.DocumentManager
28379 * @extends Roo.bootstrap.Component
28380 * Bootstrap DocumentManager class
28381 * @cfg {String} paramName default 'imageUpload'
28382 * @cfg {String} toolTipName default 'filename'
28383 * @cfg {String} method default POST
28384 * @cfg {String} url action url
28385 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28386 * @cfg {Boolean} multiple multiple upload default true
28387 * @cfg {Number} thumbSize default 300
28388 * @cfg {String} fieldLabel
28389 * @cfg {Number} labelWidth default 4
28390 * @cfg {String} labelAlign (left|top) default left
28391 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28392 * @cfg {Number} labellg set the width of label (1-12)
28393 * @cfg {Number} labelmd set the width of label (1-12)
28394 * @cfg {Number} labelsm set the width of label (1-12)
28395 * @cfg {Number} labelxs set the width of label (1-12)
28398 * Create a new DocumentManager
28399 * @param {Object} config The config object
28402 Roo.bootstrap.DocumentManager = function(config){
28403 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28406 this.delegates = [];
28411 * Fire when initial the DocumentManager
28412 * @param {Roo.bootstrap.DocumentManager} this
28417 * inspect selected file
28418 * @param {Roo.bootstrap.DocumentManager} this
28419 * @param {File} file
28424 * Fire when xhr load exception
28425 * @param {Roo.bootstrap.DocumentManager} this
28426 * @param {XMLHttpRequest} xhr
28428 "exception" : true,
28430 * @event afterupload
28431 * Fire when xhr load exception
28432 * @param {Roo.bootstrap.DocumentManager} this
28433 * @param {XMLHttpRequest} xhr
28435 "afterupload" : true,
28438 * prepare the form data
28439 * @param {Roo.bootstrap.DocumentManager} this
28440 * @param {Object} formData
28445 * Fire when remove the file
28446 * @param {Roo.bootstrap.DocumentManager} this
28447 * @param {Object} file
28452 * Fire after refresh the file
28453 * @param {Roo.bootstrap.DocumentManager} this
28458 * Fire after click the image
28459 * @param {Roo.bootstrap.DocumentManager} this
28460 * @param {Object} file
28465 * Fire when upload a image and editable set to true
28466 * @param {Roo.bootstrap.DocumentManager} this
28467 * @param {Object} file
28471 * @event beforeselectfile
28472 * Fire before select file
28473 * @param {Roo.bootstrap.DocumentManager} this
28475 "beforeselectfile" : true,
28478 * Fire before process file
28479 * @param {Roo.bootstrap.DocumentManager} this
28480 * @param {Object} file
28484 * @event previewrendered
28485 * Fire when preview rendered
28486 * @param {Roo.bootstrap.DocumentManager} this
28487 * @param {Object} file
28489 "previewrendered" : true
28494 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28503 paramName : 'imageUpload',
28504 toolTipName : 'filename',
28507 labelAlign : 'left',
28517 getAutoCreate : function()
28519 var managerWidget = {
28521 cls : 'roo-document-manager',
28525 cls : 'roo-document-manager-selector',
28530 cls : 'roo-document-manager-uploader',
28534 cls : 'roo-document-manager-upload-btn',
28535 html : '<i class="fa fa-plus"></i>'
28546 cls : 'column col-md-12',
28551 if(this.fieldLabel.length){
28556 cls : 'column col-md-12',
28557 html : this.fieldLabel
28561 cls : 'column col-md-12',
28566 if(this.labelAlign == 'left'){
28571 html : this.fieldLabel
28580 if(this.labelWidth > 12){
28581 content[0].style = "width: " + this.labelWidth + 'px';
28584 if(this.labelWidth < 13 && this.labelmd == 0){
28585 this.labelmd = this.labelWidth;
28588 if(this.labellg > 0){
28589 content[0].cls += ' col-lg-' + this.labellg;
28590 content[1].cls += ' col-lg-' + (12 - this.labellg);
28593 if(this.labelmd > 0){
28594 content[0].cls += ' col-md-' + this.labelmd;
28595 content[1].cls += ' col-md-' + (12 - this.labelmd);
28598 if(this.labelsm > 0){
28599 content[0].cls += ' col-sm-' + this.labelsm;
28600 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28603 if(this.labelxs > 0){
28604 content[0].cls += ' col-xs-' + this.labelxs;
28605 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28613 cls : 'row clearfix',
28621 initEvents : function()
28623 this.managerEl = this.el.select('.roo-document-manager', true).first();
28624 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28626 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28627 this.selectorEl.hide();
28630 this.selectorEl.attr('multiple', 'multiple');
28633 this.selectorEl.on('change', this.onFileSelected, this);
28635 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28636 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28638 this.uploader.on('click', this.onUploaderClick, this);
28640 this.renderProgressDialog();
28644 window.addEventListener("resize", function() { _this.refresh(); } );
28646 this.fireEvent('initial', this);
28649 renderProgressDialog : function()
28653 this.progressDialog = new Roo.bootstrap.Modal({
28654 cls : 'roo-document-manager-progress-dialog',
28655 allow_close : false,
28665 btnclick : function() {
28666 _this.uploadCancel();
28672 this.progressDialog.render(Roo.get(document.body));
28674 this.progress = new Roo.bootstrap.Progress({
28675 cls : 'roo-document-manager-progress',
28680 this.progress.render(this.progressDialog.getChildContainer());
28682 this.progressBar = new Roo.bootstrap.ProgressBar({
28683 cls : 'roo-document-manager-progress-bar',
28686 aria_valuemax : 12,
28690 this.progressBar.render(this.progress.getChildContainer());
28693 onUploaderClick : function(e)
28695 e.preventDefault();
28697 if(this.fireEvent('beforeselectfile', this) != false){
28698 this.selectorEl.dom.click();
28703 onFileSelected : function(e)
28705 e.preventDefault();
28707 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28711 Roo.each(this.selectorEl.dom.files, function(file){
28712 if(this.fireEvent('inspect', this, file) != false){
28713 this.files.push(file);
28723 this.selectorEl.dom.value = '';
28725 if(!this.files || !this.files.length){
28729 if(this.boxes > 0 && this.files.length > this.boxes){
28730 this.files = this.files.slice(0, this.boxes);
28733 this.uploader.show();
28735 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28736 this.uploader.hide();
28745 Roo.each(this.files, function(file){
28747 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28748 var f = this.renderPreview(file);
28753 if(file.type.indexOf('image') != -1){
28754 this.delegates.push(
28756 _this.process(file);
28757 }).createDelegate(this)
28765 _this.process(file);
28766 }).createDelegate(this)
28771 this.files = files;
28773 this.delegates = this.delegates.concat(docs);
28775 if(!this.delegates.length){
28780 this.progressBar.aria_valuemax = this.delegates.length;
28787 arrange : function()
28789 if(!this.delegates.length){
28790 this.progressDialog.hide();
28795 var delegate = this.delegates.shift();
28797 this.progressDialog.show();
28799 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28801 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28806 refresh : function()
28808 this.uploader.show();
28810 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28811 this.uploader.hide();
28814 Roo.isTouch ? this.closable(false) : this.closable(true);
28816 this.fireEvent('refresh', this);
28819 onRemove : function(e, el, o)
28821 e.preventDefault();
28823 this.fireEvent('remove', this, o);
28827 remove : function(o)
28831 Roo.each(this.files, function(file){
28832 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28841 this.files = files;
28848 Roo.each(this.files, function(file){
28853 file.target.remove();
28862 onClick : function(e, el, o)
28864 e.preventDefault();
28866 this.fireEvent('click', this, o);
28870 closable : function(closable)
28872 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28874 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28886 xhrOnLoad : function(xhr)
28888 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28892 if (xhr.readyState !== 4) {
28894 this.fireEvent('exception', this, xhr);
28898 var response = Roo.decode(xhr.responseText);
28900 if(!response.success){
28902 this.fireEvent('exception', this, xhr);
28906 var file = this.renderPreview(response.data);
28908 this.files.push(file);
28912 this.fireEvent('afterupload', this, xhr);
28916 xhrOnError : function(xhr)
28918 Roo.log('xhr on error');
28920 var response = Roo.decode(xhr.responseText);
28927 process : function(file)
28929 if(this.fireEvent('process', this, file) !== false){
28930 if(this.editable && file.type.indexOf('image') != -1){
28931 this.fireEvent('edit', this, file);
28935 this.uploadStart(file, false);
28942 uploadStart : function(file, crop)
28944 this.xhr = new XMLHttpRequest();
28946 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28951 file.xhr = this.xhr;
28953 this.managerEl.createChild({
28955 cls : 'roo-document-manager-loading',
28959 tooltip : file.name,
28960 cls : 'roo-document-manager-thumb',
28961 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28967 this.xhr.open(this.method, this.url, true);
28970 "Accept": "application/json",
28971 "Cache-Control": "no-cache",
28972 "X-Requested-With": "XMLHttpRequest"
28975 for (var headerName in headers) {
28976 var headerValue = headers[headerName];
28978 this.xhr.setRequestHeader(headerName, headerValue);
28984 this.xhr.onload = function()
28986 _this.xhrOnLoad(_this.xhr);
28989 this.xhr.onerror = function()
28991 _this.xhrOnError(_this.xhr);
28994 var formData = new FormData();
28996 formData.append('returnHTML', 'NO');
28999 formData.append('crop', crop);
29002 formData.append(this.paramName, file, file.name);
29009 if(this.fireEvent('prepare', this, formData, options) != false){
29011 if(options.manually){
29015 this.xhr.send(formData);
29019 this.uploadCancel();
29022 uploadCancel : function()
29028 this.delegates = [];
29030 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29037 renderPreview : function(file)
29039 if(typeof(file.target) != 'undefined' && file.target){
29043 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29045 var previewEl = this.managerEl.createChild({
29047 cls : 'roo-document-manager-preview',
29051 tooltip : file[this.toolTipName],
29052 cls : 'roo-document-manager-thumb',
29053 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29058 html : '<i class="fa fa-times-circle"></i>'
29063 var close = previewEl.select('button.close', true).first();
29065 close.on('click', this.onRemove, this, file);
29067 file.target = previewEl;
29069 var image = previewEl.select('img', true).first();
29073 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29075 image.on('click', this.onClick, this, file);
29077 this.fireEvent('previewrendered', this, file);
29083 onPreviewLoad : function(file, image)
29085 if(typeof(file.target) == 'undefined' || !file.target){
29089 var width = image.dom.naturalWidth || image.dom.width;
29090 var height = image.dom.naturalHeight || image.dom.height;
29092 if(width > height){
29093 file.target.addClass('wide');
29097 file.target.addClass('tall');
29102 uploadFromSource : function(file, crop)
29104 this.xhr = new XMLHttpRequest();
29106 this.managerEl.createChild({
29108 cls : 'roo-document-manager-loading',
29112 tooltip : file.name,
29113 cls : 'roo-document-manager-thumb',
29114 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29120 this.xhr.open(this.method, this.url, true);
29123 "Accept": "application/json",
29124 "Cache-Control": "no-cache",
29125 "X-Requested-With": "XMLHttpRequest"
29128 for (var headerName in headers) {
29129 var headerValue = headers[headerName];
29131 this.xhr.setRequestHeader(headerName, headerValue);
29137 this.xhr.onload = function()
29139 _this.xhrOnLoad(_this.xhr);
29142 this.xhr.onerror = function()
29144 _this.xhrOnError(_this.xhr);
29147 var formData = new FormData();
29149 formData.append('returnHTML', 'NO');
29151 formData.append('crop', crop);
29153 if(typeof(file.filename) != 'undefined'){
29154 formData.append('filename', file.filename);
29157 if(typeof(file.mimetype) != 'undefined'){
29158 formData.append('mimetype', file.mimetype);
29163 if(this.fireEvent('prepare', this, formData) != false){
29164 this.xhr.send(formData);
29174 * @class Roo.bootstrap.DocumentViewer
29175 * @extends Roo.bootstrap.Component
29176 * Bootstrap DocumentViewer class
29177 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29178 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29181 * Create a new DocumentViewer
29182 * @param {Object} config The config object
29185 Roo.bootstrap.DocumentViewer = function(config){
29186 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29191 * Fire after initEvent
29192 * @param {Roo.bootstrap.DocumentViewer} this
29198 * @param {Roo.bootstrap.DocumentViewer} this
29203 * Fire after download button
29204 * @param {Roo.bootstrap.DocumentViewer} this
29209 * Fire after trash button
29210 * @param {Roo.bootstrap.DocumentViewer} this
29217 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29219 showDownload : true,
29223 getAutoCreate : function()
29227 cls : 'roo-document-viewer',
29231 cls : 'roo-document-viewer-body',
29235 cls : 'roo-document-viewer-thumb',
29239 cls : 'roo-document-viewer-image'
29247 cls : 'roo-document-viewer-footer',
29250 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29254 cls : 'btn-group roo-document-viewer-download',
29258 cls : 'btn btn-default',
29259 html : '<i class="fa fa-download"></i>'
29265 cls : 'btn-group roo-document-viewer-trash',
29269 cls : 'btn btn-default',
29270 html : '<i class="fa fa-trash"></i>'
29283 initEvents : function()
29285 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29286 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29288 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29289 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29291 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29292 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29294 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29295 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29297 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29298 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29300 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29301 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29303 this.bodyEl.on('click', this.onClick, this);
29304 this.downloadBtn.on('click', this.onDownload, this);
29305 this.trashBtn.on('click', this.onTrash, this);
29307 this.downloadBtn.hide();
29308 this.trashBtn.hide();
29310 if(this.showDownload){
29311 this.downloadBtn.show();
29314 if(this.showTrash){
29315 this.trashBtn.show();
29318 if(!this.showDownload && !this.showTrash) {
29319 this.footerEl.hide();
29324 initial : function()
29326 this.fireEvent('initial', this);
29330 onClick : function(e)
29332 e.preventDefault();
29334 this.fireEvent('click', this);
29337 onDownload : function(e)
29339 e.preventDefault();
29341 this.fireEvent('download', this);
29344 onTrash : function(e)
29346 e.preventDefault();
29348 this.fireEvent('trash', this);
29360 * @class Roo.bootstrap.NavProgressBar
29361 * @extends Roo.bootstrap.Component
29362 * Bootstrap NavProgressBar class
29365 * Create a new nav progress bar
29366 * @param {Object} config The config object
29369 Roo.bootstrap.NavProgressBar = function(config){
29370 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29372 this.bullets = this.bullets || [];
29374 // Roo.bootstrap.NavProgressBar.register(this);
29378 * Fires when the active item changes
29379 * @param {Roo.bootstrap.NavProgressBar} this
29380 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29381 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29388 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29393 getAutoCreate : function()
29395 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29399 cls : 'roo-navigation-bar-group',
29403 cls : 'roo-navigation-top-bar'
29407 cls : 'roo-navigation-bullets-bar',
29411 cls : 'roo-navigation-bar'
29418 cls : 'roo-navigation-bottom-bar'
29428 initEvents: function()
29433 onRender : function(ct, position)
29435 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29437 if(this.bullets.length){
29438 Roo.each(this.bullets, function(b){
29447 addItem : function(cfg)
29449 var item = new Roo.bootstrap.NavProgressItem(cfg);
29451 item.parentId = this.id;
29452 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29455 var top = new Roo.bootstrap.Element({
29457 cls : 'roo-navigation-bar-text'
29460 var bottom = new Roo.bootstrap.Element({
29462 cls : 'roo-navigation-bar-text'
29465 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29466 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29468 var topText = new Roo.bootstrap.Element({
29470 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29473 var bottomText = new Roo.bootstrap.Element({
29475 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29478 topText.onRender(top.el, null);
29479 bottomText.onRender(bottom.el, null);
29482 item.bottomEl = bottom;
29485 this.barItems.push(item);
29490 getActive : function()
29492 var active = false;
29494 Roo.each(this.barItems, function(v){
29496 if (!v.isActive()) {
29508 setActiveItem : function(item)
29512 Roo.each(this.barItems, function(v){
29513 if (v.rid == item.rid) {
29517 if (v.isActive()) {
29518 v.setActive(false);
29523 item.setActive(true);
29525 this.fireEvent('changed', this, item, prev);
29528 getBarItem: function(rid)
29532 Roo.each(this.barItems, function(e) {
29533 if (e.rid != rid) {
29544 indexOfItem : function(item)
29548 Roo.each(this.barItems, function(v, i){
29550 if (v.rid != item.rid) {
29561 setActiveNext : function()
29563 var i = this.indexOfItem(this.getActive());
29565 if (i > this.barItems.length) {
29569 this.setActiveItem(this.barItems[i+1]);
29572 setActivePrev : function()
29574 var i = this.indexOfItem(this.getActive());
29580 this.setActiveItem(this.barItems[i-1]);
29583 format : function()
29585 if(!this.barItems.length){
29589 var width = 100 / this.barItems.length;
29591 Roo.each(this.barItems, function(i){
29592 i.el.setStyle('width', width + '%');
29593 i.topEl.el.setStyle('width', width + '%');
29594 i.bottomEl.el.setStyle('width', width + '%');
29603 * Nav Progress Item
29608 * @class Roo.bootstrap.NavProgressItem
29609 * @extends Roo.bootstrap.Component
29610 * Bootstrap NavProgressItem class
29611 * @cfg {String} rid the reference id
29612 * @cfg {Boolean} active (true|false) Is item active default false
29613 * @cfg {Boolean} disabled (true|false) Is item active default false
29614 * @cfg {String} html
29615 * @cfg {String} position (top|bottom) text position default bottom
29616 * @cfg {String} icon show icon instead of number
29619 * Create a new NavProgressItem
29620 * @param {Object} config The config object
29622 Roo.bootstrap.NavProgressItem = function(config){
29623 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29628 * The raw click event for the entire grid.
29629 * @param {Roo.bootstrap.NavProgressItem} this
29630 * @param {Roo.EventObject} e
29637 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29643 position : 'bottom',
29646 getAutoCreate : function()
29648 var iconCls = 'roo-navigation-bar-item-icon';
29650 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29654 cls: 'roo-navigation-bar-item',
29664 cfg.cls += ' active';
29667 cfg.cls += ' disabled';
29673 disable : function()
29675 this.setDisabled(true);
29678 enable : function()
29680 this.setDisabled(false);
29683 initEvents: function()
29685 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29687 this.iconEl.on('click', this.onClick, this);
29690 onClick : function(e)
29692 e.preventDefault();
29698 if(this.fireEvent('click', this, e) === false){
29702 this.parent().setActiveItem(this);
29705 isActive: function ()
29707 return this.active;
29710 setActive : function(state)
29712 if(this.active == state){
29716 this.active = state;
29719 this.el.addClass('active');
29723 this.el.removeClass('active');
29728 setDisabled : function(state)
29730 if(this.disabled == state){
29734 this.disabled = state;
29737 this.el.addClass('disabled');
29741 this.el.removeClass('disabled');
29744 tooltipEl : function()
29746 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29759 * @class Roo.bootstrap.FieldLabel
29760 * @extends Roo.bootstrap.Component
29761 * Bootstrap FieldLabel class
29762 * @cfg {String} html contents of the element
29763 * @cfg {String} tag tag of the element default label
29764 * @cfg {String} cls class of the element
29765 * @cfg {String} target label target
29766 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29767 * @cfg {String} invalidClass default "text-warning"
29768 * @cfg {String} validClass default "text-success"
29769 * @cfg {String} iconTooltip default "This field is required"
29770 * @cfg {String} indicatorpos (left|right) default left
29773 * Create a new FieldLabel
29774 * @param {Object} config The config object
29777 Roo.bootstrap.FieldLabel = function(config){
29778 Roo.bootstrap.Element.superclass.constructor.call(this, config);
29783 * Fires after the field has been marked as invalid.
29784 * @param {Roo.form.FieldLabel} this
29785 * @param {String} msg The validation message
29790 * Fires after the field has been validated with no errors.
29791 * @param {Roo.form.FieldLabel} this
29797 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
29804 invalidClass : 'has-warning',
29805 validClass : 'has-success',
29806 iconTooltip : 'This field is required',
29807 indicatorpos : 'left',
29809 getAutoCreate : function(){
29813 cls : 'roo-bootstrap-field-label ' + this.cls,
29818 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29819 tooltip : this.iconTooltip
29828 if(this.indicatorpos == 'right'){
29831 cls : 'roo-bootstrap-field-label ' + this.cls,
29840 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29841 tooltip : this.iconTooltip
29850 initEvents: function()
29852 Roo.bootstrap.Element.superclass.initEvents.call(this);
29854 this.indicator = this.indicatorEl();
29856 if(this.indicator){
29857 this.indicator.removeClass('visible');
29858 this.indicator.addClass('invisible');
29861 Roo.bootstrap.FieldLabel.register(this);
29864 indicatorEl : function()
29866 var indicator = this.el.select('i.roo-required-indicator',true).first();
29877 * Mark this field as valid
29879 markValid : function()
29881 if(this.indicator){
29882 this.indicator.removeClass('visible');
29883 this.indicator.addClass('invisible');
29886 this.el.removeClass(this.invalidClass);
29888 this.el.addClass(this.validClass);
29890 this.fireEvent('valid', this);
29894 * Mark this field as invalid
29895 * @param {String} msg The validation message
29897 markInvalid : function(msg)
29899 if(this.indicator){
29900 this.indicator.removeClass('invisible');
29901 this.indicator.addClass('visible');
29904 this.el.removeClass(this.validClass);
29906 this.el.addClass(this.invalidClass);
29908 this.fireEvent('invalid', this, msg);
29914 Roo.apply(Roo.bootstrap.FieldLabel, {
29919 * register a FieldLabel Group
29920 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29922 register : function(label)
29924 if(this.groups.hasOwnProperty(label.target)){
29928 this.groups[label.target] = label;
29932 * fetch a FieldLabel Group based on the target
29933 * @param {string} target
29934 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29936 get: function(target) {
29937 if (typeof(this.groups[target]) == 'undefined') {
29941 return this.groups[target] ;
29950 * page DateSplitField.
29956 * @class Roo.bootstrap.DateSplitField
29957 * @extends Roo.bootstrap.Component
29958 * Bootstrap DateSplitField class
29959 * @cfg {string} fieldLabel - the label associated
29960 * @cfg {Number} labelWidth set the width of label (0-12)
29961 * @cfg {String} labelAlign (top|left)
29962 * @cfg {Boolean} dayAllowBlank (true|false) default false
29963 * @cfg {Boolean} monthAllowBlank (true|false) default false
29964 * @cfg {Boolean} yearAllowBlank (true|false) default false
29965 * @cfg {string} dayPlaceholder
29966 * @cfg {string} monthPlaceholder
29967 * @cfg {string} yearPlaceholder
29968 * @cfg {string} dayFormat default 'd'
29969 * @cfg {string} monthFormat default 'm'
29970 * @cfg {string} yearFormat default 'Y'
29971 * @cfg {Number} labellg set the width of label (1-12)
29972 * @cfg {Number} labelmd set the width of label (1-12)
29973 * @cfg {Number} labelsm set the width of label (1-12)
29974 * @cfg {Number} labelxs set the width of label (1-12)
29978 * Create a new DateSplitField
29979 * @param {Object} config The config object
29982 Roo.bootstrap.DateSplitField = function(config){
29983 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29989 * getting the data of years
29990 * @param {Roo.bootstrap.DateSplitField} this
29991 * @param {Object} years
29996 * getting the data of days
29997 * @param {Roo.bootstrap.DateSplitField} this
29998 * @param {Object} days
30003 * Fires after the field has been marked as invalid.
30004 * @param {Roo.form.Field} this
30005 * @param {String} msg The validation message
30010 * Fires after the field has been validated with no errors.
30011 * @param {Roo.form.Field} this
30017 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30020 labelAlign : 'top',
30022 dayAllowBlank : false,
30023 monthAllowBlank : false,
30024 yearAllowBlank : false,
30025 dayPlaceholder : '',
30026 monthPlaceholder : '',
30027 yearPlaceholder : '',
30031 isFormField : true,
30037 getAutoCreate : function()
30041 cls : 'row roo-date-split-field-group',
30046 cls : 'form-hidden-field roo-date-split-field-group-value',
30052 var labelCls = 'col-md-12';
30053 var contentCls = 'col-md-4';
30055 if(this.fieldLabel){
30059 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30063 html : this.fieldLabel
30068 if(this.labelAlign == 'left'){
30070 if(this.labelWidth > 12){
30071 label.style = "width: " + this.labelWidth + 'px';
30074 if(this.labelWidth < 13 && this.labelmd == 0){
30075 this.labelmd = this.labelWidth;
30078 if(this.labellg > 0){
30079 labelCls = ' col-lg-' + this.labellg;
30080 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30083 if(this.labelmd > 0){
30084 labelCls = ' col-md-' + this.labelmd;
30085 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30088 if(this.labelsm > 0){
30089 labelCls = ' col-sm-' + this.labelsm;
30090 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30093 if(this.labelxs > 0){
30094 labelCls = ' col-xs-' + this.labelxs;
30095 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30099 label.cls += ' ' + labelCls;
30101 cfg.cn.push(label);
30104 Roo.each(['day', 'month', 'year'], function(t){
30107 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30114 inputEl: function ()
30116 return this.el.select('.roo-date-split-field-group-value', true).first();
30119 onRender : function(ct, position)
30123 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30125 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30127 this.dayField = new Roo.bootstrap.ComboBox({
30128 allowBlank : this.dayAllowBlank,
30129 alwaysQuery : true,
30130 displayField : 'value',
30133 forceSelection : true,
30135 placeholder : this.dayPlaceholder,
30136 selectOnFocus : true,
30137 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30138 triggerAction : 'all',
30140 valueField : 'value',
30141 store : new Roo.data.SimpleStore({
30142 data : (function() {
30144 _this.fireEvent('days', _this, days);
30147 fields : [ 'value' ]
30150 select : function (_self, record, index)
30152 _this.setValue(_this.getValue());
30157 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30159 this.monthField = new Roo.bootstrap.MonthField({
30160 after : '<i class=\"fa fa-calendar\"></i>',
30161 allowBlank : this.monthAllowBlank,
30162 placeholder : this.monthPlaceholder,
30165 render : function (_self)
30167 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30168 e.preventDefault();
30172 select : function (_self, oldvalue, newvalue)
30174 _this.setValue(_this.getValue());
30179 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30181 this.yearField = new Roo.bootstrap.ComboBox({
30182 allowBlank : this.yearAllowBlank,
30183 alwaysQuery : true,
30184 displayField : 'value',
30187 forceSelection : true,
30189 placeholder : this.yearPlaceholder,
30190 selectOnFocus : true,
30191 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30192 triggerAction : 'all',
30194 valueField : 'value',
30195 store : new Roo.data.SimpleStore({
30196 data : (function() {
30198 _this.fireEvent('years', _this, years);
30201 fields : [ 'value' ]
30204 select : function (_self, record, index)
30206 _this.setValue(_this.getValue());
30211 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30214 setValue : function(v, format)
30216 this.inputEl.dom.value = v;
30218 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30220 var d = Date.parseDate(v, f);
30227 this.setDay(d.format(this.dayFormat));
30228 this.setMonth(d.format(this.monthFormat));
30229 this.setYear(d.format(this.yearFormat));
30236 setDay : function(v)
30238 this.dayField.setValue(v);
30239 this.inputEl.dom.value = this.getValue();
30244 setMonth : function(v)
30246 this.monthField.setValue(v, true);
30247 this.inputEl.dom.value = this.getValue();
30252 setYear : function(v)
30254 this.yearField.setValue(v);
30255 this.inputEl.dom.value = this.getValue();
30260 getDay : function()
30262 return this.dayField.getValue();
30265 getMonth : function()
30267 return this.monthField.getValue();
30270 getYear : function()
30272 return this.yearField.getValue();
30275 getValue : function()
30277 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30279 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30289 this.inputEl.dom.value = '';
30294 validate : function()
30296 var d = this.dayField.validate();
30297 var m = this.monthField.validate();
30298 var y = this.yearField.validate();
30303 (!this.dayAllowBlank && !d) ||
30304 (!this.monthAllowBlank && !m) ||
30305 (!this.yearAllowBlank && !y)
30310 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30319 this.markInvalid();
30324 markValid : function()
30327 var label = this.el.select('label', true).first();
30328 var icon = this.el.select('i.fa-star', true).first();
30334 this.fireEvent('valid', this);
30338 * Mark this field as invalid
30339 * @param {String} msg The validation message
30341 markInvalid : function(msg)
30344 var label = this.el.select('label', true).first();
30345 var icon = this.el.select('i.fa-star', true).first();
30347 if(label && !icon){
30348 this.el.select('.roo-date-split-field-label', true).createChild({
30350 cls : 'text-danger fa fa-lg fa-star',
30351 tooltip : 'This field is required',
30352 style : 'margin-right:5px;'
30356 this.fireEvent('invalid', this, msg);
30359 clearInvalid : function()
30361 var label = this.el.select('label', true).first();
30362 var icon = this.el.select('i.fa-star', true).first();
30368 this.fireEvent('valid', this);
30371 getName: function()
30381 * http://masonry.desandro.com
30383 * The idea is to render all the bricks based on vertical width...
30385 * The original code extends 'outlayer' - we might need to use that....
30391 * @class Roo.bootstrap.LayoutMasonry
30392 * @extends Roo.bootstrap.Component
30393 * Bootstrap Layout Masonry class
30396 * Create a new Element
30397 * @param {Object} config The config object
30400 Roo.bootstrap.LayoutMasonry = function(config){
30402 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30406 Roo.bootstrap.LayoutMasonry.register(this);
30412 * Fire after layout the items
30413 * @param {Roo.bootstrap.LayoutMasonry} this
30414 * @param {Roo.EventObject} e
30421 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30424 * @cfg {Boolean} isLayoutInstant = no animation?
30426 isLayoutInstant : false, // needed?
30429 * @cfg {Number} boxWidth width of the columns
30434 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30439 * @cfg {Number} padWidth padding below box..
30444 * @cfg {Number} gutter gutter width..
30449 * @cfg {Number} maxCols maximum number of columns
30455 * @cfg {Boolean} isAutoInitial defalut true
30457 isAutoInitial : true,
30462 * @cfg {Boolean} isHorizontal defalut false
30464 isHorizontal : false,
30466 currentSize : null,
30472 bricks: null, //CompositeElement
30476 _isLayoutInited : false,
30478 // isAlternative : false, // only use for vertical layout...
30481 * @cfg {Number} alternativePadWidth padding below box..
30483 alternativePadWidth : 50,
30485 selectedBrick : [],
30487 getAutoCreate : function(){
30489 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30493 cls: 'blog-masonary-wrapper ' + this.cls,
30495 cls : 'mas-boxes masonary'
30502 getChildContainer: function( )
30504 if (this.boxesEl) {
30505 return this.boxesEl;
30508 this.boxesEl = this.el.select('.mas-boxes').first();
30510 return this.boxesEl;
30514 initEvents : function()
30518 if(this.isAutoInitial){
30519 Roo.log('hook children rendered');
30520 this.on('childrenrendered', function() {
30521 Roo.log('children rendered');
30527 initial : function()
30529 this.selectedBrick = [];
30531 this.currentSize = this.el.getBox(true);
30533 Roo.EventManager.onWindowResize(this.resize, this);
30535 if(!this.isAutoInitial){
30543 //this.layout.defer(500,this);
30547 resize : function()
30549 var cs = this.el.getBox(true);
30552 this.currentSize.width == cs.width &&
30553 this.currentSize.x == cs.x &&
30554 this.currentSize.height == cs.height &&
30555 this.currentSize.y == cs.y
30557 Roo.log("no change in with or X or Y");
30561 this.currentSize = cs;
30567 layout : function()
30569 this._resetLayout();
30571 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30573 this.layoutItems( isInstant );
30575 this._isLayoutInited = true;
30577 this.fireEvent('layout', this);
30581 _resetLayout : function()
30583 if(this.isHorizontal){
30584 this.horizontalMeasureColumns();
30588 this.verticalMeasureColumns();
30592 verticalMeasureColumns : function()
30594 this.getContainerWidth();
30596 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30597 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30601 var boxWidth = this.boxWidth + this.padWidth;
30603 if(this.containerWidth < this.boxWidth){
30604 boxWidth = this.containerWidth
30607 var containerWidth = this.containerWidth;
30609 var cols = Math.floor(containerWidth / boxWidth);
30611 this.cols = Math.max( cols, 1 );
30613 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30615 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30617 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30619 this.colWidth = boxWidth + avail - this.padWidth;
30621 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30622 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30625 horizontalMeasureColumns : function()
30627 this.getContainerWidth();
30629 var boxWidth = this.boxWidth;
30631 if(this.containerWidth < boxWidth){
30632 boxWidth = this.containerWidth;
30635 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30637 this.el.setHeight(boxWidth);
30641 getContainerWidth : function()
30643 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30646 layoutItems : function( isInstant )
30648 Roo.log(this.bricks);
30650 var items = Roo.apply([], this.bricks);
30652 if(this.isHorizontal){
30653 this._horizontalLayoutItems( items , isInstant );
30657 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30658 // this._verticalAlternativeLayoutItems( items , isInstant );
30662 this._verticalLayoutItems( items , isInstant );
30666 _verticalLayoutItems : function ( items , isInstant)
30668 if ( !items || !items.length ) {
30673 ['xs', 'xs', 'xs', 'tall'],
30674 ['xs', 'xs', 'tall'],
30675 ['xs', 'xs', 'sm'],
30676 ['xs', 'xs', 'xs'],
30682 ['sm', 'xs', 'xs'],
30686 ['tall', 'xs', 'xs', 'xs'],
30687 ['tall', 'xs', 'xs'],
30699 Roo.each(items, function(item, k){
30701 switch (item.size) {
30702 // these layouts take up a full box,
30713 boxes.push([item]);
30736 var filterPattern = function(box, length)
30744 var pattern = box.slice(0, length);
30748 Roo.each(pattern, function(i){
30749 format.push(i.size);
30752 Roo.each(standard, function(s){
30754 if(String(s) != String(format)){
30763 if(!match && length == 1){
30768 filterPattern(box, length - 1);
30772 queue.push(pattern);
30774 box = box.slice(length, box.length);
30776 filterPattern(box, 4);
30782 Roo.each(boxes, function(box, k){
30788 if(box.length == 1){
30793 filterPattern(box, 4);
30797 this._processVerticalLayoutQueue( queue, isInstant );
30801 // _verticalAlternativeLayoutItems : function( items , isInstant )
30803 // if ( !items || !items.length ) {
30807 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
30811 _horizontalLayoutItems : function ( items , isInstant)
30813 if ( !items || !items.length || items.length < 3) {
30819 var eItems = items.slice(0, 3);
30821 items = items.slice(3, items.length);
30824 ['xs', 'xs', 'xs', 'wide'],
30825 ['xs', 'xs', 'wide'],
30826 ['xs', 'xs', 'sm'],
30827 ['xs', 'xs', 'xs'],
30833 ['sm', 'xs', 'xs'],
30837 ['wide', 'xs', 'xs', 'xs'],
30838 ['wide', 'xs', 'xs'],
30851 Roo.each(items, function(item, k){
30853 switch (item.size) {
30864 boxes.push([item]);
30888 var filterPattern = function(box, length)
30896 var pattern = box.slice(0, length);
30900 Roo.each(pattern, function(i){
30901 format.push(i.size);
30904 Roo.each(standard, function(s){
30906 if(String(s) != String(format)){
30915 if(!match && length == 1){
30920 filterPattern(box, length - 1);
30924 queue.push(pattern);
30926 box = box.slice(length, box.length);
30928 filterPattern(box, 4);
30934 Roo.each(boxes, function(box, k){
30940 if(box.length == 1){
30945 filterPattern(box, 4);
30952 var pos = this.el.getBox(true);
30956 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30958 var hit_end = false;
30960 Roo.each(queue, function(box){
30964 Roo.each(box, function(b){
30966 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30976 Roo.each(box, function(b){
30978 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30981 mx = Math.max(mx, b.x);
30985 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30989 Roo.each(box, function(b){
30991 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31005 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31008 /** Sets position of item in DOM
31009 * @param {Element} item
31010 * @param {Number} x - horizontal position
31011 * @param {Number} y - vertical position
31012 * @param {Boolean} isInstant - disables transitions
31014 _processVerticalLayoutQueue : function( queue, isInstant )
31016 var pos = this.el.getBox(true);
31021 for (var i = 0; i < this.cols; i++){
31025 Roo.each(queue, function(box, k){
31027 var col = k % this.cols;
31029 Roo.each(box, function(b,kk){
31031 b.el.position('absolute');
31033 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31034 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31036 if(b.size == 'md-left' || b.size == 'md-right'){
31037 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31038 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31041 b.el.setWidth(width);
31042 b.el.setHeight(height);
31044 b.el.select('iframe',true).setSize(width,height);
31048 for (var i = 0; i < this.cols; i++){
31050 if(maxY[i] < maxY[col]){
31055 col = Math.min(col, i);
31059 x = pos.x + col * (this.colWidth + this.padWidth);
31063 var positions = [];
31065 switch (box.length){
31067 positions = this.getVerticalOneBoxColPositions(x, y, box);
31070 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31073 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31076 positions = this.getVerticalFourBoxColPositions(x, y, box);
31082 Roo.each(box, function(b,kk){
31084 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31086 var sz = b.el.getSize();
31088 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31096 for (var i = 0; i < this.cols; i++){
31097 mY = Math.max(mY, maxY[i]);
31100 this.el.setHeight(mY - pos.y);
31104 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31106 // var pos = this.el.getBox(true);
31109 // var maxX = pos.right;
31111 // var maxHeight = 0;
31113 // Roo.each(items, function(item, k){
31117 // item.el.position('absolute');
31119 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31121 // item.el.setWidth(width);
31123 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31125 // item.el.setHeight(height);
31128 // item.el.setXY([x, y], isInstant ? false : true);
31130 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31133 // y = y + height + this.alternativePadWidth;
31135 // maxHeight = maxHeight + height + this.alternativePadWidth;
31139 // this.el.setHeight(maxHeight);
31143 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31145 var pos = this.el.getBox(true);
31150 var maxX = pos.right;
31152 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31154 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31156 Roo.each(queue, function(box, k){
31158 Roo.each(box, function(b, kk){
31160 b.el.position('absolute');
31162 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31163 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31165 if(b.size == 'md-left' || b.size == 'md-right'){
31166 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31167 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31170 b.el.setWidth(width);
31171 b.el.setHeight(height);
31179 var positions = [];
31181 switch (box.length){
31183 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31186 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31189 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31192 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31198 Roo.each(box, function(b,kk){
31200 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31202 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31210 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31212 Roo.each(eItems, function(b,k){
31214 b.size = (k == 0) ? 'sm' : 'xs';
31215 b.x = (k == 0) ? 2 : 1;
31216 b.y = (k == 0) ? 2 : 1;
31218 b.el.position('absolute');
31220 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31222 b.el.setWidth(width);
31224 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31226 b.el.setHeight(height);
31230 var positions = [];
31233 x : maxX - this.unitWidth * 2 - this.gutter,
31238 x : maxX - this.unitWidth,
31239 y : minY + (this.unitWidth + this.gutter) * 2
31243 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31247 Roo.each(eItems, function(b,k){
31249 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31255 getVerticalOneBoxColPositions : function(x, y, box)
31259 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31261 if(box[0].size == 'md-left'){
31265 if(box[0].size == 'md-right'){
31270 x : x + (this.unitWidth + this.gutter) * rand,
31277 getVerticalTwoBoxColPositions : function(x, y, box)
31281 if(box[0].size == 'xs'){
31285 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31289 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31303 x : x + (this.unitWidth + this.gutter) * 2,
31304 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31311 getVerticalThreeBoxColPositions : function(x, y, box)
31315 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31323 x : x + (this.unitWidth + this.gutter) * 1,
31328 x : x + (this.unitWidth + this.gutter) * 2,
31336 if(box[0].size == 'xs' && box[1].size == 'xs'){
31345 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31349 x : x + (this.unitWidth + this.gutter) * 1,
31363 x : x + (this.unitWidth + this.gutter) * 2,
31368 x : x + (this.unitWidth + this.gutter) * 2,
31369 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31376 getVerticalFourBoxColPositions : function(x, y, box)
31380 if(box[0].size == 'xs'){
31389 y : y + (this.unitHeight + this.gutter) * 1
31394 y : y + (this.unitHeight + this.gutter) * 2
31398 x : x + (this.unitWidth + this.gutter) * 1,
31412 x : x + (this.unitWidth + this.gutter) * 2,
31417 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31418 y : y + (this.unitHeight + this.gutter) * 1
31422 x : x + (this.unitWidth + this.gutter) * 2,
31423 y : y + (this.unitWidth + this.gutter) * 2
31430 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31434 if(box[0].size == 'md-left'){
31436 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31443 if(box[0].size == 'md-right'){
31445 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31446 y : minY + (this.unitWidth + this.gutter) * 1
31452 var rand = Math.floor(Math.random() * (4 - box[0].y));
31455 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31456 y : minY + (this.unitWidth + this.gutter) * rand
31463 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31467 if(box[0].size == 'xs'){
31470 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31475 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31476 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31484 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31489 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31490 y : minY + (this.unitWidth + this.gutter) * 2
31497 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31501 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31504 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31509 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31510 y : minY + (this.unitWidth + this.gutter) * 1
31514 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31515 y : minY + (this.unitWidth + this.gutter) * 2
31522 if(box[0].size == 'xs' && box[1].size == 'xs'){
31525 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31530 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31535 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31536 y : minY + (this.unitWidth + this.gutter) * 1
31544 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31549 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31550 y : minY + (this.unitWidth + this.gutter) * 2
31554 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31555 y : minY + (this.unitWidth + this.gutter) * 2
31562 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31566 if(box[0].size == 'xs'){
31569 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31574 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31579 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),
31584 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31585 y : minY + (this.unitWidth + this.gutter) * 1
31593 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31598 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31599 y : minY + (this.unitWidth + this.gutter) * 2
31603 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31604 y : minY + (this.unitWidth + this.gutter) * 2
31608 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),
31609 y : minY + (this.unitWidth + this.gutter) * 2
31617 * remove a Masonry Brick
31618 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31620 removeBrick : function(brick_id)
31626 for (var i = 0; i<this.bricks.length; i++) {
31627 if (this.bricks[i].id == brick_id) {
31628 this.bricks.splice(i,1);
31629 this.el.dom.removeChild(Roo.get(brick_id).dom);
31636 * adds a Masonry Brick
31637 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31639 addBrick : function(cfg)
31641 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31642 //this.register(cn);
31643 cn.parentId = this.id;
31644 cn.onRender(this.el, null);
31649 * register a Masonry Brick
31650 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31653 register : function(brick)
31655 this.bricks.push(brick);
31656 brick.masonryId = this.id;
31660 * clear all the Masonry Brick
31662 clearAll : function()
31665 //this.getChildContainer().dom.innerHTML = "";
31666 this.el.dom.innerHTML = '';
31669 getSelected : function()
31671 if (!this.selectedBrick) {
31675 return this.selectedBrick;
31679 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31683 * register a Masonry Layout
31684 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31687 register : function(layout)
31689 this.groups[layout.id] = layout;
31692 * fetch a Masonry Layout based on the masonry layout ID
31693 * @param {string} the masonry layout to add
31694 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31697 get: function(layout_id) {
31698 if (typeof(this.groups[layout_id]) == 'undefined') {
31701 return this.groups[layout_id] ;
31713 * http://masonry.desandro.com
31715 * The idea is to render all the bricks based on vertical width...
31717 * The original code extends 'outlayer' - we might need to use that....
31723 * @class Roo.bootstrap.LayoutMasonryAuto
31724 * @extends Roo.bootstrap.Component
31725 * Bootstrap Layout Masonry class
31728 * Create a new Element
31729 * @param {Object} config The config object
31732 Roo.bootstrap.LayoutMasonryAuto = function(config){
31733 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31736 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31739 * @cfg {Boolean} isFitWidth - resize the width..
31741 isFitWidth : false, // options..
31743 * @cfg {Boolean} isOriginLeft = left align?
31745 isOriginLeft : true,
31747 * @cfg {Boolean} isOriginTop = top align?
31749 isOriginTop : false,
31751 * @cfg {Boolean} isLayoutInstant = no animation?
31753 isLayoutInstant : false, // needed?
31755 * @cfg {Boolean} isResizingContainer = not sure if this is used..
31757 isResizingContainer : true,
31759 * @cfg {Number} columnWidth width of the columns
31765 * @cfg {Number} maxCols maximum number of columns
31770 * @cfg {Number} padHeight padding below box..
31776 * @cfg {Boolean} isAutoInitial defalut true
31779 isAutoInitial : true,
31785 initialColumnWidth : 0,
31786 currentSize : null,
31788 colYs : null, // array.
31795 bricks: null, //CompositeElement
31796 cols : 0, // array?
31797 // element : null, // wrapped now this.el
31798 _isLayoutInited : null,
31801 getAutoCreate : function(){
31805 cls: 'blog-masonary-wrapper ' + this.cls,
31807 cls : 'mas-boxes masonary'
31814 getChildContainer: function( )
31816 if (this.boxesEl) {
31817 return this.boxesEl;
31820 this.boxesEl = this.el.select('.mas-boxes').first();
31822 return this.boxesEl;
31826 initEvents : function()
31830 if(this.isAutoInitial){
31831 Roo.log('hook children rendered');
31832 this.on('childrenrendered', function() {
31833 Roo.log('children rendered');
31840 initial : function()
31842 this.reloadItems();
31844 this.currentSize = this.el.getBox(true);
31846 /// was window resize... - let's see if this works..
31847 Roo.EventManager.onWindowResize(this.resize, this);
31849 if(!this.isAutoInitial){
31854 this.layout.defer(500,this);
31857 reloadItems: function()
31859 this.bricks = this.el.select('.masonry-brick', true);
31861 this.bricks.each(function(b) {
31862 //Roo.log(b.getSize());
31863 if (!b.attr('originalwidth')) {
31864 b.attr('originalwidth', b.getSize().width);
31869 Roo.log(this.bricks.elements.length);
31872 resize : function()
31875 var cs = this.el.getBox(true);
31877 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31878 Roo.log("no change in with or X");
31881 this.currentSize = cs;
31885 layout : function()
31888 this._resetLayout();
31889 //this._manageStamps();
31891 // don't animate first layout
31892 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31893 this.layoutItems( isInstant );
31895 // flag for initalized
31896 this._isLayoutInited = true;
31899 layoutItems : function( isInstant )
31901 //var items = this._getItemsForLayout( this.items );
31902 // original code supports filtering layout items.. we just ignore it..
31904 this._layoutItems( this.bricks , isInstant );
31906 this._postLayout();
31908 _layoutItems : function ( items , isInstant)
31910 //this.fireEvent( 'layout', this, items );
31913 if ( !items || !items.elements.length ) {
31914 // no items, emit event with empty array
31919 items.each(function(item) {
31920 Roo.log("layout item");
31922 // get x/y object from method
31923 var position = this._getItemLayoutPosition( item );
31925 position.item = item;
31926 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31927 queue.push( position );
31930 this._processLayoutQueue( queue );
31932 /** Sets position of item in DOM
31933 * @param {Element} item
31934 * @param {Number} x - horizontal position
31935 * @param {Number} y - vertical position
31936 * @param {Boolean} isInstant - disables transitions
31938 _processLayoutQueue : function( queue )
31940 for ( var i=0, len = queue.length; i < len; i++ ) {
31941 var obj = queue[i];
31942 obj.item.position('absolute');
31943 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31949 * Any logic you want to do after each layout,
31950 * i.e. size the container
31952 _postLayout : function()
31954 this.resizeContainer();
31957 resizeContainer : function()
31959 if ( !this.isResizingContainer ) {
31962 var size = this._getContainerSize();
31964 this.el.setSize(size.width,size.height);
31965 this.boxesEl.setSize(size.width,size.height);
31971 _resetLayout : function()
31973 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31974 this.colWidth = this.el.getWidth();
31975 //this.gutter = this.el.getWidth();
31977 this.measureColumns();
31983 this.colYs.push( 0 );
31989 measureColumns : function()
31991 this.getContainerWidth();
31992 // if columnWidth is 0, default to outerWidth of first item
31993 if ( !this.columnWidth ) {
31994 var firstItem = this.bricks.first();
31995 Roo.log(firstItem);
31996 this.columnWidth = this.containerWidth;
31997 if (firstItem && firstItem.attr('originalwidth') ) {
31998 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32000 // columnWidth fall back to item of first element
32001 Roo.log("set column width?");
32002 this.initialColumnWidth = this.columnWidth ;
32004 // if first elem has no width, default to size of container
32009 if (this.initialColumnWidth) {
32010 this.columnWidth = this.initialColumnWidth;
32015 // column width is fixed at the top - however if container width get's smaller we should
32018 // this bit calcs how man columns..
32020 var columnWidth = this.columnWidth += this.gutter;
32022 // calculate columns
32023 var containerWidth = this.containerWidth + this.gutter;
32025 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32026 // fix rounding errors, typically with gutters
32027 var excess = columnWidth - containerWidth % columnWidth;
32030 // if overshoot is less than a pixel, round up, otherwise floor it
32031 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32032 cols = Math[ mathMethod ]( cols );
32033 this.cols = Math.max( cols, 1 );
32034 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32036 // padding positioning..
32037 var totalColWidth = this.cols * this.columnWidth;
32038 var padavail = this.containerWidth - totalColWidth;
32039 // so for 2 columns - we need 3 'pads'
32041 var padNeeded = (1+this.cols) * this.padWidth;
32043 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32045 this.columnWidth += padExtra
32046 //this.padWidth = Math.floor(padavail / ( this.cols));
32048 // adjust colum width so that padding is fixed??
32050 // we have 3 columns ... total = width * 3
32051 // we have X left over... that should be used by
32053 //if (this.expandC) {
32061 getContainerWidth : function()
32063 /* // container is parent if fit width
32064 var container = this.isFitWidth ? this.element.parentNode : this.element;
32065 // check that this.size and size are there
32066 // IE8 triggers resize on body size change, so they might not be
32068 var size = getSize( container ); //FIXME
32069 this.containerWidth = size && size.innerWidth; //FIXME
32072 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32076 _getItemLayoutPosition : function( item ) // what is item?
32078 // we resize the item to our columnWidth..
32080 item.setWidth(this.columnWidth);
32081 item.autoBoxAdjust = false;
32083 var sz = item.getSize();
32085 // how many columns does this brick span
32086 var remainder = this.containerWidth % this.columnWidth;
32088 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32089 // round if off by 1 pixel, otherwise use ceil
32090 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32091 colSpan = Math.min( colSpan, this.cols );
32093 // normally this should be '1' as we dont' currently allow multi width columns..
32095 var colGroup = this._getColGroup( colSpan );
32096 // get the minimum Y value from the columns
32097 var minimumY = Math.min.apply( Math, colGroup );
32098 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32100 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32102 // position the brick
32104 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32105 y: this.currentSize.y + minimumY + this.padHeight
32109 // apply setHeight to necessary columns
32110 var setHeight = minimumY + sz.height + this.padHeight;
32111 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32113 var setSpan = this.cols + 1 - colGroup.length;
32114 for ( var i = 0; i < setSpan; i++ ) {
32115 this.colYs[ shortColIndex + i ] = setHeight ;
32122 * @param {Number} colSpan - number of columns the element spans
32123 * @returns {Array} colGroup
32125 _getColGroup : function( colSpan )
32127 if ( colSpan < 2 ) {
32128 // if brick spans only one column, use all the column Ys
32133 // how many different places could this brick fit horizontally
32134 var groupCount = this.cols + 1 - colSpan;
32135 // for each group potential horizontal position
32136 for ( var i = 0; i < groupCount; i++ ) {
32137 // make an array of colY values for that one group
32138 var groupColYs = this.colYs.slice( i, i + colSpan );
32139 // and get the max value of the array
32140 colGroup[i] = Math.max.apply( Math, groupColYs );
32145 _manageStamp : function( stamp )
32147 var stampSize = stamp.getSize();
32148 var offset = stamp.getBox();
32149 // get the columns that this stamp affects
32150 var firstX = this.isOriginLeft ? offset.x : offset.right;
32151 var lastX = firstX + stampSize.width;
32152 var firstCol = Math.floor( firstX / this.columnWidth );
32153 firstCol = Math.max( 0, firstCol );
32155 var lastCol = Math.floor( lastX / this.columnWidth );
32156 // lastCol should not go over if multiple of columnWidth #425
32157 lastCol -= lastX % this.columnWidth ? 0 : 1;
32158 lastCol = Math.min( this.cols - 1, lastCol );
32160 // set colYs to bottom of the stamp
32161 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32164 for ( var i = firstCol; i <= lastCol; i++ ) {
32165 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32170 _getContainerSize : function()
32172 this.maxY = Math.max.apply( Math, this.colYs );
32177 if ( this.isFitWidth ) {
32178 size.width = this._getContainerFitWidth();
32184 _getContainerFitWidth : function()
32186 var unusedCols = 0;
32187 // count unused columns
32190 if ( this.colYs[i] !== 0 ) {
32195 // fit container to columns that have been used
32196 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32199 needsResizeLayout : function()
32201 var previousWidth = this.containerWidth;
32202 this.getContainerWidth();
32203 return previousWidth !== this.containerWidth;
32218 * @class Roo.bootstrap.MasonryBrick
32219 * @extends Roo.bootstrap.Component
32220 * Bootstrap MasonryBrick class
32223 * Create a new MasonryBrick
32224 * @param {Object} config The config object
32227 Roo.bootstrap.MasonryBrick = function(config){
32229 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32231 Roo.bootstrap.MasonryBrick.register(this);
32237 * When a MasonryBrick is clcik
32238 * @param {Roo.bootstrap.MasonryBrick} this
32239 * @param {Roo.EventObject} e
32245 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32248 * @cfg {String} title
32252 * @cfg {String} html
32256 * @cfg {String} bgimage
32260 * @cfg {String} videourl
32264 * @cfg {String} cls
32268 * @cfg {String} href
32272 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32277 * @cfg {String} placetitle (center|bottom)
32282 * @cfg {Boolean} isFitContainer defalut true
32284 isFitContainer : true,
32287 * @cfg {Boolean} preventDefault defalut false
32289 preventDefault : false,
32292 * @cfg {Boolean} inverse defalut false
32294 maskInverse : false,
32296 getAutoCreate : function()
32298 if(!this.isFitContainer){
32299 return this.getSplitAutoCreate();
32302 var cls = 'masonry-brick masonry-brick-full';
32304 if(this.href.length){
32305 cls += ' masonry-brick-link';
32308 if(this.bgimage.length){
32309 cls += ' masonry-brick-image';
32312 if(this.maskInverse){
32313 cls += ' mask-inverse';
32316 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32317 cls += ' enable-mask';
32321 cls += ' masonry-' + this.size + '-brick';
32324 if(this.placetitle.length){
32326 switch (this.placetitle) {
32328 cls += ' masonry-center-title';
32331 cls += ' masonry-bottom-title';
32338 if(!this.html.length && !this.bgimage.length){
32339 cls += ' masonry-center-title';
32342 if(!this.html.length && this.bgimage.length){
32343 cls += ' masonry-bottom-title';
32348 cls += ' ' + this.cls;
32352 tag: (this.href.length) ? 'a' : 'div',
32357 cls: 'masonry-brick-mask'
32361 cls: 'masonry-brick-paragraph',
32367 if(this.href.length){
32368 cfg.href = this.href;
32371 var cn = cfg.cn[1].cn;
32373 if(this.title.length){
32376 cls: 'masonry-brick-title',
32381 if(this.html.length){
32384 cls: 'masonry-brick-text',
32389 if (!this.title.length && !this.html.length) {
32390 cfg.cn[1].cls += ' hide';
32393 if(this.bgimage.length){
32396 cls: 'masonry-brick-image-view',
32401 if(this.videourl.length){
32402 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32403 // youtube support only?
32406 cls: 'masonry-brick-image-view',
32409 allowfullscreen : true
32417 getSplitAutoCreate : function()
32419 var cls = 'masonry-brick masonry-brick-split';
32421 if(this.href.length){
32422 cls += ' masonry-brick-link';
32425 if(this.bgimage.length){
32426 cls += ' masonry-brick-image';
32430 cls += ' masonry-' + this.size + '-brick';
32433 switch (this.placetitle) {
32435 cls += ' masonry-center-title';
32438 cls += ' masonry-bottom-title';
32441 if(!this.bgimage.length){
32442 cls += ' masonry-center-title';
32445 if(this.bgimage.length){
32446 cls += ' masonry-bottom-title';
32452 cls += ' ' + this.cls;
32456 tag: (this.href.length) ? 'a' : 'div',
32461 cls: 'masonry-brick-split-head',
32465 cls: 'masonry-brick-paragraph',
32472 cls: 'masonry-brick-split-body',
32478 if(this.href.length){
32479 cfg.href = this.href;
32482 if(this.title.length){
32483 cfg.cn[0].cn[0].cn.push({
32485 cls: 'masonry-brick-title',
32490 if(this.html.length){
32491 cfg.cn[1].cn.push({
32493 cls: 'masonry-brick-text',
32498 if(this.bgimage.length){
32499 cfg.cn[0].cn.push({
32501 cls: 'masonry-brick-image-view',
32506 if(this.videourl.length){
32507 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32508 // youtube support only?
32509 cfg.cn[0].cn.cn.push({
32511 cls: 'masonry-brick-image-view',
32514 allowfullscreen : true
32521 initEvents: function()
32523 switch (this.size) {
32556 this.el.on('touchstart', this.onTouchStart, this);
32557 this.el.on('touchmove', this.onTouchMove, this);
32558 this.el.on('touchend', this.onTouchEnd, this);
32559 this.el.on('contextmenu', this.onContextMenu, this);
32561 this.el.on('mouseenter' ,this.enter, this);
32562 this.el.on('mouseleave', this.leave, this);
32563 this.el.on('click', this.onClick, this);
32566 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32567 this.parent().bricks.push(this);
32572 onClick: function(e, el)
32574 var time = this.endTimer - this.startTimer;
32575 // Roo.log(e.preventDefault());
32578 e.preventDefault();
32583 if(!this.preventDefault){
32587 e.preventDefault();
32589 if (this.activcClass != '') {
32590 this.selectBrick();
32593 this.fireEvent('click', this);
32596 enter: function(e, el)
32598 e.preventDefault();
32600 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32604 if(this.bgimage.length && this.html.length){
32605 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32609 leave: function(e, el)
32611 e.preventDefault();
32613 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32617 if(this.bgimage.length && this.html.length){
32618 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32622 onTouchStart: function(e, el)
32624 // e.preventDefault();
32626 this.touchmoved = false;
32628 if(!this.isFitContainer){
32632 if(!this.bgimage.length || !this.html.length){
32636 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32638 this.timer = new Date().getTime();
32642 onTouchMove: function(e, el)
32644 this.touchmoved = true;
32647 onContextMenu : function(e,el)
32649 e.preventDefault();
32650 e.stopPropagation();
32654 onTouchEnd: function(e, el)
32656 // e.preventDefault();
32658 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32665 if(!this.bgimage.length || !this.html.length){
32667 if(this.href.length){
32668 window.location.href = this.href;
32674 if(!this.isFitContainer){
32678 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32680 window.location.href = this.href;
32683 //selection on single brick only
32684 selectBrick : function() {
32686 if (!this.parentId) {
32690 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32691 var index = m.selectedBrick.indexOf(this.id);
32694 m.selectedBrick.splice(index,1);
32695 this.el.removeClass(this.activeClass);
32699 for(var i = 0; i < m.selectedBrick.length; i++) {
32700 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32701 b.el.removeClass(b.activeClass);
32704 m.selectedBrick = [];
32706 m.selectedBrick.push(this.id);
32707 this.el.addClass(this.activeClass);
32713 Roo.apply(Roo.bootstrap.MasonryBrick, {
32716 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32718 * register a Masonry Brick
32719 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32722 register : function(brick)
32724 //this.groups[brick.id] = brick;
32725 this.groups.add(brick.id, brick);
32728 * fetch a masonry brick based on the masonry brick ID
32729 * @param {string} the masonry brick to add
32730 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32733 get: function(brick_id)
32735 // if (typeof(this.groups[brick_id]) == 'undefined') {
32738 // return this.groups[brick_id] ;
32740 if(this.groups.key(brick_id)) {
32741 return this.groups.key(brick_id);
32759 * @class Roo.bootstrap.Brick
32760 * @extends Roo.bootstrap.Component
32761 * Bootstrap Brick class
32764 * Create a new Brick
32765 * @param {Object} config The config object
32768 Roo.bootstrap.Brick = function(config){
32769 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32775 * When a Brick is click
32776 * @param {Roo.bootstrap.Brick} this
32777 * @param {Roo.EventObject} e
32783 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
32786 * @cfg {String} title
32790 * @cfg {String} html
32794 * @cfg {String} bgimage
32798 * @cfg {String} cls
32802 * @cfg {String} href
32806 * @cfg {String} video
32810 * @cfg {Boolean} square
32814 getAutoCreate : function()
32816 var cls = 'roo-brick';
32818 if(this.href.length){
32819 cls += ' roo-brick-link';
32822 if(this.bgimage.length){
32823 cls += ' roo-brick-image';
32826 if(!this.html.length && !this.bgimage.length){
32827 cls += ' roo-brick-center-title';
32830 if(!this.html.length && this.bgimage.length){
32831 cls += ' roo-brick-bottom-title';
32835 cls += ' ' + this.cls;
32839 tag: (this.href.length) ? 'a' : 'div',
32844 cls: 'roo-brick-paragraph',
32850 if(this.href.length){
32851 cfg.href = this.href;
32854 var cn = cfg.cn[0].cn;
32856 if(this.title.length){
32859 cls: 'roo-brick-title',
32864 if(this.html.length){
32867 cls: 'roo-brick-text',
32874 if(this.bgimage.length){
32877 cls: 'roo-brick-image-view',
32885 initEvents: function()
32887 if(this.title.length || this.html.length){
32888 this.el.on('mouseenter' ,this.enter, this);
32889 this.el.on('mouseleave', this.leave, this);
32892 Roo.EventManager.onWindowResize(this.resize, this);
32894 if(this.bgimage.length){
32895 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32896 this.imageEl.on('load', this.onImageLoad, this);
32903 onImageLoad : function()
32908 resize : function()
32910 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32912 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32914 if(this.bgimage.length){
32915 var image = this.el.select('.roo-brick-image-view', true).first();
32917 image.setWidth(paragraph.getWidth());
32920 image.setHeight(paragraph.getWidth());
32923 this.el.setHeight(image.getHeight());
32924 paragraph.setHeight(image.getHeight());
32930 enter: function(e, el)
32932 e.preventDefault();
32934 if(this.bgimage.length){
32935 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32936 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32940 leave: function(e, el)
32942 e.preventDefault();
32944 if(this.bgimage.length){
32945 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32946 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32962 * @class Roo.bootstrap.NumberField
32963 * @extends Roo.bootstrap.Input
32964 * Bootstrap NumberField class
32970 * Create a new NumberField
32971 * @param {Object} config The config object
32974 Roo.bootstrap.NumberField = function(config){
32975 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32978 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32981 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32983 allowDecimals : true,
32985 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32987 decimalSeparator : ".",
32989 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32991 decimalPrecision : 2,
32993 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32995 allowNegative : true,
32997 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32999 minValue : Number.NEGATIVE_INFINITY,
33001 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33003 maxValue : Number.MAX_VALUE,
33005 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33007 minText : "The minimum value for this field is {0}",
33009 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33011 maxText : "The maximum value for this field is {0}",
33013 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33014 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33016 nanText : "{0} is not a valid number",
33018 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33023 initEvents : function()
33025 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33027 var allowed = "0123456789";
33029 if(this.allowDecimals){
33030 allowed += this.decimalSeparator;
33033 if(this.allowNegative){
33037 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33039 var keyPress = function(e){
33041 var k = e.getKey();
33043 var c = e.getCharCode();
33046 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33047 allowed.indexOf(String.fromCharCode(c)) === -1
33053 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33057 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33062 this.el.on("keypress", keyPress, this);
33065 validateValue : function(value)
33068 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33072 var num = this.parseValue(value);
33075 this.markInvalid(String.format(this.nanText, value));
33079 if(num < this.minValue){
33080 this.markInvalid(String.format(this.minText, this.minValue));
33084 if(num > this.maxValue){
33085 this.markInvalid(String.format(this.maxText, this.maxValue));
33092 getValue : function()
33094 return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
33097 parseValue : function(value)
33099 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33100 return isNaN(value) ? '' : value;
33103 fixPrecision : function(value)
33105 var nan = isNaN(value);
33107 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33108 return nan ? '' : value;
33110 return parseFloat(value).toFixed(this.decimalPrecision);
33113 setValue : function(v)
33115 v = this.fixPrecision(v);
33116 Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
33119 decimalPrecisionFcn : function(v)
33121 return Math.floor(v);
33124 beforeBlur : function()
33130 var v = this.parseValue(this.getRawValue());
33145 * @class Roo.bootstrap.DocumentSlider
33146 * @extends Roo.bootstrap.Component
33147 * Bootstrap DocumentSlider class
33150 * Create a new DocumentViewer
33151 * @param {Object} config The config object
33154 Roo.bootstrap.DocumentSlider = function(config){
33155 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33162 * Fire after initEvent
33163 * @param {Roo.bootstrap.DocumentSlider} this
33168 * Fire after update
33169 * @param {Roo.bootstrap.DocumentSlider} this
33175 * @param {Roo.bootstrap.DocumentSlider} this
33181 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33187 getAutoCreate : function()
33191 cls : 'roo-document-slider',
33195 cls : 'roo-document-slider-header',
33199 cls : 'roo-document-slider-header-title'
33205 cls : 'roo-document-slider-body',
33209 cls : 'roo-document-slider-prev',
33213 cls : 'fa fa-chevron-left'
33219 cls : 'roo-document-slider-thumb',
33223 cls : 'roo-document-slider-image'
33229 cls : 'roo-document-slider-next',
33233 cls : 'fa fa-chevron-right'
33245 initEvents : function()
33247 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33248 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33250 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33251 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33253 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33254 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33256 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33257 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33259 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33260 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33262 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33263 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33265 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33266 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33268 this.thumbEl.on('click', this.onClick, this);
33270 this.prevIndicator.on('click', this.prev, this);
33272 this.nextIndicator.on('click', this.next, this);
33276 initial : function()
33278 if(this.files.length){
33279 this.indicator = 1;
33283 this.fireEvent('initial', this);
33286 update : function()
33288 this.imageEl.attr('src', this.files[this.indicator - 1]);
33290 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33292 this.prevIndicator.show();
33294 if(this.indicator == 1){
33295 this.prevIndicator.hide();
33298 this.nextIndicator.show();
33300 if(this.indicator == this.files.length){
33301 this.nextIndicator.hide();
33304 this.thumbEl.scrollTo('top');
33306 this.fireEvent('update', this);
33309 onClick : function(e)
33311 e.preventDefault();
33313 this.fireEvent('click', this);
33318 e.preventDefault();
33320 this.indicator = Math.max(1, this.indicator - 1);
33327 e.preventDefault();
33329 this.indicator = Math.min(this.files.length, this.indicator + 1);
33343 * @class Roo.bootstrap.RadioSet
33344 * @extends Roo.bootstrap.Input
33345 * Bootstrap RadioSet class
33346 * @cfg {String} indicatorpos (left|right) default left
33347 * @cfg {Boolean} inline (true|false) inline the element (default true)
33348 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33350 * Create a new RadioSet
33351 * @param {Object} config The config object
33354 Roo.bootstrap.RadioSet = function(config){
33356 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33360 Roo.bootstrap.RadioSet.register(this);
33365 * Fires when the element is checked or unchecked.
33366 * @param {Roo.bootstrap.RadioSet} this This radio
33367 * @param {Roo.bootstrap.Radio} item The checked item
33374 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33382 indicatorpos : 'left',
33384 getAutoCreate : function()
33388 cls : 'roo-radio-set-label',
33392 html : this.fieldLabel
33397 if(this.indicatorpos == 'left'){
33400 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33401 tooltip : 'This field is required'
33406 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33407 tooltip : 'This field is required'
33413 cls : 'roo-radio-set-items'
33416 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33418 if (align === 'left' && this.fieldLabel.length) {
33421 cls : "roo-radio-set-right",
33427 if(this.labelWidth > 12){
33428 label.style = "width: " + this.labelWidth + 'px';
33431 if(this.labelWidth < 13 && this.labelmd == 0){
33432 this.labelmd = this.labelWidth;
33435 if(this.labellg > 0){
33436 label.cls += ' col-lg-' + this.labellg;
33437 items.cls += ' col-lg-' + (12 - this.labellg);
33440 if(this.labelmd > 0){
33441 label.cls += ' col-md-' + this.labelmd;
33442 items.cls += ' col-md-' + (12 - this.labelmd);
33445 if(this.labelsm > 0){
33446 label.cls += ' col-sm-' + this.labelsm;
33447 items.cls += ' col-sm-' + (12 - this.labelsm);
33450 if(this.labelxs > 0){
33451 label.cls += ' col-xs-' + this.labelxs;
33452 items.cls += ' col-xs-' + (12 - this.labelxs);
33458 cls : 'roo-radio-set',
33462 cls : 'roo-radio-set-input',
33465 value : this.value ? this.value : ''
33472 if(this.weight.length){
33473 cfg.cls += ' roo-radio-' + this.weight;
33477 cfg.cls += ' roo-radio-set-inline';
33481 ['xs','sm','md','lg'].map(function(size){
33482 if (settings[size]) {
33483 cfg.cls += ' col-' + size + '-' + settings[size];
33491 initEvents : function()
33493 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33494 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33496 if(!this.fieldLabel.length){
33497 this.labelEl.hide();
33500 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33501 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33503 this.indicatorEl().addClass('invisible');
33505 this.originalValue = this.getValue();
33509 inputEl: function ()
33511 return this.el.select('.roo-radio-set-input', true).first();
33514 getChildContainer : function()
33516 return this.itemsEl;
33519 register : function(item)
33521 this.radioes.push(item);
33525 validate : function()
33529 Roo.each(this.radioes, function(i){
33538 if(this.allowBlank) {
33542 if(this.disabled || valid){
33547 this.markInvalid();
33552 markValid : function()
33554 if(this.labelEl.isVisible(true)){
33555 this.indicatorEl().removeClass('visible');
33556 this.indicatorEl().addClass('invisible');
33559 this.el.removeClass([this.invalidClass, this.validClass]);
33560 this.el.addClass(this.validClass);
33562 this.fireEvent('valid', this);
33565 markInvalid : function(msg)
33567 if(this.allowBlank || this.disabled){
33571 if(this.labelEl.isVisible(true)){
33572 this.indicatorEl().removeClass('invisible');
33573 this.indicatorEl().addClass('visible');
33576 this.el.removeClass([this.invalidClass, this.validClass]);
33577 this.el.addClass(this.invalidClass);
33579 this.fireEvent('invalid', this, msg);
33583 setValue : function(v, suppressEvent)
33585 if(this.value === v){
33592 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33595 Roo.each(this.radioes, function(i){
33598 i.el.removeClass('checked');
33600 if(i.value === v || i.value.toString() === v.toString()){
33602 i.el.addClass('checked');
33604 if(suppressEvent !== true){
33605 this.fireEvent('check', this, i);
33614 clearInvalid : function(){
33616 if(!this.el || this.preventMark){
33620 this.el.removeClass([this.invalidClass]);
33622 this.fireEvent('valid', this);
33627 Roo.apply(Roo.bootstrap.RadioSet, {
33631 register : function(set)
33633 this.groups[set.name] = set;
33636 get: function(name)
33638 if (typeof(this.groups[name]) == 'undefined') {
33642 return this.groups[name] ;
33648 * Ext JS Library 1.1.1
33649 * Copyright(c) 2006-2007, Ext JS, LLC.
33651 * Originally Released Under LGPL - original licence link has changed is not relivant.
33654 * <script type="text/javascript">
33659 * @class Roo.bootstrap.SplitBar
33660 * @extends Roo.util.Observable
33661 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33665 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33666 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33667 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33668 split.minSize = 100;
33669 split.maxSize = 600;
33670 split.animate = true;
33671 split.on('moved', splitterMoved);
33674 * Create a new SplitBar
33675 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
33676 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
33677 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33678 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
33679 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33680 position of the SplitBar).
33682 Roo.bootstrap.SplitBar = function(cfg){
33687 // dragElement : elm
33688 // resizingElement: el,
33690 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33691 // placement : Roo.bootstrap.SplitBar.LEFT ,
33692 // existingProxy ???
33695 this.el = Roo.get(cfg.dragElement, true);
33696 this.el.dom.unselectable = "on";
33698 this.resizingEl = Roo.get(cfg.resizingElement, true);
33702 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33703 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33706 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33709 * The minimum size of the resizing element. (Defaults to 0)
33715 * The maximum size of the resizing element. (Defaults to 2000)
33718 this.maxSize = 2000;
33721 * Whether to animate the transition to the new size
33724 this.animate = false;
33727 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33730 this.useShim = false;
33735 if(!cfg.existingProxy){
33737 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33739 this.proxy = Roo.get(cfg.existingProxy).dom;
33742 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33745 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33748 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33751 this.dragSpecs = {};
33754 * @private The adapter to use to positon and resize elements
33756 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33757 this.adapter.init(this);
33759 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33761 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33762 this.el.addClass("roo-splitbar-h");
33765 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33766 this.el.addClass("roo-splitbar-v");
33772 * Fires when the splitter is moved (alias for {@link #event-moved})
33773 * @param {Roo.bootstrap.SplitBar} this
33774 * @param {Number} newSize the new width or height
33779 * Fires when the splitter is moved
33780 * @param {Roo.bootstrap.SplitBar} this
33781 * @param {Number} newSize the new width or height
33785 * @event beforeresize
33786 * Fires before the splitter is dragged
33787 * @param {Roo.bootstrap.SplitBar} this
33789 "beforeresize" : true,
33791 "beforeapply" : true
33794 Roo.util.Observable.call(this);
33797 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33798 onStartProxyDrag : function(x, y){
33799 this.fireEvent("beforeresize", this);
33801 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
33803 o.enableDisplayMode("block");
33804 // all splitbars share the same overlay
33805 Roo.bootstrap.SplitBar.prototype.overlay = o;
33807 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33808 this.overlay.show();
33809 Roo.get(this.proxy).setDisplayed("block");
33810 var size = this.adapter.getElementSize(this);
33811 this.activeMinSize = this.getMinimumSize();;
33812 this.activeMaxSize = this.getMaximumSize();;
33813 var c1 = size - this.activeMinSize;
33814 var c2 = Math.max(this.activeMaxSize - size, 0);
33815 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33816 this.dd.resetConstraints();
33817 this.dd.setXConstraint(
33818 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
33819 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33821 this.dd.setYConstraint(0, 0);
33823 this.dd.resetConstraints();
33824 this.dd.setXConstraint(0, 0);
33825 this.dd.setYConstraint(
33826 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
33827 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33830 this.dragSpecs.startSize = size;
33831 this.dragSpecs.startPoint = [x, y];
33832 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33836 * @private Called after the drag operation by the DDProxy
33838 onEndProxyDrag : function(e){
33839 Roo.get(this.proxy).setDisplayed(false);
33840 var endPoint = Roo.lib.Event.getXY(e);
33842 this.overlay.hide();
33845 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33846 newSize = this.dragSpecs.startSize +
33847 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33848 endPoint[0] - this.dragSpecs.startPoint[0] :
33849 this.dragSpecs.startPoint[0] - endPoint[0]
33852 newSize = this.dragSpecs.startSize +
33853 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33854 endPoint[1] - this.dragSpecs.startPoint[1] :
33855 this.dragSpecs.startPoint[1] - endPoint[1]
33858 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33859 if(newSize != this.dragSpecs.startSize){
33860 if(this.fireEvent('beforeapply', this, newSize) !== false){
33861 this.adapter.setElementSize(this, newSize);
33862 this.fireEvent("moved", this, newSize);
33863 this.fireEvent("resize", this, newSize);
33869 * Get the adapter this SplitBar uses
33870 * @return The adapter object
33872 getAdapter : function(){
33873 return this.adapter;
33877 * Set the adapter this SplitBar uses
33878 * @param {Object} adapter A SplitBar adapter object
33880 setAdapter : function(adapter){
33881 this.adapter = adapter;
33882 this.adapter.init(this);
33886 * Gets the minimum size for the resizing element
33887 * @return {Number} The minimum size
33889 getMinimumSize : function(){
33890 return this.minSize;
33894 * Sets the minimum size for the resizing element
33895 * @param {Number} minSize The minimum size
33897 setMinimumSize : function(minSize){
33898 this.minSize = minSize;
33902 * Gets the maximum size for the resizing element
33903 * @return {Number} The maximum size
33905 getMaximumSize : function(){
33906 return this.maxSize;
33910 * Sets the maximum size for the resizing element
33911 * @param {Number} maxSize The maximum size
33913 setMaximumSize : function(maxSize){
33914 this.maxSize = maxSize;
33918 * Sets the initialize size for the resizing element
33919 * @param {Number} size The initial size
33921 setCurrentSize : function(size){
33922 var oldAnimate = this.animate;
33923 this.animate = false;
33924 this.adapter.setElementSize(this, size);
33925 this.animate = oldAnimate;
33929 * Destroy this splitbar.
33930 * @param {Boolean} removeEl True to remove the element
33932 destroy : function(removeEl){
33934 this.shim.remove();
33937 this.proxy.parentNode.removeChild(this.proxy);
33945 * @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.
33947 Roo.bootstrap.SplitBar.createProxy = function(dir){
33948 var proxy = new Roo.Element(document.createElement("div"));
33949 proxy.unselectable();
33950 var cls = 'roo-splitbar-proxy';
33951 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33952 document.body.appendChild(proxy.dom);
33957 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33958 * Default Adapter. It assumes the splitter and resizing element are not positioned
33959 * elements and only gets/sets the width of the element. Generally used for table based layouts.
33961 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33964 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33965 // do nothing for now
33966 init : function(s){
33970 * Called before drag operations to get the current size of the resizing element.
33971 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33973 getElementSize : function(s){
33974 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33975 return s.resizingEl.getWidth();
33977 return s.resizingEl.getHeight();
33982 * Called after drag operations to set the size of the resizing element.
33983 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33984 * @param {Number} newSize The new size to set
33985 * @param {Function} onComplete A function to be invoked when resizing is complete
33987 setElementSize : function(s, newSize, onComplete){
33988 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33990 s.resizingEl.setWidth(newSize);
33992 onComplete(s, newSize);
33995 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34000 s.resizingEl.setHeight(newSize);
34002 onComplete(s, newSize);
34005 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34012 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34013 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34014 * Adapter that moves the splitter element to align with the resized sizing element.
34015 * Used with an absolute positioned SplitBar.
34016 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34017 * document.body, make sure you assign an id to the body element.
34019 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34020 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34021 this.container = Roo.get(container);
34024 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34025 init : function(s){
34026 this.basic.init(s);
34029 getElementSize : function(s){
34030 return this.basic.getElementSize(s);
34033 setElementSize : function(s, newSize, onComplete){
34034 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34037 moveSplitter : function(s){
34038 var yes = Roo.bootstrap.SplitBar;
34039 switch(s.placement){
34041 s.el.setX(s.resizingEl.getRight());
34044 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34047 s.el.setY(s.resizingEl.getBottom());
34050 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34057 * Orientation constant - Create a vertical SplitBar
34061 Roo.bootstrap.SplitBar.VERTICAL = 1;
34064 * Orientation constant - Create a horizontal SplitBar
34068 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34071 * Placement constant - The resizing element is to the left of the splitter element
34075 Roo.bootstrap.SplitBar.LEFT = 1;
34078 * Placement constant - The resizing element is to the right of the splitter element
34082 Roo.bootstrap.SplitBar.RIGHT = 2;
34085 * Placement constant - The resizing element is positioned above the splitter element
34089 Roo.bootstrap.SplitBar.TOP = 3;
34092 * Placement constant - The resizing element is positioned under splitter element
34096 Roo.bootstrap.SplitBar.BOTTOM = 4;
34097 Roo.namespace("Roo.bootstrap.layout");/*
34099 * Ext JS Library 1.1.1
34100 * Copyright(c) 2006-2007, Ext JS, LLC.
34102 * Originally Released Under LGPL - original licence link has changed is not relivant.
34105 * <script type="text/javascript">
34109 * @class Roo.bootstrap.layout.Manager
34110 * @extends Roo.bootstrap.Component
34111 * Base class for layout managers.
34113 Roo.bootstrap.layout.Manager = function(config)
34115 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34121 /** false to disable window resize monitoring @type Boolean */
34122 this.monitorWindowResize = true;
34127 * Fires when a layout is performed.
34128 * @param {Roo.LayoutManager} this
34132 * @event regionresized
34133 * Fires when the user resizes a region.
34134 * @param {Roo.LayoutRegion} region The resized region
34135 * @param {Number} newSize The new size (width for east/west, height for north/south)
34137 "regionresized" : true,
34139 * @event regioncollapsed
34140 * Fires when a region is collapsed.
34141 * @param {Roo.LayoutRegion} region The collapsed region
34143 "regioncollapsed" : true,
34145 * @event regionexpanded
34146 * Fires when a region is expanded.
34147 * @param {Roo.LayoutRegion} region The expanded region
34149 "regionexpanded" : true
34151 this.updating = false;
34154 this.el = Roo.get(config.el);
34160 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34165 monitorWindowResize : true,
34171 onRender : function(ct, position)
34174 this.el = Roo.get(ct);
34177 //this.fireEvent('render',this);
34181 initEvents: function()
34185 // ie scrollbar fix
34186 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34187 document.body.scroll = "no";
34188 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34189 this.el.position('relative');
34191 this.id = this.el.id;
34192 this.el.addClass("roo-layout-container");
34193 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34194 if(this.el.dom != document.body ) {
34195 this.el.on('resize', this.layout,this);
34196 this.el.on('show', this.layout,this);
34202 * Returns true if this layout is currently being updated
34203 * @return {Boolean}
34205 isUpdating : function(){
34206 return this.updating;
34210 * Suspend the LayoutManager from doing auto-layouts while
34211 * making multiple add or remove calls
34213 beginUpdate : function(){
34214 this.updating = true;
34218 * Restore auto-layouts and optionally disable the manager from performing a layout
34219 * @param {Boolean} noLayout true to disable a layout update
34221 endUpdate : function(noLayout){
34222 this.updating = false;
34228 layout: function(){
34232 onRegionResized : function(region, newSize){
34233 this.fireEvent("regionresized", region, newSize);
34237 onRegionCollapsed : function(region){
34238 this.fireEvent("regioncollapsed", region);
34241 onRegionExpanded : function(region){
34242 this.fireEvent("regionexpanded", region);
34246 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34247 * performs box-model adjustments.
34248 * @return {Object} The size as an object {width: (the width), height: (the height)}
34250 getViewSize : function()
34253 if(this.el.dom != document.body){
34254 size = this.el.getSize();
34256 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34258 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34259 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34264 * Returns the Element this layout is bound to.
34265 * @return {Roo.Element}
34267 getEl : function(){
34272 * Returns the specified region.
34273 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34274 * @return {Roo.LayoutRegion}
34276 getRegion : function(target){
34277 return this.regions[target.toLowerCase()];
34280 onWindowResize : function(){
34281 if(this.monitorWindowResize){
34288 * Ext JS Library 1.1.1
34289 * Copyright(c) 2006-2007, Ext JS, LLC.
34291 * Originally Released Under LGPL - original licence link has changed is not relivant.
34294 * <script type="text/javascript">
34297 * @class Roo.bootstrap.layout.Border
34298 * @extends Roo.bootstrap.layout.Manager
34299 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34300 * please see: examples/bootstrap/nested.html<br><br>
34302 <b>The container the layout is rendered into can be either the body element or any other element.
34303 If it is not the body element, the container needs to either be an absolute positioned element,
34304 or you will need to add "position:relative" to the css of the container. You will also need to specify
34305 the container size if it is not the body element.</b>
34308 * Create a new Border
34309 * @param {Object} config Configuration options
34311 Roo.bootstrap.layout.Border = function(config){
34312 config = config || {};
34313 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34317 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34318 if(config[region]){
34319 config[region].region = region;
34320 this.addRegion(config[region]);
34326 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34328 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34330 * Creates and adds a new region if it doesn't already exist.
34331 * @param {String} target The target region key (north, south, east, west or center).
34332 * @param {Object} config The regions config object
34333 * @return {BorderLayoutRegion} The new region
34335 addRegion : function(config)
34337 if(!this.regions[config.region]){
34338 var r = this.factory(config);
34339 this.bindRegion(r);
34341 return this.regions[config.region];
34345 bindRegion : function(r){
34346 this.regions[r.config.region] = r;
34348 r.on("visibilitychange", this.layout, this);
34349 r.on("paneladded", this.layout, this);
34350 r.on("panelremoved", this.layout, this);
34351 r.on("invalidated", this.layout, this);
34352 r.on("resized", this.onRegionResized, this);
34353 r.on("collapsed", this.onRegionCollapsed, this);
34354 r.on("expanded", this.onRegionExpanded, this);
34358 * Performs a layout update.
34360 layout : function()
34362 if(this.updating) {
34366 // render all the rebions if they have not been done alreayd?
34367 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34368 if(this.regions[region] && !this.regions[region].bodyEl){
34369 this.regions[region].onRender(this.el)
34373 var size = this.getViewSize();
34374 var w = size.width;
34375 var h = size.height;
34380 //var x = 0, y = 0;
34382 var rs = this.regions;
34383 var north = rs["north"];
34384 var south = rs["south"];
34385 var west = rs["west"];
34386 var east = rs["east"];
34387 var center = rs["center"];
34388 //if(this.hideOnLayout){ // not supported anymore
34389 //c.el.setStyle("display", "none");
34391 if(north && north.isVisible()){
34392 var b = north.getBox();
34393 var m = north.getMargins();
34394 b.width = w - (m.left+m.right);
34397 centerY = b.height + b.y + m.bottom;
34398 centerH -= centerY;
34399 north.updateBox(this.safeBox(b));
34401 if(south && south.isVisible()){
34402 var b = south.getBox();
34403 var m = south.getMargins();
34404 b.width = w - (m.left+m.right);
34406 var totalHeight = (b.height + m.top + m.bottom);
34407 b.y = h - totalHeight + m.top;
34408 centerH -= totalHeight;
34409 south.updateBox(this.safeBox(b));
34411 if(west && west.isVisible()){
34412 var b = west.getBox();
34413 var m = west.getMargins();
34414 b.height = centerH - (m.top+m.bottom);
34416 b.y = centerY + m.top;
34417 var totalWidth = (b.width + m.left + m.right);
34418 centerX += totalWidth;
34419 centerW -= totalWidth;
34420 west.updateBox(this.safeBox(b));
34422 if(east && east.isVisible()){
34423 var b = east.getBox();
34424 var m = east.getMargins();
34425 b.height = centerH - (m.top+m.bottom);
34426 var totalWidth = (b.width + m.left + m.right);
34427 b.x = w - totalWidth + m.left;
34428 b.y = centerY + m.top;
34429 centerW -= totalWidth;
34430 east.updateBox(this.safeBox(b));
34433 var m = center.getMargins();
34435 x: centerX + m.left,
34436 y: centerY + m.top,
34437 width: centerW - (m.left+m.right),
34438 height: centerH - (m.top+m.bottom)
34440 //if(this.hideOnLayout){
34441 //center.el.setStyle("display", "block");
34443 center.updateBox(this.safeBox(centerBox));
34446 this.fireEvent("layout", this);
34450 safeBox : function(box){
34451 box.width = Math.max(0, box.width);
34452 box.height = Math.max(0, box.height);
34457 * Adds a ContentPanel (or subclass) to this layout.
34458 * @param {String} target The target region key (north, south, east, west or center).
34459 * @param {Roo.ContentPanel} panel The panel to add
34460 * @return {Roo.ContentPanel} The added panel
34462 add : function(target, panel){
34464 target = target.toLowerCase();
34465 return this.regions[target].add(panel);
34469 * Remove a ContentPanel (or subclass) to this layout.
34470 * @param {String} target The target region key (north, south, east, west or center).
34471 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34472 * @return {Roo.ContentPanel} The removed panel
34474 remove : function(target, panel){
34475 target = target.toLowerCase();
34476 return this.regions[target].remove(panel);
34480 * Searches all regions for a panel with the specified id
34481 * @param {String} panelId
34482 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34484 findPanel : function(panelId){
34485 var rs = this.regions;
34486 for(var target in rs){
34487 if(typeof rs[target] != "function"){
34488 var p = rs[target].getPanel(panelId);
34498 * Searches all regions for a panel with the specified id and activates (shows) it.
34499 * @param {String/ContentPanel} panelId The panels id or the panel itself
34500 * @return {Roo.ContentPanel} The shown panel or null
34502 showPanel : function(panelId) {
34503 var rs = this.regions;
34504 for(var target in rs){
34505 var r = rs[target];
34506 if(typeof r != "function"){
34507 if(r.hasPanel(panelId)){
34508 return r.showPanel(panelId);
34516 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34517 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34520 restoreState : function(provider){
34522 provider = Roo.state.Manager;
34524 var sm = new Roo.LayoutStateManager();
34525 sm.init(this, provider);
34531 * Adds a xtype elements to the layout.
34535 xtype : 'ContentPanel',
34542 xtype : 'NestedLayoutPanel',
34548 items : [ ... list of content panels or nested layout panels.. ]
34552 * @param {Object} cfg Xtype definition of item to add.
34554 addxtype : function(cfg)
34556 // basically accepts a pannel...
34557 // can accept a layout region..!?!?
34558 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34561 // theory? children can only be panels??
34563 //if (!cfg.xtype.match(/Panel$/)) {
34568 if (typeof(cfg.region) == 'undefined') {
34569 Roo.log("Failed to add Panel, region was not set");
34573 var region = cfg.region;
34579 xitems = cfg.items;
34586 case 'Content': // ContentPanel (el, cfg)
34587 case 'Scroll': // ContentPanel (el, cfg)
34589 cfg.autoCreate = true;
34590 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34592 // var el = this.el.createChild();
34593 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34596 this.add(region, ret);
34600 case 'TreePanel': // our new panel!
34601 cfg.el = this.el.createChild();
34602 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34603 this.add(region, ret);
34608 // create a new Layout (which is a Border Layout...
34610 var clayout = cfg.layout;
34611 clayout.el = this.el.createChild();
34612 clayout.items = clayout.items || [];
34616 // replace this exitems with the clayout ones..
34617 xitems = clayout.items;
34619 // force background off if it's in center...
34620 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34621 cfg.background = false;
34623 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34626 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34627 //console.log('adding nested layout panel ' + cfg.toSource());
34628 this.add(region, ret);
34629 nb = {}; /// find first...
34634 // needs grid and region
34636 //var el = this.getRegion(region).el.createChild();
34638 *var el = this.el.createChild();
34639 // create the grid first...
34640 cfg.grid.container = el;
34641 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34644 if (region == 'center' && this.active ) {
34645 cfg.background = false;
34648 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34650 this.add(region, ret);
34652 if (cfg.background) {
34653 // render grid on panel activation (if panel background)
34654 ret.on('activate', function(gp) {
34655 if (!gp.grid.rendered) {
34656 // gp.grid.render(el);
34660 // cfg.grid.render(el);
34666 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34667 // it was the old xcomponent building that caused this before.
34668 // espeically if border is the top element in the tree.
34678 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34680 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34681 this.add(region, ret);
34685 throw "Can not add '" + cfg.xtype + "' to Border";
34691 this.beginUpdate();
34695 Roo.each(xitems, function(i) {
34696 region = nb && i.region ? i.region : false;
34698 var add = ret.addxtype(i);
34701 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34702 if (!i.background) {
34703 abn[region] = nb[region] ;
34710 // make the last non-background panel active..
34711 //if (nb) { Roo.log(abn); }
34714 for(var r in abn) {
34715 region = this.getRegion(r);
34717 // tried using nb[r], but it does not work..
34719 region.showPanel(abn[r]);
34730 factory : function(cfg)
34733 var validRegions = Roo.bootstrap.layout.Border.regions;
34735 var target = cfg.region;
34738 var r = Roo.bootstrap.layout;
34742 return new r.North(cfg);
34744 return new r.South(cfg);
34746 return new r.East(cfg);
34748 return new r.West(cfg);
34750 return new r.Center(cfg);
34752 throw 'Layout region "'+target+'" not supported.';
34759 * Ext JS Library 1.1.1
34760 * Copyright(c) 2006-2007, Ext JS, LLC.
34762 * Originally Released Under LGPL - original licence link has changed is not relivant.
34765 * <script type="text/javascript">
34769 * @class Roo.bootstrap.layout.Basic
34770 * @extends Roo.util.Observable
34771 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34772 * and does not have a titlebar, tabs or any other features. All it does is size and position
34773 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34774 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
34775 * @cfg {string} region the region that it inhabits..
34776 * @cfg {bool} skipConfig skip config?
34780 Roo.bootstrap.layout.Basic = function(config){
34782 this.mgr = config.mgr;
34784 this.position = config.region;
34786 var skipConfig = config.skipConfig;
34790 * @scope Roo.BasicLayoutRegion
34794 * @event beforeremove
34795 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34796 * @param {Roo.LayoutRegion} this
34797 * @param {Roo.ContentPanel} panel The panel
34798 * @param {Object} e The cancel event object
34800 "beforeremove" : true,
34802 * @event invalidated
34803 * Fires when the layout for this region is changed.
34804 * @param {Roo.LayoutRegion} this
34806 "invalidated" : true,
34808 * @event visibilitychange
34809 * Fires when this region is shown or hidden
34810 * @param {Roo.LayoutRegion} this
34811 * @param {Boolean} visibility true or false
34813 "visibilitychange" : true,
34815 * @event paneladded
34816 * Fires when a panel is added.
34817 * @param {Roo.LayoutRegion} this
34818 * @param {Roo.ContentPanel} panel The panel
34820 "paneladded" : true,
34822 * @event panelremoved
34823 * Fires when a panel is removed.
34824 * @param {Roo.LayoutRegion} this
34825 * @param {Roo.ContentPanel} panel The panel
34827 "panelremoved" : true,
34829 * @event beforecollapse
34830 * Fires when this region before collapse.
34831 * @param {Roo.LayoutRegion} this
34833 "beforecollapse" : true,
34836 * Fires when this region is collapsed.
34837 * @param {Roo.LayoutRegion} this
34839 "collapsed" : true,
34842 * Fires when this region is expanded.
34843 * @param {Roo.LayoutRegion} this
34848 * Fires when this region is slid into view.
34849 * @param {Roo.LayoutRegion} this
34851 "slideshow" : true,
34854 * Fires when this region slides out of view.
34855 * @param {Roo.LayoutRegion} this
34857 "slidehide" : true,
34859 * @event panelactivated
34860 * Fires when a panel is activated.
34861 * @param {Roo.LayoutRegion} this
34862 * @param {Roo.ContentPanel} panel The activated panel
34864 "panelactivated" : true,
34867 * Fires when the user resizes this region.
34868 * @param {Roo.LayoutRegion} this
34869 * @param {Number} newSize The new size (width for east/west, height for north/south)
34873 /** A collection of panels in this region. @type Roo.util.MixedCollection */
34874 this.panels = new Roo.util.MixedCollection();
34875 this.panels.getKey = this.getPanelId.createDelegate(this);
34877 this.activePanel = null;
34878 // ensure listeners are added...
34880 if (config.listeners || config.events) {
34881 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34882 listeners : config.listeners || {},
34883 events : config.events || {}
34887 if(skipConfig !== true){
34888 this.applyConfig(config);
34892 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34894 getPanelId : function(p){
34898 applyConfig : function(config){
34899 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34900 this.config = config;
34905 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
34906 * the width, for horizontal (north, south) the height.
34907 * @param {Number} newSize The new width or height
34909 resizeTo : function(newSize){
34910 var el = this.el ? this.el :
34911 (this.activePanel ? this.activePanel.getEl() : null);
34913 switch(this.position){
34916 el.setWidth(newSize);
34917 this.fireEvent("resized", this, newSize);
34921 el.setHeight(newSize);
34922 this.fireEvent("resized", this, newSize);
34928 getBox : function(){
34929 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34932 getMargins : function(){
34933 return this.margins;
34936 updateBox : function(box){
34938 var el = this.activePanel.getEl();
34939 el.dom.style.left = box.x + "px";
34940 el.dom.style.top = box.y + "px";
34941 this.activePanel.setSize(box.width, box.height);
34945 * Returns the container element for this region.
34946 * @return {Roo.Element}
34948 getEl : function(){
34949 return this.activePanel;
34953 * Returns true if this region is currently visible.
34954 * @return {Boolean}
34956 isVisible : function(){
34957 return this.activePanel ? true : false;
34960 setActivePanel : function(panel){
34961 panel = this.getPanel(panel);
34962 if(this.activePanel && this.activePanel != panel){
34963 this.activePanel.setActiveState(false);
34964 this.activePanel.getEl().setLeftTop(-10000,-10000);
34966 this.activePanel = panel;
34967 panel.setActiveState(true);
34969 panel.setSize(this.box.width, this.box.height);
34971 this.fireEvent("panelactivated", this, panel);
34972 this.fireEvent("invalidated");
34976 * Show the specified panel.
34977 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34978 * @return {Roo.ContentPanel} The shown panel or null
34980 showPanel : function(panel){
34981 panel = this.getPanel(panel);
34983 this.setActivePanel(panel);
34989 * Get the active panel for this region.
34990 * @return {Roo.ContentPanel} The active panel or null
34992 getActivePanel : function(){
34993 return this.activePanel;
34997 * Add the passed ContentPanel(s)
34998 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34999 * @return {Roo.ContentPanel} The panel added (if only one was added)
35001 add : function(panel){
35002 if(arguments.length > 1){
35003 for(var i = 0, len = arguments.length; i < len; i++) {
35004 this.add(arguments[i]);
35008 if(this.hasPanel(panel)){
35009 this.showPanel(panel);
35012 var el = panel.getEl();
35013 if(el.dom.parentNode != this.mgr.el.dom){
35014 this.mgr.el.dom.appendChild(el.dom);
35016 if(panel.setRegion){
35017 panel.setRegion(this);
35019 this.panels.add(panel);
35020 el.setStyle("position", "absolute");
35021 if(!panel.background){
35022 this.setActivePanel(panel);
35023 if(this.config.initialSize && this.panels.getCount()==1){
35024 this.resizeTo(this.config.initialSize);
35027 this.fireEvent("paneladded", this, panel);
35032 * Returns true if the panel is in this region.
35033 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35034 * @return {Boolean}
35036 hasPanel : function(panel){
35037 if(typeof panel == "object"){ // must be panel obj
35038 panel = panel.getId();
35040 return this.getPanel(panel) ? true : false;
35044 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35045 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35046 * @param {Boolean} preservePanel Overrides the config preservePanel option
35047 * @return {Roo.ContentPanel} The panel that was removed
35049 remove : function(panel, preservePanel){
35050 panel = this.getPanel(panel);
35055 this.fireEvent("beforeremove", this, panel, e);
35056 if(e.cancel === true){
35059 var panelId = panel.getId();
35060 this.panels.removeKey(panelId);
35065 * Returns the panel specified or null if it's not in this region.
35066 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35067 * @return {Roo.ContentPanel}
35069 getPanel : function(id){
35070 if(typeof id == "object"){ // must be panel obj
35073 return this.panels.get(id);
35077 * Returns this regions position (north/south/east/west/center).
35080 getPosition: function(){
35081 return this.position;
35085 * Ext JS Library 1.1.1
35086 * Copyright(c) 2006-2007, Ext JS, LLC.
35088 * Originally Released Under LGPL - original licence link has changed is not relivant.
35091 * <script type="text/javascript">
35095 * @class Roo.bootstrap.layout.Region
35096 * @extends Roo.bootstrap.layout.Basic
35097 * This class represents a region in a layout manager.
35099 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35100 * @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})
35101 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35102 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35103 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35104 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35105 * @cfg {String} title The title for the region (overrides panel titles)
35106 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35107 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35108 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35109 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35110 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35111 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35112 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35113 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35114 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35115 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35117 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35118 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35119 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35120 * @cfg {Number} width For East/West panels
35121 * @cfg {Number} height For North/South panels
35122 * @cfg {Boolean} split To show the splitter
35123 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35125 * @cfg {string} cls Extra CSS classes to add to region
35127 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35128 * @cfg {string} region the region that it inhabits..
35131 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35132 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35134 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35135 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35136 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35138 Roo.bootstrap.layout.Region = function(config)
35140 this.applyConfig(config);
35142 var mgr = config.mgr;
35143 var pos = config.region;
35144 config.skipConfig = true;
35145 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35148 this.onRender(mgr.el);
35151 this.visible = true;
35152 this.collapsed = false;
35153 this.unrendered_panels = [];
35156 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35158 position: '', // set by wrapper (eg. north/south etc..)
35159 unrendered_panels : null, // unrendered panels.
35160 createBody : function(){
35161 /** This region's body element
35162 * @type Roo.Element */
35163 this.bodyEl = this.el.createChild({
35165 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35169 onRender: function(ctr, pos)
35171 var dh = Roo.DomHelper;
35172 /** This region's container element
35173 * @type Roo.Element */
35174 this.el = dh.append(ctr.dom, {
35176 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35178 /** This region's title element
35179 * @type Roo.Element */
35181 this.titleEl = dh.append(this.el.dom,
35184 unselectable: "on",
35185 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35187 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35188 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35191 this.titleEl.enableDisplayMode();
35192 /** This region's title text element
35193 * @type HTMLElement */
35194 this.titleTextEl = this.titleEl.dom.firstChild;
35195 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35197 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35198 this.closeBtn.enableDisplayMode();
35199 this.closeBtn.on("click", this.closeClicked, this);
35200 this.closeBtn.hide();
35202 this.createBody(this.config);
35203 if(this.config.hideWhenEmpty){
35205 this.on("paneladded", this.validateVisibility, this);
35206 this.on("panelremoved", this.validateVisibility, this);
35208 if(this.autoScroll){
35209 this.bodyEl.setStyle("overflow", "auto");
35211 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35213 //if(c.titlebar !== false){
35214 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35215 this.titleEl.hide();
35217 this.titleEl.show();
35218 if(this.config.title){
35219 this.titleTextEl.innerHTML = this.config.title;
35223 if(this.config.collapsed){
35224 this.collapse(true);
35226 if(this.config.hidden){
35230 if (this.unrendered_panels && this.unrendered_panels.length) {
35231 for (var i =0;i< this.unrendered_panels.length; i++) {
35232 this.add(this.unrendered_panels[i]);
35234 this.unrendered_panels = null;
35240 applyConfig : function(c)
35243 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35244 var dh = Roo.DomHelper;
35245 if(c.titlebar !== false){
35246 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35247 this.collapseBtn.on("click", this.collapse, this);
35248 this.collapseBtn.enableDisplayMode();
35250 if(c.showPin === true || this.showPin){
35251 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35252 this.stickBtn.enableDisplayMode();
35253 this.stickBtn.on("click", this.expand, this);
35254 this.stickBtn.hide();
35259 /** This region's collapsed element
35260 * @type Roo.Element */
35263 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35264 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35267 if(c.floatable !== false){
35268 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35269 this.collapsedEl.on("click", this.collapseClick, this);
35272 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35273 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35274 id: "message", unselectable: "on", style:{"float":"left"}});
35275 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35277 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35278 this.expandBtn.on("click", this.expand, this);
35282 if(this.collapseBtn){
35283 this.collapseBtn.setVisible(c.collapsible == true);
35286 this.cmargins = c.cmargins || this.cmargins ||
35287 (this.position == "west" || this.position == "east" ?
35288 {top: 0, left: 2, right:2, bottom: 0} :
35289 {top: 2, left: 0, right:0, bottom: 2});
35291 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35294 this.bottomTabs = c.tabPosition != "top";
35296 this.autoScroll = c.autoScroll || false;
35301 this.duration = c.duration || .30;
35302 this.slideDuration = c.slideDuration || .45;
35307 * Returns true if this region is currently visible.
35308 * @return {Boolean}
35310 isVisible : function(){
35311 return this.visible;
35315 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35316 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35318 //setCollapsedTitle : function(title){
35319 // title = title || " ";
35320 // if(this.collapsedTitleTextEl){
35321 // this.collapsedTitleTextEl.innerHTML = title;
35325 getBox : function(){
35327 // if(!this.collapsed){
35328 b = this.el.getBox(false, true);
35330 // b = this.collapsedEl.getBox(false, true);
35335 getMargins : function(){
35336 return this.margins;
35337 //return this.collapsed ? this.cmargins : this.margins;
35340 highlight : function(){
35341 this.el.addClass("x-layout-panel-dragover");
35344 unhighlight : function(){
35345 this.el.removeClass("x-layout-panel-dragover");
35348 updateBox : function(box)
35350 if (!this.bodyEl) {
35351 return; // not rendered yet..
35355 if(!this.collapsed){
35356 this.el.dom.style.left = box.x + "px";
35357 this.el.dom.style.top = box.y + "px";
35358 this.updateBody(box.width, box.height);
35360 this.collapsedEl.dom.style.left = box.x + "px";
35361 this.collapsedEl.dom.style.top = box.y + "px";
35362 this.collapsedEl.setSize(box.width, box.height);
35365 this.tabs.autoSizeTabs();
35369 updateBody : function(w, h)
35372 this.el.setWidth(w);
35373 w -= this.el.getBorderWidth("rl");
35374 if(this.config.adjustments){
35375 w += this.config.adjustments[0];
35378 if(h !== null && h > 0){
35379 this.el.setHeight(h);
35380 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35381 h -= this.el.getBorderWidth("tb");
35382 if(this.config.adjustments){
35383 h += this.config.adjustments[1];
35385 this.bodyEl.setHeight(h);
35387 h = this.tabs.syncHeight(h);
35390 if(this.panelSize){
35391 w = w !== null ? w : this.panelSize.width;
35392 h = h !== null ? h : this.panelSize.height;
35394 if(this.activePanel){
35395 var el = this.activePanel.getEl();
35396 w = w !== null ? w : el.getWidth();
35397 h = h !== null ? h : el.getHeight();
35398 this.panelSize = {width: w, height: h};
35399 this.activePanel.setSize(w, h);
35401 if(Roo.isIE && this.tabs){
35402 this.tabs.el.repaint();
35407 * Returns the container element for this region.
35408 * @return {Roo.Element}
35410 getEl : function(){
35415 * Hides this region.
35418 //if(!this.collapsed){
35419 this.el.dom.style.left = "-2000px";
35422 // this.collapsedEl.dom.style.left = "-2000px";
35423 // this.collapsedEl.hide();
35425 this.visible = false;
35426 this.fireEvent("visibilitychange", this, false);
35430 * Shows this region if it was previously hidden.
35433 //if(!this.collapsed){
35436 // this.collapsedEl.show();
35438 this.visible = true;
35439 this.fireEvent("visibilitychange", this, true);
35442 closeClicked : function(){
35443 if(this.activePanel){
35444 this.remove(this.activePanel);
35448 collapseClick : function(e){
35450 e.stopPropagation();
35453 e.stopPropagation();
35459 * Collapses this region.
35460 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35463 collapse : function(skipAnim, skipCheck = false){
35464 if(this.collapsed) {
35468 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35470 this.collapsed = true;
35472 this.split.el.hide();
35474 if(this.config.animate && skipAnim !== true){
35475 this.fireEvent("invalidated", this);
35476 this.animateCollapse();
35478 this.el.setLocation(-20000,-20000);
35480 this.collapsedEl.show();
35481 this.fireEvent("collapsed", this);
35482 this.fireEvent("invalidated", this);
35488 animateCollapse : function(){
35493 * Expands this region if it was previously collapsed.
35494 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35495 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35498 expand : function(e, skipAnim){
35500 e.stopPropagation();
35502 if(!this.collapsed || this.el.hasActiveFx()) {
35506 this.afterSlideIn();
35509 this.collapsed = false;
35510 if(this.config.animate && skipAnim !== true){
35511 this.animateExpand();
35515 this.split.el.show();
35517 this.collapsedEl.setLocation(-2000,-2000);
35518 this.collapsedEl.hide();
35519 this.fireEvent("invalidated", this);
35520 this.fireEvent("expanded", this);
35524 animateExpand : function(){
35528 initTabs : function()
35530 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35532 var ts = new Roo.bootstrap.panel.Tabs({
35533 el: this.bodyEl.dom,
35534 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35535 disableTooltips: this.config.disableTabTips,
35536 toolbar : this.config.toolbar
35539 if(this.config.hideTabs){
35540 ts.stripWrap.setDisplayed(false);
35543 ts.resizeTabs = this.config.resizeTabs === true;
35544 ts.minTabWidth = this.config.minTabWidth || 40;
35545 ts.maxTabWidth = this.config.maxTabWidth || 250;
35546 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35547 ts.monitorResize = false;
35548 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35549 ts.bodyEl.addClass('roo-layout-tabs-body');
35550 this.panels.each(this.initPanelAsTab, this);
35553 initPanelAsTab : function(panel){
35554 var ti = this.tabs.addTab(
35558 this.config.closeOnTab && panel.isClosable(),
35561 if(panel.tabTip !== undefined){
35562 ti.setTooltip(panel.tabTip);
35564 ti.on("activate", function(){
35565 this.setActivePanel(panel);
35568 if(this.config.closeOnTab){
35569 ti.on("beforeclose", function(t, e){
35571 this.remove(panel);
35575 panel.tabItem = ti;
35580 updatePanelTitle : function(panel, title)
35582 if(this.activePanel == panel){
35583 this.updateTitle(title);
35586 var ti = this.tabs.getTab(panel.getEl().id);
35588 if(panel.tabTip !== undefined){
35589 ti.setTooltip(panel.tabTip);
35594 updateTitle : function(title){
35595 if(this.titleTextEl && !this.config.title){
35596 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35600 setActivePanel : function(panel)
35602 panel = this.getPanel(panel);
35603 if(this.activePanel && this.activePanel != panel){
35604 if(this.activePanel.setActiveState(false) === false){
35608 this.activePanel = panel;
35609 panel.setActiveState(true);
35610 if(this.panelSize){
35611 panel.setSize(this.panelSize.width, this.panelSize.height);
35614 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35616 this.updateTitle(panel.getTitle());
35618 this.fireEvent("invalidated", this);
35620 this.fireEvent("panelactivated", this, panel);
35624 * Shows the specified panel.
35625 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35626 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35628 showPanel : function(panel)
35630 panel = this.getPanel(panel);
35633 var tab = this.tabs.getTab(panel.getEl().id);
35634 if(tab.isHidden()){
35635 this.tabs.unhideTab(tab.id);
35639 this.setActivePanel(panel);
35646 * Get the active panel for this region.
35647 * @return {Roo.ContentPanel} The active panel or null
35649 getActivePanel : function(){
35650 return this.activePanel;
35653 validateVisibility : function(){
35654 if(this.panels.getCount() < 1){
35655 this.updateTitle(" ");
35656 this.closeBtn.hide();
35659 if(!this.isVisible()){
35666 * Adds the passed ContentPanel(s) to this region.
35667 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35668 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35670 add : function(panel)
35672 if(arguments.length > 1){
35673 for(var i = 0, len = arguments.length; i < len; i++) {
35674 this.add(arguments[i]);
35679 // if we have not been rendered yet, then we can not really do much of this..
35680 if (!this.bodyEl) {
35681 this.unrendered_panels.push(panel);
35688 if(this.hasPanel(panel)){
35689 this.showPanel(panel);
35692 panel.setRegion(this);
35693 this.panels.add(panel);
35694 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35695 // sinle panel - no tab...?? would it not be better to render it with the tabs,
35696 // and hide them... ???
35697 this.bodyEl.dom.appendChild(panel.getEl().dom);
35698 if(panel.background !== true){
35699 this.setActivePanel(panel);
35701 this.fireEvent("paneladded", this, panel);
35708 this.initPanelAsTab(panel);
35712 if(panel.background !== true){
35713 this.tabs.activate(panel.getEl().id);
35715 this.fireEvent("paneladded", this, panel);
35720 * Hides the tab for the specified panel.
35721 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35723 hidePanel : function(panel){
35724 if(this.tabs && (panel = this.getPanel(panel))){
35725 this.tabs.hideTab(panel.getEl().id);
35730 * Unhides the tab for a previously hidden panel.
35731 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35733 unhidePanel : function(panel){
35734 if(this.tabs && (panel = this.getPanel(panel))){
35735 this.tabs.unhideTab(panel.getEl().id);
35739 clearPanels : function(){
35740 while(this.panels.getCount() > 0){
35741 this.remove(this.panels.first());
35746 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35747 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35748 * @param {Boolean} preservePanel Overrides the config preservePanel option
35749 * @return {Roo.ContentPanel} The panel that was removed
35751 remove : function(panel, preservePanel)
35753 panel = this.getPanel(panel);
35758 this.fireEvent("beforeremove", this, panel, e);
35759 if(e.cancel === true){
35762 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35763 var panelId = panel.getId();
35764 this.panels.removeKey(panelId);
35766 document.body.appendChild(panel.getEl().dom);
35769 this.tabs.removeTab(panel.getEl().id);
35770 }else if (!preservePanel){
35771 this.bodyEl.dom.removeChild(panel.getEl().dom);
35773 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35774 var p = this.panels.first();
35775 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35776 tempEl.appendChild(p.getEl().dom);
35777 this.bodyEl.update("");
35778 this.bodyEl.dom.appendChild(p.getEl().dom);
35780 this.updateTitle(p.getTitle());
35782 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35783 this.setActivePanel(p);
35785 panel.setRegion(null);
35786 if(this.activePanel == panel){
35787 this.activePanel = null;
35789 if(this.config.autoDestroy !== false && preservePanel !== true){
35790 try{panel.destroy();}catch(e){}
35792 this.fireEvent("panelremoved", this, panel);
35797 * Returns the TabPanel component used by this region
35798 * @return {Roo.TabPanel}
35800 getTabs : function(){
35804 createTool : function(parentEl, className){
35805 var btn = Roo.DomHelper.append(parentEl, {
35807 cls: "x-layout-tools-button",
35810 cls: "roo-layout-tools-button-inner " + className,
35814 btn.addClassOnOver("roo-layout-tools-button-over");
35819 * Ext JS Library 1.1.1
35820 * Copyright(c) 2006-2007, Ext JS, LLC.
35822 * Originally Released Under LGPL - original licence link has changed is not relivant.
35825 * <script type="text/javascript">
35831 * @class Roo.SplitLayoutRegion
35832 * @extends Roo.LayoutRegion
35833 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35835 Roo.bootstrap.layout.Split = function(config){
35836 this.cursor = config.cursor;
35837 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35840 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35842 splitTip : "Drag to resize.",
35843 collapsibleSplitTip : "Drag to resize. Double click to hide.",
35844 useSplitTips : false,
35846 applyConfig : function(config){
35847 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35850 onRender : function(ctr,pos) {
35852 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35853 if(!this.config.split){
35858 var splitEl = Roo.DomHelper.append(ctr.dom, {
35860 id: this.el.id + "-split",
35861 cls: "roo-layout-split roo-layout-split-"+this.position,
35864 /** The SplitBar for this region
35865 * @type Roo.SplitBar */
35866 // does not exist yet...
35867 Roo.log([this.position, this.orientation]);
35869 this.split = new Roo.bootstrap.SplitBar({
35870 dragElement : splitEl,
35871 resizingElement: this.el,
35872 orientation : this.orientation
35875 this.split.on("moved", this.onSplitMove, this);
35876 this.split.useShim = this.config.useShim === true;
35877 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35878 if(this.useSplitTips){
35879 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35881 //if(config.collapsible){
35882 // this.split.el.on("dblclick", this.collapse, this);
35885 if(typeof this.config.minSize != "undefined"){
35886 this.split.minSize = this.config.minSize;
35888 if(typeof this.config.maxSize != "undefined"){
35889 this.split.maxSize = this.config.maxSize;
35891 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35892 this.hideSplitter();
35897 getHMaxSize : function(){
35898 var cmax = this.config.maxSize || 10000;
35899 var center = this.mgr.getRegion("center");
35900 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35903 getVMaxSize : function(){
35904 var cmax = this.config.maxSize || 10000;
35905 var center = this.mgr.getRegion("center");
35906 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35909 onSplitMove : function(split, newSize){
35910 this.fireEvent("resized", this, newSize);
35914 * Returns the {@link Roo.SplitBar} for this region.
35915 * @return {Roo.SplitBar}
35917 getSplitBar : function(){
35922 this.hideSplitter();
35923 Roo.bootstrap.layout.Split.superclass.hide.call(this);
35926 hideSplitter : function(){
35928 this.split.el.setLocation(-2000,-2000);
35929 this.split.el.hide();
35935 this.split.el.show();
35937 Roo.bootstrap.layout.Split.superclass.show.call(this);
35940 beforeSlide: function(){
35941 if(Roo.isGecko){// firefox overflow auto bug workaround
35942 this.bodyEl.clip();
35944 this.tabs.bodyEl.clip();
35946 if(this.activePanel){
35947 this.activePanel.getEl().clip();
35949 if(this.activePanel.beforeSlide){
35950 this.activePanel.beforeSlide();
35956 afterSlide : function(){
35957 if(Roo.isGecko){// firefox overflow auto bug workaround
35958 this.bodyEl.unclip();
35960 this.tabs.bodyEl.unclip();
35962 if(this.activePanel){
35963 this.activePanel.getEl().unclip();
35964 if(this.activePanel.afterSlide){
35965 this.activePanel.afterSlide();
35971 initAutoHide : function(){
35972 if(this.autoHide !== false){
35973 if(!this.autoHideHd){
35974 var st = new Roo.util.DelayedTask(this.slideIn, this);
35975 this.autoHideHd = {
35976 "mouseout": function(e){
35977 if(!e.within(this.el, true)){
35981 "mouseover" : function(e){
35987 this.el.on(this.autoHideHd);
35991 clearAutoHide : function(){
35992 if(this.autoHide !== false){
35993 this.el.un("mouseout", this.autoHideHd.mouseout);
35994 this.el.un("mouseover", this.autoHideHd.mouseover);
35998 clearMonitor : function(){
35999 Roo.get(document).un("click", this.slideInIf, this);
36002 // these names are backwards but not changed for compat
36003 slideOut : function(){
36004 if(this.isSlid || this.el.hasActiveFx()){
36007 this.isSlid = true;
36008 if(this.collapseBtn){
36009 this.collapseBtn.hide();
36011 this.closeBtnState = this.closeBtn.getStyle('display');
36012 this.closeBtn.hide();
36014 this.stickBtn.show();
36017 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36018 this.beforeSlide();
36019 this.el.setStyle("z-index", 10001);
36020 this.el.slideIn(this.getSlideAnchor(), {
36021 callback: function(){
36023 this.initAutoHide();
36024 Roo.get(document).on("click", this.slideInIf, this);
36025 this.fireEvent("slideshow", this);
36032 afterSlideIn : function(){
36033 this.clearAutoHide();
36034 this.isSlid = false;
36035 this.clearMonitor();
36036 this.el.setStyle("z-index", "");
36037 if(this.collapseBtn){
36038 this.collapseBtn.show();
36040 this.closeBtn.setStyle('display', this.closeBtnState);
36042 this.stickBtn.hide();
36044 this.fireEvent("slidehide", this);
36047 slideIn : function(cb){
36048 if(!this.isSlid || this.el.hasActiveFx()){
36052 this.isSlid = false;
36053 this.beforeSlide();
36054 this.el.slideOut(this.getSlideAnchor(), {
36055 callback: function(){
36056 this.el.setLeftTop(-10000, -10000);
36058 this.afterSlideIn();
36066 slideInIf : function(e){
36067 if(!e.within(this.el)){
36072 animateCollapse : function(){
36073 this.beforeSlide();
36074 this.el.setStyle("z-index", 20000);
36075 var anchor = this.getSlideAnchor();
36076 this.el.slideOut(anchor, {
36077 callback : function(){
36078 this.el.setStyle("z-index", "");
36079 this.collapsedEl.slideIn(anchor, {duration:.3});
36081 this.el.setLocation(-10000,-10000);
36083 this.fireEvent("collapsed", this);
36090 animateExpand : function(){
36091 this.beforeSlide();
36092 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36093 this.el.setStyle("z-index", 20000);
36094 this.collapsedEl.hide({
36097 this.el.slideIn(this.getSlideAnchor(), {
36098 callback : function(){
36099 this.el.setStyle("z-index", "");
36102 this.split.el.show();
36104 this.fireEvent("invalidated", this);
36105 this.fireEvent("expanded", this);
36133 getAnchor : function(){
36134 return this.anchors[this.position];
36137 getCollapseAnchor : function(){
36138 return this.canchors[this.position];
36141 getSlideAnchor : function(){
36142 return this.sanchors[this.position];
36145 getAlignAdj : function(){
36146 var cm = this.cmargins;
36147 switch(this.position){
36163 getExpandAdj : function(){
36164 var c = this.collapsedEl, cm = this.cmargins;
36165 switch(this.position){
36167 return [-(cm.right+c.getWidth()+cm.left), 0];
36170 return [cm.right+c.getWidth()+cm.left, 0];
36173 return [0, -(cm.top+cm.bottom+c.getHeight())];
36176 return [0, cm.top+cm.bottom+c.getHeight()];
36182 * Ext JS Library 1.1.1
36183 * Copyright(c) 2006-2007, Ext JS, LLC.
36185 * Originally Released Under LGPL - original licence link has changed is not relivant.
36188 * <script type="text/javascript">
36191 * These classes are private internal classes
36193 Roo.bootstrap.layout.Center = function(config){
36194 config.region = "center";
36195 Roo.bootstrap.layout.Region.call(this, config);
36196 this.visible = true;
36197 this.minWidth = config.minWidth || 20;
36198 this.minHeight = config.minHeight || 20;
36201 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36203 // center panel can't be hidden
36207 // center panel can't be hidden
36210 getMinWidth: function(){
36211 return this.minWidth;
36214 getMinHeight: function(){
36215 return this.minHeight;
36228 Roo.bootstrap.layout.North = function(config)
36230 config.region = 'north';
36231 config.cursor = 'n-resize';
36233 Roo.bootstrap.layout.Split.call(this, config);
36237 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36238 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36239 this.split.el.addClass("roo-layout-split-v");
36241 var size = config.initialSize || config.height;
36242 if(typeof size != "undefined"){
36243 this.el.setHeight(size);
36246 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36248 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36252 getBox : function(){
36253 if(this.collapsed){
36254 return this.collapsedEl.getBox();
36256 var box = this.el.getBox();
36258 box.height += this.split.el.getHeight();
36263 updateBox : function(box){
36264 if(this.split && !this.collapsed){
36265 box.height -= this.split.el.getHeight();
36266 this.split.el.setLeft(box.x);
36267 this.split.el.setTop(box.y+box.height);
36268 this.split.el.setWidth(box.width);
36270 if(this.collapsed){
36271 this.updateBody(box.width, null);
36273 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36281 Roo.bootstrap.layout.South = function(config){
36282 config.region = 'south';
36283 config.cursor = 's-resize';
36284 Roo.bootstrap.layout.Split.call(this, config);
36286 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36287 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36288 this.split.el.addClass("roo-layout-split-v");
36290 var size = config.initialSize || config.height;
36291 if(typeof size != "undefined"){
36292 this.el.setHeight(size);
36296 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36297 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36298 getBox : function(){
36299 if(this.collapsed){
36300 return this.collapsedEl.getBox();
36302 var box = this.el.getBox();
36304 var sh = this.split.el.getHeight();
36311 updateBox : function(box){
36312 if(this.split && !this.collapsed){
36313 var sh = this.split.el.getHeight();
36316 this.split.el.setLeft(box.x);
36317 this.split.el.setTop(box.y-sh);
36318 this.split.el.setWidth(box.width);
36320 if(this.collapsed){
36321 this.updateBody(box.width, null);
36323 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36327 Roo.bootstrap.layout.East = function(config){
36328 config.region = "east";
36329 config.cursor = "e-resize";
36330 Roo.bootstrap.layout.Split.call(this, config);
36332 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36333 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36334 this.split.el.addClass("roo-layout-split-h");
36336 var size = config.initialSize || config.width;
36337 if(typeof size != "undefined"){
36338 this.el.setWidth(size);
36341 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36342 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36343 getBox : function(){
36344 if(this.collapsed){
36345 return this.collapsedEl.getBox();
36347 var box = this.el.getBox();
36349 var sw = this.split.el.getWidth();
36356 updateBox : function(box){
36357 if(this.split && !this.collapsed){
36358 var sw = this.split.el.getWidth();
36360 this.split.el.setLeft(box.x);
36361 this.split.el.setTop(box.y);
36362 this.split.el.setHeight(box.height);
36365 if(this.collapsed){
36366 this.updateBody(null, box.height);
36368 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36372 Roo.bootstrap.layout.West = function(config){
36373 config.region = "west";
36374 config.cursor = "w-resize";
36376 Roo.bootstrap.layout.Split.call(this, config);
36378 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36379 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36380 this.split.el.addClass("roo-layout-split-h");
36384 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36385 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36387 onRender: function(ctr, pos)
36389 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36390 var size = this.config.initialSize || this.config.width;
36391 if(typeof size != "undefined"){
36392 this.el.setWidth(size);
36396 getBox : function(){
36397 if(this.collapsed){
36398 return this.collapsedEl.getBox();
36400 var box = this.el.getBox();
36402 box.width += this.split.el.getWidth();
36407 updateBox : function(box){
36408 if(this.split && !this.collapsed){
36409 var sw = this.split.el.getWidth();
36411 this.split.el.setLeft(box.x+box.width);
36412 this.split.el.setTop(box.y);
36413 this.split.el.setHeight(box.height);
36415 if(this.collapsed){
36416 this.updateBody(null, box.height);
36418 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36421 Roo.namespace("Roo.bootstrap.panel");/*
36423 * Ext JS Library 1.1.1
36424 * Copyright(c) 2006-2007, Ext JS, LLC.
36426 * Originally Released Under LGPL - original licence link has changed is not relivant.
36429 * <script type="text/javascript">
36432 * @class Roo.ContentPanel
36433 * @extends Roo.util.Observable
36434 * A basic ContentPanel element.
36435 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36436 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36437 * @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
36438 * @cfg {Boolean} closable True if the panel can be closed/removed
36439 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36440 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36441 * @cfg {Toolbar} toolbar A toolbar for this panel
36442 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36443 * @cfg {String} title The title for this panel
36444 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36445 * @cfg {String} url Calls {@link #setUrl} with this value
36446 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36447 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36448 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36449 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36450 * @cfg {Boolean} badges render the badges
36453 * Create a new ContentPanel.
36454 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36455 * @param {String/Object} config A string to set only the title or a config object
36456 * @param {String} content (optional) Set the HTML content for this panel
36457 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36459 Roo.bootstrap.panel.Content = function( config){
36461 this.tpl = config.tpl || false;
36463 var el = config.el;
36464 var content = config.content;
36466 if(config.autoCreate){ // xtype is available if this is called from factory
36469 this.el = Roo.get(el);
36470 if(!this.el && config && config.autoCreate){
36471 if(typeof config.autoCreate == "object"){
36472 if(!config.autoCreate.id){
36473 config.autoCreate.id = config.id||el;
36475 this.el = Roo.DomHelper.append(document.body,
36476 config.autoCreate, true);
36478 var elcfg = { tag: "div",
36479 cls: "roo-layout-inactive-content",
36483 elcfg.html = config.html;
36487 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36490 this.closable = false;
36491 this.loaded = false;
36492 this.active = false;
36495 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36497 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36499 this.wrapEl = this.el; //this.el.wrap();
36501 if (config.toolbar.items) {
36502 ti = config.toolbar.items ;
36503 delete config.toolbar.items ;
36507 this.toolbar.render(this.wrapEl, 'before');
36508 for(var i =0;i < ti.length;i++) {
36509 // Roo.log(['add child', items[i]]);
36510 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36512 this.toolbar.items = nitems;
36513 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36514 delete config.toolbar;
36518 // xtype created footer. - not sure if will work as we normally have to render first..
36519 if (this.footer && !this.footer.el && this.footer.xtype) {
36520 if (!this.wrapEl) {
36521 this.wrapEl = this.el.wrap();
36524 this.footer.container = this.wrapEl.createChild();
36526 this.footer = Roo.factory(this.footer, Roo);
36531 if(typeof config == "string"){
36532 this.title = config;
36534 Roo.apply(this, config);
36538 this.resizeEl = Roo.get(this.resizeEl, true);
36540 this.resizeEl = this.el;
36542 // handle view.xtype
36550 * Fires when this panel is activated.
36551 * @param {Roo.ContentPanel} this
36555 * @event deactivate
36556 * Fires when this panel is activated.
36557 * @param {Roo.ContentPanel} this
36559 "deactivate" : true,
36563 * Fires when this panel is resized if fitToFrame is true.
36564 * @param {Roo.ContentPanel} this
36565 * @param {Number} width The width after any component adjustments
36566 * @param {Number} height The height after any component adjustments
36572 * Fires when this tab is created
36573 * @param {Roo.ContentPanel} this
36584 if(this.autoScroll){
36585 this.resizeEl.setStyle("overflow", "auto");
36587 // fix randome scrolling
36588 //this.el.on('scroll', function() {
36589 // Roo.log('fix random scolling');
36590 // this.scrollTo('top',0);
36593 content = content || this.content;
36595 this.setContent(content);
36597 if(config && config.url){
36598 this.setUrl(this.url, this.params, this.loadOnce);
36603 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36605 if (this.view && typeof(this.view.xtype) != 'undefined') {
36606 this.view.el = this.el.appendChild(document.createElement("div"));
36607 this.view = Roo.factory(this.view);
36608 this.view.render && this.view.render(false, '');
36612 this.fireEvent('render', this);
36615 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36619 setRegion : function(region){
36620 this.region = region;
36621 this.setActiveClass(region && !this.background);
36625 setActiveClass: function(state)
36628 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36629 this.el.setStyle('position','relative');
36631 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36632 this.el.setStyle('position', 'absolute');
36637 * Returns the toolbar for this Panel if one was configured.
36638 * @return {Roo.Toolbar}
36640 getToolbar : function(){
36641 return this.toolbar;
36644 setActiveState : function(active)
36646 this.active = active;
36647 this.setActiveClass(active);
36649 if(this.fireEvent("deactivate", this) === false){
36654 this.fireEvent("activate", this);
36658 * Updates this panel's element
36659 * @param {String} content The new content
36660 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36662 setContent : function(content, loadScripts){
36663 this.el.update(content, loadScripts);
36666 ignoreResize : function(w, h){
36667 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36670 this.lastSize = {width: w, height: h};
36675 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36676 * @return {Roo.UpdateManager} The UpdateManager
36678 getUpdateManager : function(){
36679 return this.el.getUpdateManager();
36682 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36683 * @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:
36686 url: "your-url.php",
36687 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36688 callback: yourFunction,
36689 scope: yourObject, //(optional scope)
36692 text: "Loading...",
36697 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36698 * 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.
36699 * @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}
36700 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36701 * @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.
36702 * @return {Roo.ContentPanel} this
36705 var um = this.el.getUpdateManager();
36706 um.update.apply(um, arguments);
36712 * 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.
36713 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36714 * @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)
36715 * @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)
36716 * @return {Roo.UpdateManager} The UpdateManager
36718 setUrl : function(url, params, loadOnce){
36719 if(this.refreshDelegate){
36720 this.removeListener("activate", this.refreshDelegate);
36722 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36723 this.on("activate", this.refreshDelegate);
36724 return this.el.getUpdateManager();
36727 _handleRefresh : function(url, params, loadOnce){
36728 if(!loadOnce || !this.loaded){
36729 var updater = this.el.getUpdateManager();
36730 updater.update(url, params, this._setLoaded.createDelegate(this));
36734 _setLoaded : function(){
36735 this.loaded = true;
36739 * Returns this panel's id
36742 getId : function(){
36747 * Returns this panel's element - used by regiosn to add.
36748 * @return {Roo.Element}
36750 getEl : function(){
36751 return this.wrapEl || this.el;
36756 adjustForComponents : function(width, height)
36758 //Roo.log('adjustForComponents ');
36759 if(this.resizeEl != this.el){
36760 width -= this.el.getFrameWidth('lr');
36761 height -= this.el.getFrameWidth('tb');
36764 var te = this.toolbar.getEl();
36765 te.setWidth(width);
36766 height -= te.getHeight();
36769 var te = this.footer.getEl();
36770 te.setWidth(width);
36771 height -= te.getHeight();
36775 if(this.adjustments){
36776 width += this.adjustments[0];
36777 height += this.adjustments[1];
36779 return {"width": width, "height": height};
36782 setSize : function(width, height){
36783 if(this.fitToFrame && !this.ignoreResize(width, height)){
36784 if(this.fitContainer && this.resizeEl != this.el){
36785 this.el.setSize(width, height);
36787 var size = this.adjustForComponents(width, height);
36788 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36789 this.fireEvent('resize', this, size.width, size.height);
36794 * Returns this panel's title
36797 getTitle : function(){
36799 if (typeof(this.title) != 'object') {
36804 for (var k in this.title) {
36805 if (!this.title.hasOwnProperty(k)) {
36809 if (k.indexOf('-') >= 0) {
36810 var s = k.split('-');
36811 for (var i = 0; i<s.length; i++) {
36812 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36815 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36822 * Set this panel's title
36823 * @param {String} title
36825 setTitle : function(title){
36826 this.title = title;
36828 this.region.updatePanelTitle(this, title);
36833 * Returns true is this panel was configured to be closable
36834 * @return {Boolean}
36836 isClosable : function(){
36837 return this.closable;
36840 beforeSlide : function(){
36842 this.resizeEl.clip();
36845 afterSlide : function(){
36847 this.resizeEl.unclip();
36851 * Force a content refresh from the URL specified in the {@link #setUrl} method.
36852 * Will fail silently if the {@link #setUrl} method has not been called.
36853 * This does not activate the panel, just updates its content.
36855 refresh : function(){
36856 if(this.refreshDelegate){
36857 this.loaded = false;
36858 this.refreshDelegate();
36863 * Destroys this panel
36865 destroy : function(){
36866 this.el.removeAllListeners();
36867 var tempEl = document.createElement("span");
36868 tempEl.appendChild(this.el.dom);
36869 tempEl.innerHTML = "";
36875 * form - if the content panel contains a form - this is a reference to it.
36876 * @type {Roo.form.Form}
36880 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36881 * This contains a reference to it.
36887 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36897 * @param {Object} cfg Xtype definition of item to add.
36901 getChildContainer: function () {
36902 return this.getEl();
36907 var ret = new Roo.factory(cfg);
36912 if (cfg.xtype.match(/^Form$/)) {
36915 //if (this.footer) {
36916 // el = this.footer.container.insertSibling(false, 'before');
36918 el = this.el.createChild();
36921 this.form = new Roo.form.Form(cfg);
36924 if ( this.form.allItems.length) {
36925 this.form.render(el.dom);
36929 // should only have one of theses..
36930 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36931 // views.. should not be just added - used named prop 'view''
36933 cfg.el = this.el.appendChild(document.createElement("div"));
36936 var ret = new Roo.factory(cfg);
36938 ret.render && ret.render(false, ''); // render blank..
36948 * @class Roo.bootstrap.panel.Grid
36949 * @extends Roo.bootstrap.panel.Content
36951 * Create a new GridPanel.
36952 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36953 * @param {Object} config A the config object
36959 Roo.bootstrap.panel.Grid = function(config)
36963 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36964 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36966 config.el = this.wrapper;
36967 //this.el = this.wrapper;
36969 if (config.container) {
36970 // ctor'ed from a Border/panel.grid
36973 this.wrapper.setStyle("overflow", "hidden");
36974 this.wrapper.addClass('roo-grid-container');
36979 if(config.toolbar){
36980 var tool_el = this.wrapper.createChild();
36981 this.toolbar = Roo.factory(config.toolbar);
36983 if (config.toolbar.items) {
36984 ti = config.toolbar.items ;
36985 delete config.toolbar.items ;
36989 this.toolbar.render(tool_el);
36990 for(var i =0;i < ti.length;i++) {
36991 // Roo.log(['add child', items[i]]);
36992 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36994 this.toolbar.items = nitems;
36996 delete config.toolbar;
36999 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37000 config.grid.scrollBody = true;;
37001 config.grid.monitorWindowResize = false; // turn off autosizing
37002 config.grid.autoHeight = false;
37003 config.grid.autoWidth = false;
37005 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37007 if (config.background) {
37008 // render grid on panel activation (if panel background)
37009 this.on('activate', function(gp) {
37010 if (!gp.grid.rendered) {
37011 gp.grid.render(this.wrapper);
37012 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37017 this.grid.render(this.wrapper);
37018 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37021 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37022 // ??? needed ??? config.el = this.wrapper;
37027 // xtype created footer. - not sure if will work as we normally have to render first..
37028 if (this.footer && !this.footer.el && this.footer.xtype) {
37030 var ctr = this.grid.getView().getFooterPanel(true);
37031 this.footer.dataSource = this.grid.dataSource;
37032 this.footer = Roo.factory(this.footer, Roo);
37033 this.footer.render(ctr);
37043 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37044 getId : function(){
37045 return this.grid.id;
37049 * Returns the grid for this panel
37050 * @return {Roo.bootstrap.Table}
37052 getGrid : function(){
37056 setSize : function(width, height){
37057 if(!this.ignoreResize(width, height)){
37058 var grid = this.grid;
37059 var size = this.adjustForComponents(width, height);
37060 var gridel = grid.getGridEl();
37061 gridel.setSize(size.width, size.height);
37063 var thd = grid.getGridEl().select('thead',true).first();
37064 var tbd = grid.getGridEl().select('tbody', true).first();
37066 tbd.setSize(width, height - thd.getHeight());
37075 beforeSlide : function(){
37076 this.grid.getView().scroller.clip();
37079 afterSlide : function(){
37080 this.grid.getView().scroller.unclip();
37083 destroy : function(){
37084 this.grid.destroy();
37086 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37091 * @class Roo.bootstrap.panel.Nest
37092 * @extends Roo.bootstrap.panel.Content
37094 * Create a new Panel, that can contain a layout.Border.
37097 * @param {Roo.BorderLayout} layout The layout for this panel
37098 * @param {String/Object} config A string to set only the title or a config object
37100 Roo.bootstrap.panel.Nest = function(config)
37102 // construct with only one argument..
37103 /* FIXME - implement nicer consturctors
37104 if (layout.layout) {
37106 layout = config.layout;
37107 delete config.layout;
37109 if (layout.xtype && !layout.getEl) {
37110 // then layout needs constructing..
37111 layout = Roo.factory(layout, Roo);
37115 config.el = config.layout.getEl();
37117 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37119 config.layout.monitorWindowResize = false; // turn off autosizing
37120 this.layout = config.layout;
37121 this.layout.getEl().addClass("roo-layout-nested-layout");
37128 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37130 setSize : function(width, height){
37131 if(!this.ignoreResize(width, height)){
37132 var size = this.adjustForComponents(width, height);
37133 var el = this.layout.getEl();
37134 if (size.height < 1) {
37135 el.setWidth(size.width);
37137 el.setSize(size.width, size.height);
37139 var touch = el.dom.offsetWidth;
37140 this.layout.layout();
37141 // ie requires a double layout on the first pass
37142 if(Roo.isIE && !this.initialized){
37143 this.initialized = true;
37144 this.layout.layout();
37149 // activate all subpanels if not currently active..
37151 setActiveState : function(active){
37152 this.active = active;
37153 this.setActiveClass(active);
37156 this.fireEvent("deactivate", this);
37160 this.fireEvent("activate", this);
37161 // not sure if this should happen before or after..
37162 if (!this.layout) {
37163 return; // should not happen..
37166 for (var r in this.layout.regions) {
37167 reg = this.layout.getRegion(r);
37168 if (reg.getActivePanel()) {
37169 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37170 reg.setActivePanel(reg.getActivePanel());
37173 if (!reg.panels.length) {
37176 reg.showPanel(reg.getPanel(0));
37185 * Returns the nested BorderLayout for this panel
37186 * @return {Roo.BorderLayout}
37188 getLayout : function(){
37189 return this.layout;
37193 * Adds a xtype elements to the layout of the nested panel
37197 xtype : 'ContentPanel',
37204 xtype : 'NestedLayoutPanel',
37210 items : [ ... list of content panels or nested layout panels.. ]
37214 * @param {Object} cfg Xtype definition of item to add.
37216 addxtype : function(cfg) {
37217 return this.layout.addxtype(cfg);
37222 * Ext JS Library 1.1.1
37223 * Copyright(c) 2006-2007, Ext JS, LLC.
37225 * Originally Released Under LGPL - original licence link has changed is not relivant.
37228 * <script type="text/javascript">
37231 * @class Roo.TabPanel
37232 * @extends Roo.util.Observable
37233 * A lightweight tab container.
37237 // basic tabs 1, built from existing content
37238 var tabs = new Roo.TabPanel("tabs1");
37239 tabs.addTab("script", "View Script");
37240 tabs.addTab("markup", "View Markup");
37241 tabs.activate("script");
37243 // more advanced tabs, built from javascript
37244 var jtabs = new Roo.TabPanel("jtabs");
37245 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37247 // set up the UpdateManager
37248 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37249 var updater = tab2.getUpdateManager();
37250 updater.setDefaultUrl("ajax1.htm");
37251 tab2.on('activate', updater.refresh, updater, true);
37253 // Use setUrl for Ajax loading
37254 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37255 tab3.setUrl("ajax2.htm", null, true);
37258 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37261 jtabs.activate("jtabs-1");
37264 * Create a new TabPanel.
37265 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37266 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37268 Roo.bootstrap.panel.Tabs = function(config){
37270 * The container element for this TabPanel.
37271 * @type Roo.Element
37273 this.el = Roo.get(config.el);
37276 if(typeof config == "boolean"){
37277 this.tabPosition = config ? "bottom" : "top";
37279 Roo.apply(this, config);
37283 if(this.tabPosition == "bottom"){
37284 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37285 this.el.addClass("roo-tabs-bottom");
37287 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37288 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37289 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37291 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37293 if(this.tabPosition != "bottom"){
37294 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37295 * @type Roo.Element
37297 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37298 this.el.addClass("roo-tabs-top");
37302 this.bodyEl.setStyle("position", "relative");
37304 this.active = null;
37305 this.activateDelegate = this.activate.createDelegate(this);
37310 * Fires when the active tab changes
37311 * @param {Roo.TabPanel} this
37312 * @param {Roo.TabPanelItem} activePanel The new active tab
37316 * @event beforetabchange
37317 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37318 * @param {Roo.TabPanel} this
37319 * @param {Object} e Set cancel to true on this object to cancel the tab change
37320 * @param {Roo.TabPanelItem} tab The tab being changed to
37322 "beforetabchange" : true
37325 Roo.EventManager.onWindowResize(this.onResize, this);
37326 this.cpad = this.el.getPadding("lr");
37327 this.hiddenCount = 0;
37330 // toolbar on the tabbar support...
37331 if (this.toolbar) {
37332 alert("no toolbar support yet");
37333 this.toolbar = false;
37335 var tcfg = this.toolbar;
37336 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37337 this.toolbar = new Roo.Toolbar(tcfg);
37338 if (Roo.isSafari) {
37339 var tbl = tcfg.container.child('table', true);
37340 tbl.setAttribute('width', '100%');
37348 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37351 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37353 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37355 tabPosition : "top",
37357 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37359 currentTabWidth : 0,
37361 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37365 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37369 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37371 preferredTabWidth : 175,
37373 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37375 resizeTabs : false,
37377 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37379 monitorResize : true,
37381 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37386 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37387 * @param {String} id The id of the div to use <b>or create</b>
37388 * @param {String} text The text for the tab
37389 * @param {String} content (optional) Content to put in the TabPanelItem body
37390 * @param {Boolean} closable (optional) True to create a close icon on the tab
37391 * @return {Roo.TabPanelItem} The created TabPanelItem
37393 addTab : function(id, text, content, closable, tpl)
37395 var item = new Roo.bootstrap.panel.TabItem({
37399 closable : closable,
37402 this.addTabItem(item);
37404 item.setContent(content);
37410 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37411 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37412 * @return {Roo.TabPanelItem}
37414 getTab : function(id){
37415 return this.items[id];
37419 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37420 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37422 hideTab : function(id){
37423 var t = this.items[id];
37426 this.hiddenCount++;
37427 this.autoSizeTabs();
37432 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37433 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37435 unhideTab : function(id){
37436 var t = this.items[id];
37438 t.setHidden(false);
37439 this.hiddenCount--;
37440 this.autoSizeTabs();
37445 * Adds an existing {@link Roo.TabPanelItem}.
37446 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37448 addTabItem : function(item){
37449 this.items[item.id] = item;
37450 this.items.push(item);
37451 // if(this.resizeTabs){
37452 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37453 // this.autoSizeTabs();
37455 // item.autoSize();
37460 * Removes a {@link Roo.TabPanelItem}.
37461 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37463 removeTab : function(id){
37464 var items = this.items;
37465 var tab = items[id];
37466 if(!tab) { return; }
37467 var index = items.indexOf(tab);
37468 if(this.active == tab && items.length > 1){
37469 var newTab = this.getNextAvailable(index);
37474 this.stripEl.dom.removeChild(tab.pnode.dom);
37475 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37476 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37478 items.splice(index, 1);
37479 delete this.items[tab.id];
37480 tab.fireEvent("close", tab);
37481 tab.purgeListeners();
37482 this.autoSizeTabs();
37485 getNextAvailable : function(start){
37486 var items = this.items;
37488 // look for a next tab that will slide over to
37489 // replace the one being removed
37490 while(index < items.length){
37491 var item = items[++index];
37492 if(item && !item.isHidden()){
37496 // if one isn't found select the previous tab (on the left)
37499 var item = items[--index];
37500 if(item && !item.isHidden()){
37508 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37509 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37511 disableTab : function(id){
37512 var tab = this.items[id];
37513 if(tab && this.active != tab){
37519 * Enables a {@link Roo.TabPanelItem} that is disabled.
37520 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37522 enableTab : function(id){
37523 var tab = this.items[id];
37528 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37529 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37530 * @return {Roo.TabPanelItem} The TabPanelItem.
37532 activate : function(id){
37533 var tab = this.items[id];
37537 if(tab == this.active || tab.disabled){
37541 this.fireEvent("beforetabchange", this, e, tab);
37542 if(e.cancel !== true && !tab.disabled){
37544 this.active.hide();
37546 this.active = this.items[id];
37547 this.active.show();
37548 this.fireEvent("tabchange", this, this.active);
37554 * Gets the active {@link Roo.TabPanelItem}.
37555 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37557 getActiveTab : function(){
37558 return this.active;
37562 * Updates the tab body element to fit the height of the container element
37563 * for overflow scrolling
37564 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37566 syncHeight : function(targetHeight){
37567 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37568 var bm = this.bodyEl.getMargins();
37569 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37570 this.bodyEl.setHeight(newHeight);
37574 onResize : function(){
37575 if(this.monitorResize){
37576 this.autoSizeTabs();
37581 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37583 beginUpdate : function(){
37584 this.updating = true;
37588 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37590 endUpdate : function(){
37591 this.updating = false;
37592 this.autoSizeTabs();
37596 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37598 autoSizeTabs : function(){
37599 var count = this.items.length;
37600 var vcount = count - this.hiddenCount;
37601 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37604 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37605 var availWidth = Math.floor(w / vcount);
37606 var b = this.stripBody;
37607 if(b.getWidth() > w){
37608 var tabs = this.items;
37609 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37610 if(availWidth < this.minTabWidth){
37611 /*if(!this.sleft){ // incomplete scrolling code
37612 this.createScrollButtons();
37615 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37618 if(this.currentTabWidth < this.preferredTabWidth){
37619 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37625 * Returns the number of tabs in this TabPanel.
37628 getCount : function(){
37629 return this.items.length;
37633 * Resizes all the tabs to the passed width
37634 * @param {Number} The new width
37636 setTabWidth : function(width){
37637 this.currentTabWidth = width;
37638 for(var i = 0, len = this.items.length; i < len; i++) {
37639 if(!this.items[i].isHidden()) {
37640 this.items[i].setWidth(width);
37646 * Destroys this TabPanel
37647 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37649 destroy : function(removeEl){
37650 Roo.EventManager.removeResizeListener(this.onResize, this);
37651 for(var i = 0, len = this.items.length; i < len; i++){
37652 this.items[i].purgeListeners();
37654 if(removeEl === true){
37655 this.el.update("");
37660 createStrip : function(container)
37662 var strip = document.createElement("nav");
37663 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37664 container.appendChild(strip);
37668 createStripList : function(strip)
37670 // div wrapper for retard IE
37671 // returns the "tr" element.
37672 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37673 //'<div class="x-tabs-strip-wrap">'+
37674 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37675 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37676 return strip.firstChild; //.firstChild.firstChild.firstChild;
37678 createBody : function(container)
37680 var body = document.createElement("div");
37681 Roo.id(body, "tab-body");
37682 //Roo.fly(body).addClass("x-tabs-body");
37683 Roo.fly(body).addClass("tab-content");
37684 container.appendChild(body);
37687 createItemBody :function(bodyEl, id){
37688 var body = Roo.getDom(id);
37690 body = document.createElement("div");
37693 //Roo.fly(body).addClass("x-tabs-item-body");
37694 Roo.fly(body).addClass("tab-pane");
37695 bodyEl.insertBefore(body, bodyEl.firstChild);
37699 createStripElements : function(stripEl, text, closable, tpl)
37701 var td = document.createElement("li"); // was td..
37704 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37707 stripEl.appendChild(td);
37709 td.className = "x-tabs-closable";
37710 if(!this.closeTpl){
37711 this.closeTpl = new Roo.Template(
37712 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37713 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37714 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
37717 var el = this.closeTpl.overwrite(td, {"text": text});
37718 var close = el.getElementsByTagName("div")[0];
37719 var inner = el.getElementsByTagName("em")[0];
37720 return {"el": el, "close": close, "inner": inner};
37723 // not sure what this is..
37724 // if(!this.tabTpl){
37725 //this.tabTpl = new Roo.Template(
37726 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37727 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37729 // this.tabTpl = new Roo.Template(
37730 // '<a href="#">' +
37731 // '<span unselectable="on"' +
37732 // (this.disableTooltips ? '' : ' title="{text}"') +
37733 // ' >{text}</span></a>'
37739 var template = tpl || this.tabTpl || false;
37743 template = new Roo.Template(
37745 '<span unselectable="on"' +
37746 (this.disableTooltips ? '' : ' title="{text}"') +
37747 ' >{text}</span></a>'
37751 switch (typeof(template)) {
37755 template = new Roo.Template(template);
37761 var el = template.overwrite(td, {"text": text});
37763 var inner = el.getElementsByTagName("span")[0];
37765 return {"el": el, "inner": inner};
37773 * @class Roo.TabPanelItem
37774 * @extends Roo.util.Observable
37775 * Represents an individual item (tab plus body) in a TabPanel.
37776 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37777 * @param {String} id The id of this TabPanelItem
37778 * @param {String} text The text for the tab of this TabPanelItem
37779 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37781 Roo.bootstrap.panel.TabItem = function(config){
37783 * The {@link Roo.TabPanel} this TabPanelItem belongs to
37784 * @type Roo.TabPanel
37786 this.tabPanel = config.panel;
37788 * The id for this TabPanelItem
37791 this.id = config.id;
37793 this.disabled = false;
37795 this.text = config.text;
37797 this.loaded = false;
37798 this.closable = config.closable;
37801 * The body element for this TabPanelItem.
37802 * @type Roo.Element
37804 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37805 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37806 this.bodyEl.setStyle("display", "block");
37807 this.bodyEl.setStyle("zoom", "1");
37808 //this.hideAction();
37810 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37812 this.el = Roo.get(els.el);
37813 this.inner = Roo.get(els.inner, true);
37814 this.textEl = Roo.get(this.el.dom.firstChild, true);
37815 this.pnode = Roo.get(els.el.parentNode, true);
37816 // this.el.on("mousedown", this.onTabMouseDown, this);
37817 this.el.on("click", this.onTabClick, this);
37819 if(config.closable){
37820 var c = Roo.get(els.close, true);
37821 c.dom.title = this.closeText;
37822 c.addClassOnOver("close-over");
37823 c.on("click", this.closeClick, this);
37829 * Fires when this tab becomes the active tab.
37830 * @param {Roo.TabPanel} tabPanel The parent TabPanel
37831 * @param {Roo.TabPanelItem} this
37835 * @event beforeclose
37836 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37837 * @param {Roo.TabPanelItem} this
37838 * @param {Object} e Set cancel to true on this object to cancel the close.
37840 "beforeclose": true,
37843 * Fires when this tab is closed.
37844 * @param {Roo.TabPanelItem} this
37848 * @event deactivate
37849 * Fires when this tab is no longer the active tab.
37850 * @param {Roo.TabPanel} tabPanel The parent TabPanel
37851 * @param {Roo.TabPanelItem} this
37853 "deactivate" : true
37855 this.hidden = false;
37857 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37860 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37862 purgeListeners : function(){
37863 Roo.util.Observable.prototype.purgeListeners.call(this);
37864 this.el.removeAllListeners();
37867 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37870 this.pnode.addClass("active");
37873 this.tabPanel.stripWrap.repaint();
37875 this.fireEvent("activate", this.tabPanel, this);
37879 * Returns true if this tab is the active tab.
37880 * @return {Boolean}
37882 isActive : function(){
37883 return this.tabPanel.getActiveTab() == this;
37887 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37890 this.pnode.removeClass("active");
37892 this.fireEvent("deactivate", this.tabPanel, this);
37895 hideAction : function(){
37896 this.bodyEl.hide();
37897 this.bodyEl.setStyle("position", "absolute");
37898 this.bodyEl.setLeft("-20000px");
37899 this.bodyEl.setTop("-20000px");
37902 showAction : function(){
37903 this.bodyEl.setStyle("position", "relative");
37904 this.bodyEl.setTop("");
37905 this.bodyEl.setLeft("");
37906 this.bodyEl.show();
37910 * Set the tooltip for the tab.
37911 * @param {String} tooltip The tab's tooltip
37913 setTooltip : function(text){
37914 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37915 this.textEl.dom.qtip = text;
37916 this.textEl.dom.removeAttribute('title');
37918 this.textEl.dom.title = text;
37922 onTabClick : function(e){
37923 e.preventDefault();
37924 this.tabPanel.activate(this.id);
37927 onTabMouseDown : function(e){
37928 e.preventDefault();
37929 this.tabPanel.activate(this.id);
37932 getWidth : function(){
37933 return this.inner.getWidth();
37936 setWidth : function(width){
37937 var iwidth = width - this.pnode.getPadding("lr");
37938 this.inner.setWidth(iwidth);
37939 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37940 this.pnode.setWidth(width);
37944 * Show or hide the tab
37945 * @param {Boolean} hidden True to hide or false to show.
37947 setHidden : function(hidden){
37948 this.hidden = hidden;
37949 this.pnode.setStyle("display", hidden ? "none" : "");
37953 * Returns true if this tab is "hidden"
37954 * @return {Boolean}
37956 isHidden : function(){
37957 return this.hidden;
37961 * Returns the text for this tab
37964 getText : function(){
37968 autoSize : function(){
37969 //this.el.beginMeasure();
37970 this.textEl.setWidth(1);
37972 * #2804 [new] Tabs in Roojs
37973 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37975 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37976 //this.el.endMeasure();
37980 * Sets the text for the tab (Note: this also sets the tooltip text)
37981 * @param {String} text The tab's text and tooltip
37983 setText : function(text){
37985 this.textEl.update(text);
37986 this.setTooltip(text);
37987 //if(!this.tabPanel.resizeTabs){
37988 // this.autoSize();
37992 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37994 activate : function(){
37995 this.tabPanel.activate(this.id);
37999 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38001 disable : function(){
38002 if(this.tabPanel.active != this){
38003 this.disabled = true;
38004 this.pnode.addClass("disabled");
38009 * Enables this TabPanelItem if it was previously disabled.
38011 enable : function(){
38012 this.disabled = false;
38013 this.pnode.removeClass("disabled");
38017 * Sets the content for this TabPanelItem.
38018 * @param {String} content The content
38019 * @param {Boolean} loadScripts true to look for and load scripts
38021 setContent : function(content, loadScripts){
38022 this.bodyEl.update(content, loadScripts);
38026 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38027 * @return {Roo.UpdateManager} The UpdateManager
38029 getUpdateManager : function(){
38030 return this.bodyEl.getUpdateManager();
38034 * Set a URL to be used to load the content for this TabPanelItem.
38035 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38036 * @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)
38037 * @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)
38038 * @return {Roo.UpdateManager} The UpdateManager
38040 setUrl : function(url, params, loadOnce){
38041 if(this.refreshDelegate){
38042 this.un('activate', this.refreshDelegate);
38044 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38045 this.on("activate", this.refreshDelegate);
38046 return this.bodyEl.getUpdateManager();
38050 _handleRefresh : function(url, params, loadOnce){
38051 if(!loadOnce || !this.loaded){
38052 var updater = this.bodyEl.getUpdateManager();
38053 updater.update(url, params, this._setLoaded.createDelegate(this));
38058 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38059 * Will fail silently if the setUrl method has not been called.
38060 * This does not activate the panel, just updates its content.
38062 refresh : function(){
38063 if(this.refreshDelegate){
38064 this.loaded = false;
38065 this.refreshDelegate();
38070 _setLoaded : function(){
38071 this.loaded = true;
38075 closeClick : function(e){
38078 this.fireEvent("beforeclose", this, o);
38079 if(o.cancel !== true){
38080 this.tabPanel.removeTab(this.id);
38084 * The text displayed in the tooltip for the close icon.
38087 closeText : "Close this tab"
38090 * This script refer to:
38091 * Title: International Telephone Input
38092 * Author: Jack O'Connor
38093 * Code version: v12.1.12
38094 * Availability: https://github.com/jackocnr/intl-tel-input.git
38097 Roo.bootstrap.PhoneInputData = function() {
38100 "Afghanistan (افغانستان)",
38105 "Albania (Shqipëri)",
38110 "Algeria (الجزائر)",
38135 "Antigua and Barbuda",
38145 "Armenia (Հայաստան)",
38161 "Austria (Österreich)",
38166 "Azerbaijan (Azərbaycan)",
38176 "Bahrain (البحرين)",
38181 "Bangladesh (বাংলাদেশ)",
38191 "Belarus (Беларусь)",
38196 "Belgium (België)",
38226 "Bosnia and Herzegovina (Босна и Херцеговина)",
38241 "British Indian Ocean Territory",
38246 "British Virgin Islands",
38256 "Bulgaria (България)",
38266 "Burundi (Uburundi)",
38271 "Cambodia (កម្ពុជា)",
38276 "Cameroon (Cameroun)",
38285 ["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"]
38288 "Cape Verde (Kabu Verdi)",
38293 "Caribbean Netherlands",
38304 "Central African Republic (République centrafricaine)",
38324 "Christmas Island",
38330 "Cocos (Keeling) Islands",
38341 "Comoros (جزر القمر)",
38346 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38351 "Congo (Republic) (Congo-Brazzaville)",
38371 "Croatia (Hrvatska)",
38392 "Czech Republic (Česká republika)",
38397 "Denmark (Danmark)",
38412 "Dominican Republic (República Dominicana)",
38416 ["809", "829", "849"]
38434 "Equatorial Guinea (Guinea Ecuatorial)",
38454 "Falkland Islands (Islas Malvinas)",
38459 "Faroe Islands (Føroyar)",
38480 "French Guiana (Guyane française)",
38485 "French Polynesia (Polynésie française)",
38500 "Georgia (საქართველო)",
38505 "Germany (Deutschland)",
38525 "Greenland (Kalaallit Nunaat)",
38562 "Guinea-Bissau (Guiné Bissau)",
38587 "Hungary (Magyarország)",
38592 "Iceland (Ísland)",
38612 "Iraq (العراق)",
38628 "Israel (ישראל)",
38655 "Jordan (الأردن)",
38660 "Kazakhstan (Казахстан)",
38681 "Kuwait (الكويت)",
38686 "Kyrgyzstan (Кыргызстан)",
38696 "Latvia (Latvija)",
38701 "Lebanon (لبنان)",
38716 "Libya (ليبيا)",
38726 "Lithuania (Lietuva)",
38741 "Macedonia (FYROM) (Македонија)",
38746 "Madagascar (Madagasikara)",
38776 "Marshall Islands",
38786 "Mauritania (موريتانيا)",
38791 "Mauritius (Moris)",
38812 "Moldova (Republica Moldova)",
38822 "Mongolia (Монгол)",
38827 "Montenegro (Crna Gora)",
38837 "Morocco (المغرب)",
38843 "Mozambique (Moçambique)",
38848 "Myanmar (Burma) (မြန်မာ)",
38853 "Namibia (Namibië)",
38868 "Netherlands (Nederland)",
38873 "New Caledonia (Nouvelle-Calédonie)",
38908 "North Korea (조선 민주주의 인민 공화국)",
38913 "Northern Mariana Islands",
38929 "Pakistan (پاکستان)",
38939 "Palestine (فلسطين)",
38949 "Papua New Guinea",
38991 "Réunion (La Réunion)",
38997 "Romania (România)",
39013 "Saint Barthélemy",
39024 "Saint Kitts and Nevis",
39034 "Saint Martin (Saint-Martin (partie française))",
39040 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39045 "Saint Vincent and the Grenadines",
39060 "São Tomé and Príncipe (São Tomé e Príncipe)",
39065 "Saudi Arabia (المملكة العربية السعودية)",
39070 "Senegal (Sénégal)",
39100 "Slovakia (Slovensko)",
39105 "Slovenia (Slovenija)",
39115 "Somalia (Soomaaliya)",
39125 "South Korea (대한민국)",
39130 "South Sudan (جنوب السودان)",
39140 "Sri Lanka (ශ්රී ලංකාව)",
39145 "Sudan (السودان)",
39155 "Svalbard and Jan Mayen",
39166 "Sweden (Sverige)",
39171 "Switzerland (Schweiz)",
39176 "Syria (سوريا)",
39221 "Trinidad and Tobago",
39226 "Tunisia (تونس)",
39231 "Turkey (Türkiye)",
39241 "Turks and Caicos Islands",
39251 "U.S. Virgin Islands",
39261 "Ukraine (Україна)",
39266 "United Arab Emirates (الإمارات العربية المتحدة)",
39288 "Uzbekistan (Oʻzbekiston)",
39298 "Vatican City (Città del Vaticano)",
39309 "Vietnam (Việt Nam)",
39314 "Wallis and Futuna (Wallis-et-Futuna)",
39319 "Western Sahara (الصحراء الغربية)",
39325 "Yemen (اليمن)",
39349 * This script refer to:
39350 * Title: International Telephone Input
39351 * Author: Jack O'Connor
39352 * Code version: v12.1.12
39353 * Availability: https://github.com/jackocnr/intl-tel-input.git
39357 * @class Roo.bootstrap.PhoneInput
39358 * @extends Roo.bootstrap.TriggerField
39359 * An input with International dial-code selection
39361 * @cfg {String} defaultDialCode default '+852'
39362 * @cfg {Array} preferedCountries default []
39365 * Create a new PhoneInput.
39366 * @param {Object} config Configuration options
39369 Roo.bootstrap.PhoneInput = function(config) {
39370 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39373 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39375 listWidth: undefined,
39377 selectedClass: 'active',
39379 invalidClass : "has-warning",
39381 validClass: 'has-success',
39383 allowed: '0123456789',
39386 * @cfg {String} defaultDialCode The default dial code when initializing the input
39388 defaultDialCode: '+852',
39391 * @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
39393 preferedCountries: false,
39395 getAutoCreate : function()
39397 var data = Roo.bootstrap.PhoneInputData();
39398 var align = this.labelAlign || this.parentLabelAlign();
39401 this.allCountries = [];
39402 this.dialCodeMapping = [];
39404 for (var i = 0; i < data.length; i++) {
39406 this.allCountries[i] = {
39410 priority: c[3] || 0,
39411 areaCodes: c[4] || null
39413 this.dialCodeMapping[c[2]] = {
39416 priority: c[3] || 0,
39417 areaCodes: c[4] || null
39429 cls : 'form-control tel-input',
39430 autocomplete: 'new-password'
39433 var hiddenInput = {
39436 cls: 'hidden-tel-input'
39440 hiddenInput.name = this.name;
39443 if (this.disabled) {
39444 input.disabled = true;
39447 var flag_container = {
39464 cls: this.hasFeedback ? 'has-feedback' : '',
39470 cls: 'dial-code-holder',
39477 cls: 'roo-select2-container input-group',
39484 if (this.fieldLabel.length) {
39487 tooltip: 'This field is required'
39493 cls: 'control-label',
39499 html: this.fieldLabel
39502 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39508 if(this.indicatorpos == 'right') {
39509 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39516 if(align == 'left') {
39524 if(this.labelWidth > 12){
39525 label.style = "width: " + this.labelWidth + 'px';
39527 if(this.labelWidth < 13 && this.labelmd == 0){
39528 this.labelmd = this.labelWidth;
39530 if(this.labellg > 0){
39531 label.cls += ' col-lg-' + this.labellg;
39532 input.cls += ' col-lg-' + (12 - this.labellg);
39534 if(this.labelmd > 0){
39535 label.cls += ' col-md-' + this.labelmd;
39536 container.cls += ' col-md-' + (12 - this.labelmd);
39538 if(this.labelsm > 0){
39539 label.cls += ' col-sm-' + this.labelsm;
39540 container.cls += ' col-sm-' + (12 - this.labelsm);
39542 if(this.labelxs > 0){
39543 label.cls += ' col-xs-' + this.labelxs;
39544 container.cls += ' col-xs-' + (12 - this.labelxs);
39554 var settings = this;
39556 ['xs','sm','md','lg'].map(function(size){
39557 if (settings[size]) {
39558 cfg.cls += ' col-' + size + '-' + settings[size];
39562 this.store = new Roo.data.Store({
39563 proxy : new Roo.data.MemoryProxy({}),
39564 reader : new Roo.data.JsonReader({
39575 'name' : 'dialCode',
39579 'name' : 'priority',
39583 'name' : 'areaCodes',
39590 if(!this.preferedCountries) {
39591 this.preferedCountries = [
39598 var p = this.preferedCountries.reverse();
39601 for (var i = 0; i < p.length; i++) {
39602 for (var j = 0; j < this.allCountries.length; j++) {
39603 if(this.allCountries[j].iso2 == p[i]) {
39604 var t = this.allCountries[j];
39605 this.allCountries.splice(j,1);
39606 this.allCountries.unshift(t);
39612 this.store.proxy.data = {
39614 data: this.allCountries
39620 initEvents : function()
39623 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39625 this.indicator = this.indicatorEl();
39626 this.flag = this.flagEl();
39627 this.dialCodeHolder = this.dialCodeHolderEl();
39629 this.trigger = this.el.select('div.flag-box',true).first();
39630 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39635 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39636 _this.list.setWidth(lw);
39639 this.list.on('mouseover', this.onViewOver, this);
39640 this.list.on('mousemove', this.onViewMove, this);
39641 this.inputEl().on("keyup", this.onKeyUp, this);
39643 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39645 this.view = new Roo.View(this.list, this.tpl, {
39646 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39649 this.view.on('click', this.onViewClick, this);
39650 this.setValue(this.defaultDialCode);
39653 onTriggerClick : function(e)
39655 Roo.log('trigger click');
39660 if(this.isExpanded()){
39662 this.hasFocus = false;
39664 this.store.load({});
39665 this.hasFocus = true;
39670 isExpanded : function()
39672 return this.list.isVisible();
39675 collapse : function()
39677 if(!this.isExpanded()){
39681 Roo.get(document).un('mousedown', this.collapseIf, this);
39682 Roo.get(document).un('mousewheel', this.collapseIf, this);
39683 this.fireEvent('collapse', this);
39687 expand : function()
39691 if(this.isExpanded() || !this.hasFocus){
39695 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39696 this.list.setWidth(lw);
39699 this.restrictHeight();
39701 Roo.get(document).on('mousedown', this.collapseIf, this);
39702 Roo.get(document).on('mousewheel', this.collapseIf, this);
39704 this.fireEvent('expand', this);
39707 restrictHeight : function()
39709 this.list.alignTo(this.inputEl(), this.listAlign);
39710 this.list.alignTo(this.inputEl(), this.listAlign);
39713 onViewOver : function(e, t)
39715 if(this.inKeyMode){
39718 var item = this.view.findItemFromChild(t);
39721 var index = this.view.indexOf(item);
39722 this.select(index, false);
39727 onViewClick : function(view, doFocus, el, e)
39729 var index = this.view.getSelectedIndexes()[0];
39731 var r = this.store.getAt(index);
39734 this.onSelect(r, index);
39736 if(doFocus !== false && !this.blockFocus){
39737 this.inputEl().focus();
39741 onViewMove : function(e, t)
39743 this.inKeyMode = false;
39746 select : function(index, scrollIntoView)
39748 this.selectedIndex = index;
39749 this.view.select(index);
39750 if(scrollIntoView !== false){
39751 var el = this.view.getNode(index);
39753 this.list.scrollChildIntoView(el, false);
39758 createList : function()
39760 this.list = Roo.get(document.body).createChild({
39762 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39763 style: 'display:none'
39765 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39768 collapseIf : function(e)
39770 var in_combo = e.within(this.el);
39771 var in_list = e.within(this.list);
39772 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39774 if (in_combo || in_list || is_list) {
39780 onSelect : function(record, index)
39782 if(this.fireEvent('beforeselect', this, record, index) !== false){
39784 this.setFlagClass(record.data.iso2);
39785 this.setDialCode(record.data.dialCode);
39786 this.hasFocus = false;
39788 this.fireEvent('select', this, record, index);
39792 flagEl : function()
39794 var flag = this.el.select('div.flag',true).first();
39801 dialCodeHolderEl : function()
39803 var d = this.el.select('input.dial-code-holder',true).first();
39810 setDialCode : function(v)
39812 this.dialCodeHolder.dom.value = '+'+v;
39815 setFlagClass : function(n)
39817 this.flag.dom.className = 'flag '+n;
39820 getValue : function()
39822 var v = this.inputEl().getValue();
39823 if(this.dialCodeHolder) {
39824 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39829 setValue : function(v)
39831 var d = this.getDialCode(v);
39833 //invalid dial code
39834 if(v.length == 0 || !d || d.length == 0) {
39836 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39837 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39843 this.setFlagClass(this.dialCodeMapping[d].iso2);
39844 this.setDialCode(d);
39845 this.inputEl().dom.value = v.replace('+'+d,'');
39846 this.hiddenEl().dom.value = this.getValue();
39851 getDialCode : function(v = '')
39853 if (v.length == 0) {
39854 return this.dialCodeHolder.dom.value;
39858 if (v.charAt(0) != "+") {
39861 var numericChars = "";
39862 for (var i = 1; i < v.length; i++) {
39863 var c = v.charAt(i);
39866 if (this.dialCodeMapping[numericChars]) {
39867 dialCode = v.substr(1, i);
39869 if (numericChars.length == 4) {
39879 this.setValue(this.defaultDialCode);
39883 hiddenEl : function()
39885 return this.el.select('input.hidden-tel-input',true).first();
39888 onKeyUp : function(e){
39890 var k = e.getKey();
39891 var c = e.getCharCode();
39894 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39895 this.allowed.indexOf(String.fromCharCode(c)) === -1
39900 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39903 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39907 this.setValue(this.getValue());
39912 * @class Roo.bootstrap.MoneyField
39913 * @extends Roo.bootstrap.ComboBox
39914 * Bootstrap MoneyField class
39917 * Create a new MoneyField.
39918 * @param {Object} config Configuration options
39921 Roo.bootstrap.MoneyField = function(config) {
39923 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39927 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39930 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39932 allowDecimals : true,
39934 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39936 decimalSeparator : ".",
39938 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39940 decimalPrecision : 2,
39942 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39944 allowNegative : true,
39946 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39948 minValue : Number.NEGATIVE_INFINITY,
39950 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39952 maxValue : Number.MAX_VALUE,
39954 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39956 minText : "The minimum value for this field is {0}",
39958 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39960 maxText : "The maximum value for this field is {0}",
39962 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
39963 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39965 nanText : "{0} is not a valid number",
39967 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
39971 * @cfg {String} defaults currency of the MoneyField
39972 * value should be in lkey
39974 defaultCurrency : false,
39984 getAutoCreate : function()
39986 var align = this.labelAlign || this.parentLabelAlign();
39998 cls : 'form-control roo-money-amount-input',
39999 autocomplete: 'new-password'
40003 input.name = this.name;
40006 if (this.disabled) {
40007 input.disabled = true;
40010 var clg = 12 - this.inputlg;
40011 var cmd = 12 - this.inputmd;
40012 var csm = 12 - this.inputsm;
40013 var cxs = 12 - this.inputxs;
40017 cls : 'row roo-money-field',
40021 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40025 cls: 'roo-select2-container input-group',
40029 cls : 'form-control roo-money-currency-input',
40030 autocomplete: 'new-password',
40032 name : this.currencyName
40036 cls : 'input-group-addon',
40050 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40054 cls: this.hasFeedback ? 'has-feedback' : '',
40065 if (this.fieldLabel.length) {
40068 tooltip: 'This field is required'
40074 cls: 'control-label',
40080 html: this.fieldLabel
40083 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40089 if(this.indicatorpos == 'right') {
40090 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40097 if(align == 'left') {
40105 if(this.labelWidth > 12){
40106 label.style = "width: " + this.labelWidth + 'px';
40108 if(this.labelWidth < 13 && this.labelmd == 0){
40109 this.labelmd = this.labelWidth;
40111 if(this.labellg > 0){
40112 label.cls += ' col-lg-' + this.labellg;
40113 input.cls += ' col-lg-' + (12 - this.labellg);
40115 if(this.labelmd > 0){
40116 label.cls += ' col-md-' + this.labelmd;
40117 container.cls += ' col-md-' + (12 - this.labelmd);
40119 if(this.labelsm > 0){
40120 label.cls += ' col-sm-' + this.labelsm;
40121 container.cls += ' col-sm-' + (12 - this.labelsm);
40123 if(this.labelxs > 0){
40124 label.cls += ' col-xs-' + this.labelxs;
40125 container.cls += ' col-xs-' + (12 - this.labelxs);
40135 var settings = this;
40137 ['xs','sm','md','lg'].map(function(size){
40138 if (settings[size]) {
40139 cfg.cls += ' col-' + size + '-' + settings[size];
40147 initEvents : function()
40149 this.indicator = this.indicatorEl();
40151 this.initCurrencyEvent();
40153 this.initNumberEvent();
40157 initCurrencyEvent : function()
40160 throw "can not find store for combo";
40163 this.store = Roo.factory(this.store, Roo.data);
40164 this.store.parent = this;
40168 this.triggerEl = this.el.select('.input-group-addon', true).first();
40170 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40175 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40176 _this.list.setWidth(lw);
40179 this.list.on('mouseover', this.onViewOver, this);
40180 this.list.on('mousemove', this.onViewMove, this);
40181 this.list.on('scroll', this.onViewScroll, this);
40184 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40187 this.view = new Roo.View(this.list, this.tpl, {
40188 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40191 this.view.on('click', this.onViewClick, this);
40193 this.store.on('beforeload', this.onBeforeLoad, this);
40194 this.store.on('load', this.onLoad, this);
40195 this.store.on('loadexception', this.onLoadException, this);
40197 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40198 "up" : function(e){
40199 this.inKeyMode = true;
40203 "down" : function(e){
40204 if(!this.isExpanded()){
40205 this.onTriggerClick();
40207 this.inKeyMode = true;
40212 "enter" : function(e){
40215 if(this.fireEvent("specialkey", this, e)){
40216 this.onViewClick(false);
40222 "esc" : function(e){
40226 "tab" : function(e){
40229 if(this.fireEvent("specialkey", this, e)){
40230 this.onViewClick(false);
40238 doRelay : function(foo, bar, hname){
40239 if(hname == 'down' || this.scope.isExpanded()){
40240 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40248 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40252 initNumberEvent : function(e)
40254 this.inputEl().on("keydown" , this.fireKey, this);
40255 this.inputEl().on("focus", this.onFocus, this);
40256 this.inputEl().on("blur", this.onBlur, this);
40258 this.inputEl().relayEvent('keyup', this);
40260 if(this.indicator){
40261 this.indicator.addClass('invisible');
40264 this.originalValue = this.getValue();
40266 if(this.validationEvent == 'keyup'){
40267 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40268 this.inputEl().on('keyup', this.filterValidation, this);
40270 else if(this.validationEvent !== false){
40271 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40274 if(this.selectOnFocus){
40275 this.on("focus", this.preFocus, this);
40278 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40279 this.inputEl().on("keypress", this.filterKeys, this);
40281 this.inputEl().relayEvent('keypress', this);
40284 var allowed = "0123456789";
40286 if(this.allowDecimals){
40287 allowed += this.decimalSeparator;
40290 if(this.allowNegative){
40294 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40296 var keyPress = function(e){
40298 var k = e.getKey();
40300 var c = e.getCharCode();
40303 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40304 allowed.indexOf(String.fromCharCode(c)) === -1
40310 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40314 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40319 this.inputEl().on("keypress", keyPress, this);
40323 onTriggerClick : function(e)
40330 this.loadNext = false;
40332 if(this.isExpanded()){
40337 this.hasFocus = true;
40339 if(this.triggerAction == 'all') {
40340 this.doQuery(this.allQuery, true);
40344 this.doQuery(this.getRawValue());
40347 getCurrency : function()
40349 var v = this.currencyEl().getValue();
40354 restrictHeight : function()
40356 this.list.alignTo(this.currencyEl(), this.listAlign);
40357 this.list.alignTo(this.currencyEl(), this.listAlign);
40360 onViewClick : function(view, doFocus, el, e)
40362 var index = this.view.getSelectedIndexes()[0];
40364 var r = this.store.getAt(index);
40367 this.onSelect(r, index);
40371 onSelect : function(record, index){
40373 if(this.fireEvent('beforeselect', this, record, index) !== false){
40375 this.setFromCurrencyData(index > -1 ? record.data : false);
40379 this.fireEvent('select', this, record, index);
40383 setFromCurrencyData : function(o)
40387 this.lastCurrency = o;
40389 if (this.currencyField) {
40390 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40392 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40395 this.lastSelectionText = currency;
40397 Roo.log(o['currencyField']);
40399 if(o.length * 1 == 0 && this.defaultCurrency) {
40400 Roo.log('setting currency');
40401 this.setCurrency(this.defaultCurrency);
40404 this.setCurrency(currency);
40407 setFromData : function(o)
40411 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40413 this.setFromCurrencyData(c);
40418 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40420 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40423 this.setValue(value);
40427 setCurrency : function(v)
40429 this.currencyValue = v;
40432 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40437 setValue : function(v)
40439 v = this.fixPrecision(v);
40441 v = String(v).replace(".", this.decimalSeparator);
40446 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40451 getRawValue : function()
40453 var v = this.inputEl().getValue();
40458 getValue : function()
40460 return this.fixPrecision(this.parseValue(this.getRawValue()));
40463 parseValue : function(value)
40465 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40466 return isNaN(value) ? '' : value;
40469 fixPrecision : function(value)
40471 var nan = isNaN(value);
40473 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40474 return nan ? '' : value;
40477 return parseFloat(value).toFixed(this.decimalPrecision);
40480 decimalPrecisionFcn : function(v)
40482 return Math.floor(v);
40485 validateValue : function(value)
40487 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40491 var num = this.parseValue(value);
40494 this.markInvalid(String.format(this.nanText, value));
40498 if(num < this.minValue){
40499 this.markInvalid(String.format(this.minText, this.minValue));
40503 if(num > this.maxValue){
40504 this.markInvalid(String.format(this.maxText, this.maxValue));
40511 validate : function()
40513 if(this.disabled || this.allowBlank){
40518 var currency = this.getCurrency();
40520 if(this.validateValue(this.getRawValue()) && currency.length){
40525 this.markInvalid();
40529 getName: function()
40534 beforeBlur : function()
40540 var v = this.parseValue(this.getRawValue());
40547 onBlur : function()
40551 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40552 //this.el.removeClass(this.focusClass);
40555 this.hasFocus = false;
40557 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40561 var v = this.getValue();
40563 if(String(v) !== String(this.startValue)){
40564 this.fireEvent('change', this, v, this.startValue);
40567 this.fireEvent("blur", this);
40570 inputEl : function()
40572 return this.el.select('.roo-money-amount-input', true).first();
40575 currencyEl : function()
40577 return this.el.select('.roo-money-currency-input', true).first();