4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
21 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
24 * Do not use directly - it does not do anything..
25 * @param {Object} config The config object
30 Roo.bootstrap.Component = function(config){
31 Roo.bootstrap.Component.superclass.constructor.call(this, config);
35 * @event childrenrendered
36 * Fires when the children have been rendered..
37 * @param {Roo.bootstrap.Component} this
39 "childrenrendered" : true
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
51 allowDomMove : false, // to stop relocations in parent onRender...
61 * Initialize Events for the element
63 initEvents : function() { },
69 can_build_overlaid : true,
71 container_method : false,
78 // returns the parent component..
79 return Roo.ComponentMgr.get(this.parentId)
85 onRender : function(ct, position)
87 // Roo.log("Call onRender: " + this.xtype);
89 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
92 if (this.el.attr('xtype')) {
93 this.el.attr('xtypex', this.el.attr('xtype'));
94 this.el.dom.removeAttribute('xtype');
104 var cfg = Roo.apply({}, this.getAutoCreate());
106 cfg.id = this.id || Roo.id();
108 // fill in the extra attributes
109 if (this.xattr && typeof(this.xattr) =='object') {
110 for (var i in this.xattr) {
111 cfg[i] = this.xattr[i];
116 cfg.dataId = this.dataId;
120 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
123 if (this.style) { // fixme needs to support more complex style data.
124 cfg.style = this.style;
128 cfg.name = this.name;
131 this.el = ct.createChild(cfg, position);
134 this.tooltipEl().attr('tooltip', this.tooltip);
137 if(this.tabIndex !== undefined){
138 this.el.dom.setAttribute('tabIndex', this.tabIndex);
145 * Fetch the element to add children to
146 * @return {Roo.Element} defaults to this.el
148 getChildContainer : function()
153 * Fetch the element to display the tooltip on.
154 * @return {Roo.Element} defaults to this.el
156 tooltipEl : function()
161 addxtype : function(tree,cntr)
165 cn = Roo.factory(tree);
166 //Roo.log(['addxtype', cn]);
168 cn.parentType = this.xtype; //??
169 cn.parentId = this.id;
171 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172 if (typeof(cn.container_method) == 'string') {
173 cntr = cn.container_method;
177 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
179 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
181 var build_from_html = Roo.XComponent.build_from_html;
183 var is_body = (tree.xtype == 'Body') ;
185 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
187 var self_cntr_el = Roo.get(this[cntr](false));
189 // do not try and build conditional elements
190 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
194 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196 return this.addxtypeChild(tree,cntr, is_body);
199 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
202 return this.addxtypeChild(Roo.apply({}, tree),cntr);
205 Roo.log('skipping render');
211 if (!build_from_html) {
215 // this i think handles overlaying multiple children of the same type
216 // with the sam eelement.. - which might be buggy..
218 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
224 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
228 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
235 addxtypeChild : function (tree, cntr, is_body)
237 Roo.debug && Roo.log('addxtypeChild:' + cntr);
239 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
242 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243 (typeof(tree['flexy:foreach']) != 'undefined');
247 skip_children = false;
248 // render the element if it's not BODY.
251 // if parent was disabled, then do not try and create the children..
252 if(!this[cntr](true)){
257 cn = Roo.factory(tree);
259 cn.parentType = this.xtype; //??
260 cn.parentId = this.id;
262 var build_from_html = Roo.XComponent.build_from_html;
265 // does the container contain child eleemnts with 'xtype' attributes.
266 // that match this xtype..
267 // note - when we render we create these as well..
268 // so we should check to see if body has xtype set.
269 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
271 var self_cntr_el = Roo.get(this[cntr](false));
272 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
274 //Roo.log(Roo.XComponent.build_from_html);
275 //Roo.log("got echild:");
278 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279 // and are not displayed -this causes this to use up the wrong element when matching.
280 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
283 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
290 //echild.dom.removeAttribute('xtype');
292 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293 Roo.debug && Roo.log(self_cntr_el);
294 Roo.debug && Roo.log(echild);
295 Roo.debug && Roo.log(cn);
301 // if object has flexy:if - then it may or may not be rendered.
302 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
303 // skip a flexy if element.
304 Roo.debug && Roo.log('skipping render');
305 Roo.debug && Roo.log(tree);
307 Roo.debug && Roo.log('skipping all children');
308 skip_children = true;
313 // actually if flexy:foreach is found, we really want to create
314 // multiple copies here...
316 //Roo.log(this[cntr]());
317 // some elements do not have render methods.. like the layouts...
319 if(this[cntr](true) === false){
324 cn.render && cn.render(this[cntr](true));
327 // then add the element..
334 if (typeof (tree.menu) != 'undefined') {
335 tree.menu.parentType = cn.xtype;
336 tree.menu.triggerEl = cn.el;
337 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
341 if (!tree.items || !tree.items.length) {
343 //Roo.log(["no children", this]);
348 var items = tree.items;
351 //Roo.log(items.length);
353 if (!skip_children) {
354 for(var i =0;i < items.length;i++) {
355 // Roo.log(['add child', items[i]]);
356 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
362 //Roo.log("fire childrenrendered");
364 cn.fireEvent('childrenrendered', this);
370 * Set the element that will be used to show or hide
372 setVisibilityEl : function(el)
374 this.visibilityEl = el;
378 * Get the element that will be used to show or hide
380 getVisibilityEl : function()
382 if (typeof(this.visibilityEl) == 'object') {
383 return this.visibilityEl;
386 if (typeof(this.visibilityEl) == 'string') {
387 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
394 * Show a component - removes 'hidden' class
398 if(!this.getVisibilityEl()){
402 this.getVisibilityEl().removeClass('hidden');
404 this.fireEvent('show', this);
409 * Hide a component - adds 'hidden' class
413 if(!this.getVisibilityEl()){
417 this.getVisibilityEl().addClass('hidden');
419 this.fireEvent('hide', this);
432 * @class Roo.bootstrap.Body
433 * @extends Roo.bootstrap.Component
434 * Bootstrap Body class
438 * @param {Object} config The config object
441 Roo.bootstrap.Body = function(config){
443 config = config || {};
445 Roo.bootstrap.Body.superclass.constructor.call(this, config);
446 this.el = Roo.get(config.el ? config.el : document.body );
447 if (this.cls && this.cls.length) {
448 Roo.get(document.body).addClass(this.cls);
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
454 is_body : true,// just to make sure it's constructed?
459 onRender : function(ct, position)
461 /* Roo.log("Roo.bootstrap.Body - onRender");
462 if (this.cls && this.cls.length) {
463 Roo.get(document.body).addClass(this.cls);
482 * @class Roo.bootstrap.ButtonGroup
483 * @extends Roo.bootstrap.Component
484 * Bootstrap ButtonGroup class
485 * @cfg {String} size lg | sm | xs (default empty normal)
486 * @cfg {String} align vertical | justified (default none)
487 * @cfg {String} direction up | down (default down)
488 * @cfg {Boolean} toolbar false | true
489 * @cfg {Boolean} btn true | false
494 * @param {Object} config The config object
497 Roo.bootstrap.ButtonGroup = function(config){
498 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
509 getAutoCreate : function(){
515 cfg.html = this.html || cfg.html;
526 if (['vertical','justified'].indexOf(this.align)!==-1) {
527 cfg.cls = 'btn-group-' + this.align;
529 if (this.align == 'justified') {
530 console.log(this.items);
534 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535 cfg.cls += ' btn-group-' + this.size;
538 if (this.direction == 'up') {
539 cfg.cls += ' dropup' ;
555 * @class Roo.bootstrap.Button
556 * @extends Roo.bootstrap.Component
557 * Bootstrap Button class
558 * @cfg {String} html The button content
559 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
560 * @cfg {String} size ( lg | sm | xs)
561 * @cfg {String} tag ( a | input | submit)
562 * @cfg {String} href empty or href
563 * @cfg {Boolean} disabled default false;
564 * @cfg {Boolean} isClose default false;
565 * @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)
566 * @cfg {String} badge text for badge
567 * @cfg {String} theme (default|glow)
568 * @cfg {Boolean} inverse dark themed version
569 * @cfg {Boolean} toggle is it a slidy toggle button
570 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571 * @cfg {String} ontext text for on slidy toggle state
572 * @cfg {String} offtext text for off slidy toggle state
573 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
574 * @cfg {Boolean} removeClass remove the standard class..
575 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
578 * Create a new button
579 * @param {Object} config The config object
583 Roo.bootstrap.Button = function(config){
584 Roo.bootstrap.Button.superclass.constructor.call(this, config);
585 this.weightClass = ["btn-default",
597 * When a butotn is pressed
598 * @param {Roo.bootstrap.Button} btn
599 * @param {Roo.EventObject} e
604 * After the button has been toggles
605 * @param {Roo.bootstrap.Button} btn
606 * @param {Roo.EventObject} e
607 * @param {boolean} pressed (also available as button.pressed)
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
631 preventDefault: true,
639 getAutoCreate : function(){
647 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
653 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
655 if (this.toggle == true) {
658 cls: 'slider-frame roo-button',
663 'data-off-text':'OFF',
664 cls: 'slider-button',
670 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671 cfg.cls += ' '+this.weight;
680 cfg["aria-hidden"] = true;
682 cfg.html = "×";
688 if (this.theme==='default') {
689 cfg.cls = 'btn roo-button';
691 //if (this.parentType != 'Navbar') {
692 this.weight = this.weight.length ? this.weight : 'default';
694 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
696 cfg.cls += ' btn-' + this.weight;
698 } else if (this.theme==='glow') {
701 cfg.cls = 'btn-glow roo-button';
703 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
705 cfg.cls += ' ' + this.weight;
711 this.cls += ' inverse';
715 if (this.active || this.pressed === true) {
716 cfg.cls += ' active';
720 cfg.disabled = 'disabled';
724 Roo.log('changing to ul' );
726 this.glyphicon = 'caret';
729 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
731 //gsRoo.log(this.parentType);
732 if (this.parentType === 'Navbar' && !this.parent().bar) {
733 Roo.log('changing to li?');
742 href : this.href || '#'
745 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
746 cfg.cls += ' dropdown';
753 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
755 if (this.glyphicon) {
756 cfg.html = ' ' + cfg.html;
761 cls: 'glyphicon glyphicon-' + this.glyphicon
771 // cfg.cls='btn roo-button';
775 var value = cfg.html;
780 cls: 'glyphicon glyphicon-' + this.glyphicon,
799 cfg.cls += ' dropdown';
800 cfg.html = typeof(cfg.html) != 'undefined' ?
801 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
804 if (cfg.tag !== 'a' && this.href !== '') {
805 throw "Tag must be a to set href.";
806 } else if (this.href.length > 0) {
807 cfg.href = this.href;
810 if(this.removeClass){
815 cfg.target = this.target;
820 initEvents: function() {
821 // Roo.log('init events?');
822 // Roo.log(this.el.dom);
825 if (typeof (this.menu) != 'undefined') {
826 this.menu.parentType = this.xtype;
827 this.menu.triggerEl = this.el;
828 this.addxtype(Roo.apply({}, this.menu));
832 if (this.el.hasClass('roo-button')) {
833 this.el.on('click', this.onClick, this);
835 this.el.select('.roo-button').on('click', this.onClick, this);
838 if(this.removeClass){
839 this.el.on('click', this.onClick, this);
842 this.el.enableDisplayMode();
845 onClick : function(e)
851 Roo.log('button on click ');
852 if(this.preventDefault){
856 if (this.pressed === true || this.pressed === false) {
857 this.toggleActive(e);
861 this.fireEvent('click', this, e);
865 * Enables this button
869 this.disabled = false;
870 this.el.removeClass('disabled');
874 * Disable this button
878 this.disabled = true;
879 this.el.addClass('disabled');
882 * sets the active state on/off,
883 * @param {Boolean} state (optional) Force a particular state
885 setActive : function(v) {
887 this.el[v ? 'addClass' : 'removeClass']('active');
891 * toggles the current active state
893 toggleActive : function(e)
895 this.setActive(!this.pressed);
896 this.fireEvent('toggle', this, e, !this.pressed);
899 * get the current active state
900 * @return {boolean} true if it's active
902 isActive : function()
904 return this.el.hasClass('active');
907 * set the text of the first selected button
909 setText : function(str)
911 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
914 * get the text of the first selected button
918 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
921 setWeight : function(str)
923 this.el.removeClass(this.weightClass);
924 this.el.addClass('btn-' + str);
938 * @class Roo.bootstrap.Column
939 * @extends Roo.bootstrap.Component
940 * Bootstrap Column class
941 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
942 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
943 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
944 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
945 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
946 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
947 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
948 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
951 * @cfg {Boolean} hidden (true|false) hide the element
952 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
953 * @cfg {String} fa (ban|check|...) font awesome icon
954 * @cfg {Number} fasize (1|2|....) font awsome size
956 * @cfg {String} icon (info-sign|check|...) glyphicon name
958 * @cfg {String} html content of column.
961 * Create a new Column
962 * @param {Object} config The config object
965 Roo.bootstrap.Column = function(config){
966 Roo.bootstrap.Column.superclass.constructor.call(this, config);
969 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
987 getAutoCreate : function(){
988 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
996 ['xs','sm','md','lg'].map(function(size){
997 //Roo.log( size + ':' + settings[size]);
999 if (settings[size+'off'] !== false) {
1000 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1003 if (settings[size] === false) {
1007 if (!settings[size]) { // 0 = hidden
1008 cfg.cls += ' hidden-' + size;
1011 cfg.cls += ' col-' + size + '-' + settings[size];
1016 cfg.cls += ' hidden';
1019 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1020 cfg.cls +=' alert alert-' + this.alert;
1024 if (this.html.length) {
1025 cfg.html = this.html;
1029 if (this.fasize > 1) {
1030 fasize = ' fa-' + this.fasize + 'x';
1032 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1037 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1056 * @class Roo.bootstrap.Container
1057 * @extends Roo.bootstrap.Component
1058 * Bootstrap Container class
1059 * @cfg {Boolean} jumbotron is it a jumbotron element
1060 * @cfg {String} html content of element
1061 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1062 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1063 * @cfg {String} header content of header (for panel)
1064 * @cfg {String} footer content of footer (for panel)
1065 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1066 * @cfg {String} tag (header|aside|section) type of HTML tag.
1067 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1068 * @cfg {String} fa font awesome icon
1069 * @cfg {String} icon (info-sign|check|...) glyphicon name
1070 * @cfg {Boolean} hidden (true|false) hide the element
1071 * @cfg {Boolean} expandable (true|false) default false
1072 * @cfg {Boolean} expanded (true|false) default true
1073 * @cfg {String} rheader contet on the right of header
1074 * @cfg {Boolean} clickable (true|false) default false
1078 * Create a new Container
1079 * @param {Object} config The config object
1082 Roo.bootstrap.Container = function(config){
1083 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1089 * After the panel has been expand
1091 * @param {Roo.bootstrap.Container} this
1096 * After the panel has been collapsed
1098 * @param {Roo.bootstrap.Container} this
1103 * When a element is chick
1104 * @param {Roo.bootstrap.Container} this
1105 * @param {Roo.EventObject} e
1111 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1129 getChildContainer : function() {
1135 if (this.panel.length) {
1136 return this.el.select('.panel-body',true).first();
1143 getAutoCreate : function(){
1146 tag : this.tag || 'div',
1150 if (this.jumbotron) {
1151 cfg.cls = 'jumbotron';
1156 // - this is applied by the parent..
1158 // cfg.cls = this.cls + '';
1161 if (this.sticky.length) {
1163 var bd = Roo.get(document.body);
1164 if (!bd.hasClass('bootstrap-sticky')) {
1165 bd.addClass('bootstrap-sticky');
1166 Roo.select('html',true).setStyle('height', '100%');
1169 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1173 if (this.well.length) {
1174 switch (this.well) {
1177 cfg.cls +=' well well-' +this.well;
1186 cfg.cls += ' hidden';
1190 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1191 cfg.cls +=' alert alert-' + this.alert;
1196 if (this.panel.length) {
1197 cfg.cls += ' panel panel-' + this.panel;
1199 if (this.header.length) {
1203 if(this.expandable){
1205 cfg.cls = cfg.cls + ' expandable';
1209 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1217 cls : 'panel-title',
1218 html : (this.expandable ? ' ' : '') + this.header
1222 cls: 'panel-header-right',
1228 cls : 'panel-heading',
1229 style : this.expandable ? 'cursor: pointer' : '',
1237 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1242 if (this.footer.length) {
1244 cls : 'panel-footer',
1253 body.html = this.html || cfg.html;
1254 // prefix with the icons..
1256 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1259 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1264 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1265 cfg.cls = 'container';
1271 initEvents: function()
1273 if(this.expandable){
1274 var headerEl = this.headerEl();
1277 headerEl.on('click', this.onToggleClick, this);
1282 this.el.on('click', this.onClick, this);
1287 onToggleClick : function()
1289 var headerEl = this.headerEl();
1305 if(this.fireEvent('expand', this)) {
1307 this.expanded = true;
1309 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1311 this.el.select('.panel-body',true).first().removeClass('hide');
1313 var toggleEl = this.toggleEl();
1319 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1324 collapse : function()
1326 if(this.fireEvent('collapse', this)) {
1328 this.expanded = false;
1330 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1331 this.el.select('.panel-body',true).first().addClass('hide');
1333 var toggleEl = this.toggleEl();
1339 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1343 toggleEl : function()
1345 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1349 return this.el.select('.panel-heading .fa',true).first();
1352 headerEl : function()
1354 if(!this.el || !this.panel.length || !this.header.length){
1358 return this.el.select('.panel-heading',true).first()
1363 if(!this.el || !this.panel.length){
1367 return this.el.select('.panel-body',true).first()
1370 titleEl : function()
1372 if(!this.el || !this.panel.length || !this.header.length){
1376 return this.el.select('.panel-title',true).first();
1379 setTitle : function(v)
1381 var titleEl = this.titleEl();
1387 titleEl.dom.innerHTML = v;
1390 getTitle : function()
1393 var titleEl = this.titleEl();
1399 return titleEl.dom.innerHTML;
1402 setRightTitle : function(v)
1404 var t = this.el.select('.panel-header-right',true).first();
1410 t.dom.innerHTML = v;
1413 onClick : function(e)
1417 this.fireEvent('click', this, e);
1430 * @class Roo.bootstrap.Img
1431 * @extends Roo.bootstrap.Component
1432 * Bootstrap Img class
1433 * @cfg {Boolean} imgResponsive false | true
1434 * @cfg {String} border rounded | circle | thumbnail
1435 * @cfg {String} src image source
1436 * @cfg {String} alt image alternative text
1437 * @cfg {String} href a tag href
1438 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1439 * @cfg {String} xsUrl xs image source
1440 * @cfg {String} smUrl sm image source
1441 * @cfg {String} mdUrl md image source
1442 * @cfg {String} lgUrl lg image source
1445 * Create a new Input
1446 * @param {Object} config The config object
1449 Roo.bootstrap.Img = function(config){
1450 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1456 * The img click event for the img.
1457 * @param {Roo.EventObject} e
1463 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1465 imgResponsive: true,
1475 getAutoCreate : function()
1477 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1478 return this.createSingleImg();
1483 cls: 'roo-image-responsive-group',
1488 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1490 if(!_this[size + 'Url']){
1496 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1497 html: _this.html || cfg.html,
1498 src: _this[size + 'Url']
1501 img.cls += ' roo-image-responsive-' + size;
1503 var s = ['xs', 'sm', 'md', 'lg'];
1505 s.splice(s.indexOf(size), 1);
1507 Roo.each(s, function(ss){
1508 img.cls += ' hidden-' + ss;
1511 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1512 cfg.cls += ' img-' + _this.border;
1516 cfg.alt = _this.alt;
1529 a.target = _this.target;
1533 cfg.cn.push((_this.href) ? a : img);
1540 createSingleImg : function()
1544 cls: (this.imgResponsive) ? 'img-responsive' : '',
1546 src : 'about:blank' // just incase src get's set to undefined?!?
1549 cfg.html = this.html || cfg.html;
1551 cfg.src = this.src || cfg.src;
1553 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1554 cfg.cls += ' img-' + this.border;
1571 a.target = this.target;
1576 return (this.href) ? a : cfg;
1579 initEvents: function()
1582 this.el.on('click', this.onClick, this);
1587 onClick : function(e)
1589 Roo.log('img onclick');
1590 this.fireEvent('click', this, e);
1593 * Sets the url of the image - used to update it
1594 * @param {String} url the url of the image
1597 setSrc : function(url)
1601 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1602 this.el.dom.src = url;
1606 this.el.select('img', true).first().dom.src = url;
1622 * @class Roo.bootstrap.Link
1623 * @extends Roo.bootstrap.Component
1624 * Bootstrap Link Class
1625 * @cfg {String} alt image alternative text
1626 * @cfg {String} href a tag href
1627 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1628 * @cfg {String} html the content of the link.
1629 * @cfg {String} anchor name for the anchor link
1630 * @cfg {String} fa - favicon
1632 * @cfg {Boolean} preventDefault (true | false) default false
1636 * Create a new Input
1637 * @param {Object} config The config object
1640 Roo.bootstrap.Link = function(config){
1641 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1647 * The img click event for the img.
1648 * @param {Roo.EventObject} e
1654 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1658 preventDefault: false,
1664 getAutoCreate : function()
1666 var html = this.html || '';
1668 if (this.fa !== false) {
1669 html = '<i class="fa fa-' + this.fa + '"></i>';
1674 // anchor's do not require html/href...
1675 if (this.anchor === false) {
1677 cfg.href = this.href || '#';
1679 cfg.name = this.anchor;
1680 if (this.html !== false || this.fa !== false) {
1683 if (this.href !== false) {
1684 cfg.href = this.href;
1688 if(this.alt !== false){
1693 if(this.target !== false) {
1694 cfg.target = this.target;
1700 initEvents: function() {
1702 if(!this.href || this.preventDefault){
1703 this.el.on('click', this.onClick, this);
1707 onClick : function(e)
1709 if(this.preventDefault){
1712 //Roo.log('img onclick');
1713 this.fireEvent('click', this, e);
1726 * @class Roo.bootstrap.Header
1727 * @extends Roo.bootstrap.Component
1728 * Bootstrap Header class
1729 * @cfg {String} html content of header
1730 * @cfg {Number} level (1|2|3|4|5|6) default 1
1733 * Create a new Header
1734 * @param {Object} config The config object
1738 Roo.bootstrap.Header = function(config){
1739 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1742 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1750 getAutoCreate : function(){
1755 tag: 'h' + (1 *this.level),
1756 html: this.html || ''
1768 * Ext JS Library 1.1.1
1769 * Copyright(c) 2006-2007, Ext JS, LLC.
1771 * Originally Released Under LGPL - original licence link has changed is not relivant.
1774 * <script type="text/javascript">
1778 * @class Roo.bootstrap.MenuMgr
1779 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1782 Roo.bootstrap.MenuMgr = function(){
1783 var menus, active, groups = {}, attached = false, lastShow = new Date();
1785 // private - called when first menu is created
1788 active = new Roo.util.MixedCollection();
1789 Roo.get(document).addKeyListener(27, function(){
1790 if(active.length > 0){
1798 if(active && active.length > 0){
1799 var c = active.clone();
1809 if(active.length < 1){
1810 Roo.get(document).un("mouseup", onMouseDown);
1818 var last = active.last();
1819 lastShow = new Date();
1822 Roo.get(document).on("mouseup", onMouseDown);
1827 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1828 m.parentMenu.activeChild = m;
1829 }else if(last && last.isVisible()){
1830 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1835 function onBeforeHide(m){
1837 m.activeChild.hide();
1839 if(m.autoHideTimer){
1840 clearTimeout(m.autoHideTimer);
1841 delete m.autoHideTimer;
1846 function onBeforeShow(m){
1847 var pm = m.parentMenu;
1848 if(!pm && !m.allowOtherMenus){
1850 }else if(pm && pm.activeChild && active != m){
1851 pm.activeChild.hide();
1855 // private this should really trigger on mouseup..
1856 function onMouseDown(e){
1857 Roo.log("on Mouse Up");
1859 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1860 Roo.log("MenuManager hideAll");
1869 function onBeforeCheck(mi, state){
1871 var g = groups[mi.group];
1872 for(var i = 0, l = g.length; i < l; i++){
1874 g[i].setChecked(false);
1883 * Hides all menus that are currently visible
1885 hideAll : function(){
1890 register : function(menu){
1894 menus[menu.id] = menu;
1895 menu.on("beforehide", onBeforeHide);
1896 menu.on("hide", onHide);
1897 menu.on("beforeshow", onBeforeShow);
1898 menu.on("show", onShow);
1900 if(g && menu.events["checkchange"]){
1904 groups[g].push(menu);
1905 menu.on("checkchange", onCheck);
1910 * Returns a {@link Roo.menu.Menu} object
1911 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1912 * be used to generate and return a new Menu instance.
1914 get : function(menu){
1915 if(typeof menu == "string"){ // menu id
1917 }else if(menu.events){ // menu instance
1920 /*else if(typeof menu.length == 'number'){ // array of menu items?
1921 return new Roo.bootstrap.Menu({items:menu});
1922 }else{ // otherwise, must be a config
1923 return new Roo.bootstrap.Menu(menu);
1930 unregister : function(menu){
1931 delete menus[menu.id];
1932 menu.un("beforehide", onBeforeHide);
1933 menu.un("hide", onHide);
1934 menu.un("beforeshow", onBeforeShow);
1935 menu.un("show", onShow);
1937 if(g && menu.events["checkchange"]){
1938 groups[g].remove(menu);
1939 menu.un("checkchange", onCheck);
1944 registerCheckable : function(menuItem){
1945 var g = menuItem.group;
1950 groups[g].push(menuItem);
1951 menuItem.on("beforecheckchange", onBeforeCheck);
1956 unregisterCheckable : function(menuItem){
1957 var g = menuItem.group;
1959 groups[g].remove(menuItem);
1960 menuItem.un("beforecheckchange", onBeforeCheck);
1972 * @class Roo.bootstrap.Menu
1973 * @extends Roo.bootstrap.Component
1974 * Bootstrap Menu class - container for MenuItems
1975 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1976 * @cfg {bool} hidden if the menu should be hidden when rendered.
1977 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1978 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1982 * @param {Object} config The config object
1986 Roo.bootstrap.Menu = function(config){
1987 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1988 if (this.registerMenu && this.type != 'treeview') {
1989 Roo.bootstrap.MenuMgr.register(this);
1994 * Fires before this menu is displayed
1995 * @param {Roo.menu.Menu} this
2000 * Fires before this menu is hidden
2001 * @param {Roo.menu.Menu} this
2006 * Fires after this menu is displayed
2007 * @param {Roo.menu.Menu} this
2012 * Fires after this menu is hidden
2013 * @param {Roo.menu.Menu} this
2018 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2019 * @param {Roo.menu.Menu} this
2020 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2021 * @param {Roo.EventObject} e
2026 * Fires when the mouse is hovering over this menu
2027 * @param {Roo.menu.Menu} this
2028 * @param {Roo.EventObject} e
2029 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2034 * Fires when the mouse exits this menu
2035 * @param {Roo.menu.Menu} this
2036 * @param {Roo.EventObject} e
2037 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2042 * Fires when a menu item contained in this menu is clicked
2043 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2044 * @param {Roo.EventObject} e
2048 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2051 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2055 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2058 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2060 registerMenu : true,
2062 menuItems :false, // stores the menu items..
2072 getChildContainer : function() {
2076 getAutoCreate : function(){
2078 //if (['right'].indexOf(this.align)!==-1) {
2079 // cfg.cn[1].cls += ' pull-right'
2085 cls : 'dropdown-menu' ,
2086 style : 'z-index:1000'
2090 if (this.type === 'submenu') {
2091 cfg.cls = 'submenu active';
2093 if (this.type === 'treeview') {
2094 cfg.cls = 'treeview-menu';
2099 initEvents : function() {
2101 // Roo.log("ADD event");
2102 // Roo.log(this.triggerEl.dom);
2104 this.triggerEl.on('click', this.onTriggerClick, this);
2106 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2108 this.triggerEl.addClass('dropdown-toggle');
2111 this.el.on('touchstart' , this.onTouch, this);
2113 this.el.on('click' , this.onClick, this);
2115 this.el.on("mouseover", this.onMouseOver, this);
2116 this.el.on("mouseout", this.onMouseOut, this);
2120 findTargetItem : function(e)
2122 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2126 //Roo.log(t); Roo.log(t.id);
2128 //Roo.log(this.menuitems);
2129 return this.menuitems.get(t.id);
2131 //return this.items.get(t.menuItemId);
2137 onTouch : function(e)
2139 Roo.log("menu.onTouch");
2140 //e.stopEvent(); this make the user popdown broken
2144 onClick : function(e)
2146 Roo.log("menu.onClick");
2148 var t = this.findTargetItem(e);
2149 if(!t || t.isContainer){
2154 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2155 if(t == this.activeItem && t.shouldDeactivate(e)){
2156 this.activeItem.deactivate();
2157 delete this.activeItem;
2161 this.setActiveItem(t, true);
2169 Roo.log('pass click event');
2173 this.fireEvent("click", this, t, e);
2177 if(!t.href.length || t.href == '#'){
2178 (function() { _this.hide(); }).defer(100);
2183 onMouseOver : function(e){
2184 var t = this.findTargetItem(e);
2187 // if(t.canActivate && !t.disabled){
2188 // this.setActiveItem(t, true);
2192 this.fireEvent("mouseover", this, e, t);
2194 isVisible : function(){
2195 return !this.hidden;
2197 onMouseOut : function(e){
2198 var t = this.findTargetItem(e);
2201 // if(t == this.activeItem && t.shouldDeactivate(e)){
2202 // this.activeItem.deactivate();
2203 // delete this.activeItem;
2206 this.fireEvent("mouseout", this, e, t);
2211 * Displays this menu relative to another element
2212 * @param {String/HTMLElement/Roo.Element} element The element to align to
2213 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2214 * the element (defaults to this.defaultAlign)
2215 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2217 show : function(el, pos, parentMenu){
2218 this.parentMenu = parentMenu;
2222 this.fireEvent("beforeshow", this);
2223 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2226 * Displays this menu at a specific xy position
2227 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2228 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2230 showAt : function(xy, parentMenu, /* private: */_e){
2231 this.parentMenu = parentMenu;
2236 this.fireEvent("beforeshow", this);
2237 //xy = this.el.adjustForConstraints(xy);
2241 this.hideMenuItems();
2242 this.hidden = false;
2243 this.triggerEl.addClass('open');
2245 // reassign x when hitting right
2246 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2247 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2250 // reassign y when hitting bottom
2251 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2252 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2255 // but the list may align on trigger left or trigger top... should it be a properity?
2257 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2262 this.fireEvent("show", this);
2268 this.doFocus.defer(50, this);
2272 doFocus : function(){
2274 this.focusEl.focus();
2279 * Hides this menu and optionally all parent menus
2280 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2282 hide : function(deep)
2285 this.hideMenuItems();
2286 if(this.el && this.isVisible()){
2287 this.fireEvent("beforehide", this);
2288 if(this.activeItem){
2289 this.activeItem.deactivate();
2290 this.activeItem = null;
2292 this.triggerEl.removeClass('open');;
2294 this.fireEvent("hide", this);
2296 if(deep === true && this.parentMenu){
2297 this.parentMenu.hide(true);
2301 onTriggerClick : function(e)
2303 Roo.log('trigger click');
2305 var target = e.getTarget();
2307 Roo.log(target.nodeName.toLowerCase());
2309 if(target.nodeName.toLowerCase() === 'i'){
2315 onTriggerPress : function(e)
2317 Roo.log('trigger press');
2318 //Roo.log(e.getTarget());
2319 // Roo.log(this.triggerEl.dom);
2321 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2322 var pel = Roo.get(e.getTarget());
2323 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2324 Roo.log('is treeview or dropdown?');
2328 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2332 if (this.isVisible()) {
2337 this.show(this.triggerEl, false, false);
2340 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2347 hideMenuItems : function()
2349 Roo.log("hide Menu Items");
2353 //$(backdrop).remove()
2354 this.el.select('.open',true).each(function(aa) {
2356 aa.removeClass('open');
2357 //var parent = getParent($(this))
2358 //var relatedTarget = { relatedTarget: this }
2360 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2361 //if (e.isDefaultPrevented()) return
2362 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2365 addxtypeChild : function (tree, cntr) {
2366 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2368 this.menuitems.add(comp);
2380 this.getEl().dom.innerHTML = '';
2381 this.menuitems.clear();
2395 * @class Roo.bootstrap.MenuItem
2396 * @extends Roo.bootstrap.Component
2397 * Bootstrap MenuItem class
2398 * @cfg {String} html the menu label
2399 * @cfg {String} href the link
2400 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2401 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2402 * @cfg {Boolean} active used on sidebars to highlight active itesm
2403 * @cfg {String} fa favicon to show on left of menu item.
2404 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2408 * Create a new MenuItem
2409 * @param {Object} config The config object
2413 Roo.bootstrap.MenuItem = function(config){
2414 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2419 * The raw click event for the entire grid.
2420 * @param {Roo.bootstrap.MenuItem} this
2421 * @param {Roo.EventObject} e
2427 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2431 preventDefault: false,
2432 isContainer : false,
2436 getAutoCreate : function(){
2438 if(this.isContainer){
2441 cls: 'dropdown-menu-item'
2455 if (this.fa !== false) {
2458 cls : 'fa fa-' + this.fa
2467 cls: 'dropdown-menu-item',
2470 if (this.parent().type == 'treeview') {
2471 cfg.cls = 'treeview-menu';
2474 cfg.cls += ' active';
2479 anc.href = this.href || cfg.cn[0].href ;
2480 ctag.html = this.html || cfg.cn[0].html ;
2484 initEvents: function()
2486 if (this.parent().type == 'treeview') {
2487 this.el.select('a').on('click', this.onClick, this);
2491 this.menu.parentType = this.xtype;
2492 this.menu.triggerEl = this.el;
2493 this.menu = this.addxtype(Roo.apply({}, this.menu));
2497 onClick : function(e)
2499 Roo.log('item on click ');
2501 if(this.preventDefault){
2504 //this.parent().hideMenuItems();
2506 this.fireEvent('click', this, e);
2525 * @class Roo.bootstrap.MenuSeparator
2526 * @extends Roo.bootstrap.Component
2527 * Bootstrap MenuSeparator class
2530 * Create a new MenuItem
2531 * @param {Object} config The config object
2535 Roo.bootstrap.MenuSeparator = function(config){
2536 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2539 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2541 getAutoCreate : function(){
2560 * @class Roo.bootstrap.Modal
2561 * @extends Roo.bootstrap.Component
2562 * Bootstrap Modal class
2563 * @cfg {String} title Title of dialog
2564 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2565 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2566 * @cfg {Boolean} specificTitle default false
2567 * @cfg {Array} buttons Array of buttons or standard button set..
2568 * @cfg {String} buttonPosition (left|right|center) default right
2569 * @cfg {Boolean} animate default true
2570 * @cfg {Boolean} allow_close default true
2571 * @cfg {Boolean} fitwindow default false
2572 * @cfg {String} size (sm|lg) default empty
2573 * @cfg {Number} max_width set the max width of modal
2577 * Create a new Modal Dialog
2578 * @param {Object} config The config object
2581 Roo.bootstrap.Modal = function(config){
2582 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2587 * The raw btnclick event for the button
2588 * @param {Roo.EventObject} e
2593 * Fire when dialog resize
2594 * @param {Roo.bootstrap.Modal} this
2595 * @param {Roo.EventObject} e
2599 this.buttons = this.buttons || [];
2602 this.tmpl = Roo.factory(this.tmpl);
2607 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2609 title : 'test dialog',
2619 specificTitle: false,
2621 buttonPosition: 'right',
2644 onRender : function(ct, position)
2646 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2649 var cfg = Roo.apply({}, this.getAutoCreate());
2652 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2654 //if (!cfg.name.length) {
2658 cfg.cls += ' ' + this.cls;
2661 cfg.style = this.style;
2663 this.el = Roo.get(document.body).createChild(cfg, position);
2665 //var type = this.el.dom.type;
2668 if(this.tabIndex !== undefined){
2669 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2672 this.dialogEl = this.el.select('.modal-dialog',true).first();
2673 this.bodyEl = this.el.select('.modal-body',true).first();
2674 this.closeEl = this.el.select('.modal-header .close', true).first();
2675 this.headerEl = this.el.select('.modal-header',true).first();
2676 this.titleEl = this.el.select('.modal-title',true).first();
2677 this.footerEl = this.el.select('.modal-footer',true).first();
2679 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2681 //this.el.addClass("x-dlg-modal");
2683 if (this.buttons.length) {
2684 Roo.each(this.buttons, function(bb) {
2685 var b = Roo.apply({}, bb);
2686 b.xns = b.xns || Roo.bootstrap;
2687 b.xtype = b.xtype || 'Button';
2688 if (typeof(b.listeners) == 'undefined') {
2689 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2692 var btn = Roo.factory(b);
2694 btn.render(this.el.select('.modal-footer div').first());
2698 // render the children.
2701 if(typeof(this.items) != 'undefined'){
2702 var items = this.items;
2705 for(var i =0;i < items.length;i++) {
2706 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2710 this.items = nitems;
2712 // where are these used - they used to be body/close/footer
2716 //this.el.addClass([this.fieldClass, this.cls]);
2720 getAutoCreate : function()
2724 html : this.html || ''
2729 cls : 'modal-title',
2733 if(this.specificTitle){
2739 if (this.allow_close) {
2751 if(this.size.length){
2752 size = 'modal-' + this.size;
2759 cls: "modal-dialog " + size,
2762 cls : "modal-content",
2765 cls : 'modal-header',
2770 cls : 'modal-footer',
2774 cls: 'btn-' + this.buttonPosition
2791 modal.cls += ' fade';
2797 getChildContainer : function() {
2802 getButtonContainer : function() {
2803 return this.el.select('.modal-footer div',true).first();
2806 initEvents : function()
2808 if (this.allow_close) {
2809 this.closeEl.on('click', this.hide, this);
2811 Roo.EventManager.onWindowResize(this.resize, this, true);
2818 this.maskEl.setSize(
2819 Roo.lib.Dom.getViewWidth(true),
2820 Roo.lib.Dom.getViewHeight(true)
2823 if (this.fitwindow) {
2825 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2826 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2831 if(this.max_width !== 0) {
2833 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2836 this.setSize(w, this.height);
2840 if(this.max_height) {
2841 this.setSize(w,Math.min(
2843 Roo.lib.Dom.getViewportHeight(true) - 60
2849 if(!this.fit_content) {
2850 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2854 this.setSize(w, Math.min(
2856 this.headerEl.getHeight() +
2857 this.footerEl.getHeight() +
2858 this.getChildHeight(this.bodyEl.dom.childNodes),
2859 Roo.lib.Dom.getViewportHeight(true) - 60)
2865 setSize : function(w,h)
2876 if (!this.rendered) {
2880 //this.el.setStyle('display', 'block');
2881 this.el.removeClass('hideing');
2882 this.el.addClass('show');
2884 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2887 this.el.addClass('in');
2890 this.el.addClass('in');
2893 // not sure how we can show data in here..
2895 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2898 Roo.get(document.body).addClass("x-body-masked");
2900 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2901 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2902 this.maskEl.addClass('show');
2906 this.fireEvent('show', this);
2908 // set zindex here - otherwise it appears to be ignored...
2909 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2912 this.items.forEach( function(e) {
2913 e.layout ? e.layout() : false;
2921 if(this.fireEvent("beforehide", this) !== false){
2922 this.maskEl.removeClass('show');
2923 Roo.get(document.body).removeClass("x-body-masked");
2924 this.el.removeClass('in');
2925 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2927 if(this.animate){ // why
2928 this.el.addClass('hideing');
2930 if (!this.el.hasClass('hideing')) {
2931 return; // it's been shown again...
2933 this.el.removeClass('show');
2934 this.el.removeClass('hideing');
2938 this.el.removeClass('show');
2940 this.fireEvent('hide', this);
2943 isVisible : function()
2946 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2950 addButton : function(str, cb)
2954 var b = Roo.apply({}, { html : str } );
2955 b.xns = b.xns || Roo.bootstrap;
2956 b.xtype = b.xtype || 'Button';
2957 if (typeof(b.listeners) == 'undefined') {
2958 b.listeners = { click : cb.createDelegate(this) };
2961 var btn = Roo.factory(b);
2963 btn.render(this.el.select('.modal-footer div').first());
2969 setDefaultButton : function(btn)
2971 //this.el.select('.modal-footer').()
2975 resizeTo: function(w,h)
2979 this.dialogEl.setWidth(w);
2980 if (this.diff === false) {
2981 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2984 this.bodyEl.setHeight(h - this.diff);
2986 this.fireEvent('resize', this);
2989 setContentSize : function(w, h)
2993 onButtonClick: function(btn,e)
2996 this.fireEvent('btnclick', btn.name, e);
2999 * Set the title of the Dialog
3000 * @param {String} str new Title
3002 setTitle: function(str) {
3003 this.titleEl.dom.innerHTML = str;
3006 * Set the body of the Dialog
3007 * @param {String} str new Title
3009 setBody: function(str) {
3010 this.bodyEl.dom.innerHTML = str;
3013 * Set the body of the Dialog using the template
3014 * @param {Obj} data - apply this data to the template and replace the body contents.
3016 applyBody: function(obj)
3019 Roo.log("Error - using apply Body without a template");
3022 this.tmpl.overwrite(this.bodyEl, obj);
3025 getChildHeight : function(child_nodes)
3029 child_nodes.length == 0
3034 var child_height = 0;
3036 for(var i = 0; i < child_nodes.length; i++) {
3039 * for modal with tabs...
3040 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3042 var layout_childs = child_nodes[i].childNodes;
3044 for(var j = 0; j < layout_childs.length; j++) {
3046 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3048 var layout_body_childs = layout_childs[j].childNodes;
3050 for(var k = 0; k < layout_body_childs.length; k++) {
3052 if(layout_body_childs[k].classList.contains('navbar')) {
3053 child_height += layout_body_childs[k].offsetHeight;
3057 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3059 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3061 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3063 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3064 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3079 child_height += child_nodes[i].offsetHeight;
3080 // Roo.log(child_nodes[i].offsetHeight);
3083 return child_height;
3089 Roo.apply(Roo.bootstrap.Modal, {
3091 * Button config that displays a single OK button
3100 * Button config that displays Yes and No buttons
3116 * Button config that displays OK and Cancel buttons
3131 * Button config that displays Yes, No and Cancel buttons
3155 * messagebox - can be used as a replace
3159 * @class Roo.MessageBox
3160 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3164 Roo.Msg.alert('Status', 'Changes saved successfully.');
3166 // Prompt for user data:
3167 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3169 // process text value...
3173 // Show a dialog using config options:
3175 title:'Save Changes?',
3176 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3177 buttons: Roo.Msg.YESNOCANCEL,
3184 Roo.bootstrap.MessageBox = function(){
3185 var dlg, opt, mask, waitTimer;
3186 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3187 var buttons, activeTextEl, bwidth;
3191 var handleButton = function(button){
3193 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3197 var handleHide = function(){
3199 dlg.el.removeClass(opt.cls);
3202 // Roo.TaskMgr.stop(waitTimer);
3203 // waitTimer = null;
3208 var updateButtons = function(b){
3211 buttons["ok"].hide();
3212 buttons["cancel"].hide();
3213 buttons["yes"].hide();
3214 buttons["no"].hide();
3215 //dlg.footer.dom.style.display = 'none';
3218 dlg.footerEl.dom.style.display = '';
3219 for(var k in buttons){
3220 if(typeof buttons[k] != "function"){
3223 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3224 width += buttons[k].el.getWidth()+15;
3234 var handleEsc = function(d, k, e){
3235 if(opt && opt.closable !== false){
3245 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3246 * @return {Roo.BasicDialog} The BasicDialog element
3248 getDialog : function(){
3250 dlg = new Roo.bootstrap.Modal( {
3253 //constraintoviewport:false,
3255 //collapsible : false,
3260 //buttonAlign:"center",
3261 closeClick : function(){
3262 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3265 handleButton("cancel");
3270 dlg.on("hide", handleHide);
3272 //dlg.addKeyListener(27, handleEsc);
3274 this.buttons = buttons;
3275 var bt = this.buttonText;
3276 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3277 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3278 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3279 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3281 bodyEl = dlg.bodyEl.createChild({
3283 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3284 '<textarea class="roo-mb-textarea"></textarea>' +
3285 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3287 msgEl = bodyEl.dom.firstChild;
3288 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3289 textboxEl.enableDisplayMode();
3290 textboxEl.addKeyListener([10,13], function(){
3291 if(dlg.isVisible() && opt && opt.buttons){
3294 }else if(opt.buttons.yes){
3295 handleButton("yes");
3299 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3300 textareaEl.enableDisplayMode();
3301 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3302 progressEl.enableDisplayMode();
3304 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3305 var pf = progressEl.dom.firstChild;
3307 pp = Roo.get(pf.firstChild);
3308 pp.setHeight(pf.offsetHeight);
3316 * Updates the message box body text
3317 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3318 * the XHTML-compliant non-breaking space character '&#160;')
3319 * @return {Roo.MessageBox} This message box
3321 updateText : function(text)
3323 if(!dlg.isVisible() && !opt.width){
3324 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3325 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3327 msgEl.innerHTML = text || ' ';
3329 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3330 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3332 Math.min(opt.width || cw , this.maxWidth),
3333 Math.max(opt.minWidth || this.minWidth, bwidth)
3336 activeTextEl.setWidth(w);
3338 if(dlg.isVisible()){
3339 dlg.fixedcenter = false;
3341 // to big, make it scroll. = But as usual stupid IE does not support
3344 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3345 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3346 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3348 bodyEl.dom.style.height = '';
3349 bodyEl.dom.style.overflowY = '';
3352 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3354 bodyEl.dom.style.overflowX = '';
3357 dlg.setContentSize(w, bodyEl.getHeight());
3358 if(dlg.isVisible()){
3359 dlg.fixedcenter = true;
3365 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3366 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3367 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3368 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3369 * @return {Roo.MessageBox} This message box
3371 updateProgress : function(value, text){
3373 this.updateText(text);
3376 if (pp) { // weird bug on my firefox - for some reason this is not defined
3377 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3378 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3384 * Returns true if the message box is currently displayed
3385 * @return {Boolean} True if the message box is visible, else false
3387 isVisible : function(){
3388 return dlg && dlg.isVisible();
3392 * Hides the message box if it is displayed
3395 if(this.isVisible()){
3401 * Displays a new message box, or reinitializes an existing message box, based on the config options
3402 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3403 * The following config object properties are supported:
3405 Property Type Description
3406 ---------- --------------- ------------------------------------------------------------------------------------
3407 animEl String/Element An id or Element from which the message box should animate as it opens and
3408 closes (defaults to undefined)
3409 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3410 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3411 closable Boolean False to hide the top-right close button (defaults to true). Note that
3412 progress and wait dialogs will ignore this property and always hide the
3413 close button as they can only be closed programmatically.
3414 cls String A custom CSS class to apply to the message box element
3415 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3416 displayed (defaults to 75)
3417 fn Function A callback function to execute after closing the dialog. The arguments to the
3418 function will be btn (the name of the button that was clicked, if applicable,
3419 e.g. "ok"), and text (the value of the active text field, if applicable).
3420 Progress and wait dialogs will ignore this option since they do not respond to
3421 user actions and can only be closed programmatically, so any required function
3422 should be called by the same code after it closes the dialog.
3423 icon String A CSS class that provides a background image to be used as an icon for
3424 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3425 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3426 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3427 modal Boolean False to allow user interaction with the page while the message box is
3428 displayed (defaults to true)
3429 msg String A string that will replace the existing message box body text (defaults
3430 to the XHTML-compliant non-breaking space character ' ')
3431 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3432 progress Boolean True to display a progress bar (defaults to false)
3433 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3434 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3435 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3436 title String The title text
3437 value String The string value to set into the active textbox element if displayed
3438 wait Boolean True to display a progress bar (defaults to false)
3439 width Number The width of the dialog in pixels
3446 msg: 'Please enter your address:',
3448 buttons: Roo.MessageBox.OKCANCEL,
3451 animEl: 'addAddressBtn'
3454 * @param {Object} config Configuration options
3455 * @return {Roo.MessageBox} This message box
3457 show : function(options)
3460 // this causes nightmares if you show one dialog after another
3461 // especially on callbacks..
3463 if(this.isVisible()){
3466 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3467 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3468 Roo.log("New Dialog Message:" + options.msg )
3469 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3470 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3473 var d = this.getDialog();
3475 d.setTitle(opt.title || " ");
3476 d.closeEl.setDisplayed(opt.closable !== false);
3477 activeTextEl = textboxEl;
3478 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3483 textareaEl.setHeight(typeof opt.multiline == "number" ?
3484 opt.multiline : this.defaultTextHeight);
3485 activeTextEl = textareaEl;
3494 progressEl.setDisplayed(opt.progress === true);
3495 this.updateProgress(0);
3496 activeTextEl.dom.value = opt.value || "";
3498 dlg.setDefaultButton(activeTextEl);
3500 var bs = opt.buttons;
3504 }else if(bs && bs.yes){
3505 db = buttons["yes"];
3507 dlg.setDefaultButton(db);
3509 bwidth = updateButtons(opt.buttons);
3510 this.updateText(opt.msg);
3512 d.el.addClass(opt.cls);
3514 d.proxyDrag = opt.proxyDrag === true;
3515 d.modal = opt.modal !== false;
3516 d.mask = opt.modal !== false ? mask : false;
3518 // force it to the end of the z-index stack so it gets a cursor in FF
3519 document.body.appendChild(dlg.el.dom);
3520 d.animateTarget = null;
3521 d.show(options.animEl);
3527 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3528 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3529 * and closing the message box when the process is complete.
3530 * @param {String} title The title bar text
3531 * @param {String} msg The message box body text
3532 * @return {Roo.MessageBox} This message box
3534 progress : function(title, msg){
3541 minWidth: this.minProgressWidth,
3548 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3549 * If a callback function is passed it will be called after the user clicks the button, and the
3550 * id of the button that was clicked will be passed as the only parameter to the callback
3551 * (could also be the top-right close button).
3552 * @param {String} title The title bar text
3553 * @param {String} msg The message box body text
3554 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3555 * @param {Object} scope (optional) The scope of the callback function
3556 * @return {Roo.MessageBox} This message box
3558 alert : function(title, msg, fn, scope)
3573 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3574 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3575 * You are responsible for closing the message box when the process is complete.
3576 * @param {String} msg The message box body text
3577 * @param {String} title (optional) The title bar text
3578 * @return {Roo.MessageBox} This message box
3580 wait : function(msg, title){
3591 waitTimer = Roo.TaskMgr.start({
3593 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3601 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3602 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3603 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3604 * @param {String} title The title bar text
3605 * @param {String} msg The message box body text
3606 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3607 * @param {Object} scope (optional) The scope of the callback function
3608 * @return {Roo.MessageBox} This message box
3610 confirm : function(title, msg, fn, scope){
3614 buttons: this.YESNO,
3623 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3624 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3625 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3626 * (could also be the top-right close button) and the text that was entered will be passed as the two
3627 * parameters to the callback.
3628 * @param {String} title The title bar text
3629 * @param {String} msg The message box body text
3630 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3631 * @param {Object} scope (optional) The scope of the callback function
3632 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3633 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3634 * @return {Roo.MessageBox} This message box
3636 prompt : function(title, msg, fn, scope, multiline){
3640 buttons: this.OKCANCEL,
3645 multiline: multiline,
3652 * Button config that displays a single OK button
3657 * Button config that displays Yes and No buttons
3660 YESNO : {yes:true, no:true},
3662 * Button config that displays OK and Cancel buttons
3665 OKCANCEL : {ok:true, cancel:true},
3667 * Button config that displays Yes, No and Cancel buttons
3670 YESNOCANCEL : {yes:true, no:true, cancel:true},
3673 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3676 defaultTextHeight : 75,
3678 * The maximum width in pixels of the message box (defaults to 600)
3683 * The minimum width in pixels of the message box (defaults to 100)
3688 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3689 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3692 minProgressWidth : 250,
3694 * An object containing the default button text strings that can be overriden for localized language support.
3695 * Supported properties are: ok, cancel, yes and no.
3696 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3709 * Shorthand for {@link Roo.MessageBox}
3711 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3712 Roo.Msg = Roo.Msg || Roo.MessageBox;
3721 * @class Roo.bootstrap.Navbar
3722 * @extends Roo.bootstrap.Component
3723 * Bootstrap Navbar class
3726 * Create a new Navbar
3727 * @param {Object} config The config object
3731 Roo.bootstrap.Navbar = function(config){
3732 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3736 * @event beforetoggle
3737 * Fire before toggle the menu
3738 * @param {Roo.EventObject} e
3740 "beforetoggle" : true
3744 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3753 getAutoCreate : function(){
3756 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3760 initEvents :function ()
3762 //Roo.log(this.el.select('.navbar-toggle',true));
3763 this.el.select('.navbar-toggle',true).on('click', function() {
3764 if(this.fireEvent('beforetoggle', this) !== false){
3765 this.el.select('.navbar-collapse',true).toggleClass('in');
3775 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3777 var size = this.el.getSize();
3778 this.maskEl.setSize(size.width, size.height);
3779 this.maskEl.enableDisplayMode("block");
3788 getChildContainer : function()
3790 if (this.el.select('.collapse').getCount()) {
3791 return this.el.select('.collapse',true).first();
3824 * @class Roo.bootstrap.NavSimplebar
3825 * @extends Roo.bootstrap.Navbar
3826 * Bootstrap Sidebar class
3828 * @cfg {Boolean} inverse is inverted color
3830 * @cfg {String} type (nav | pills | tabs)
3831 * @cfg {Boolean} arrangement stacked | justified
3832 * @cfg {String} align (left | right) alignment
3834 * @cfg {Boolean} main (true|false) main nav bar? default false
3835 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3837 * @cfg {String} tag (header|footer|nav|div) default is nav
3843 * Create a new Sidebar
3844 * @param {Object} config The config object
3848 Roo.bootstrap.NavSimplebar = function(config){
3849 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3852 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3868 getAutoCreate : function(){
3872 tag : this.tag || 'div',
3885 this.type = this.type || 'nav';
3886 if (['tabs','pills'].indexOf(this.type)!==-1) {
3887 cfg.cn[0].cls += ' nav-' + this.type
3891 if (this.type!=='nav') {
3892 Roo.log('nav type must be nav/tabs/pills')
3894 cfg.cn[0].cls += ' navbar-nav'
3900 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3901 cfg.cn[0].cls += ' nav-' + this.arrangement;
3905 if (this.align === 'right') {
3906 cfg.cn[0].cls += ' navbar-right';
3910 cfg.cls += ' navbar-inverse';
3934 * navbar-expand-md fixed-top
3938 * @class Roo.bootstrap.NavHeaderbar
3939 * @extends Roo.bootstrap.NavSimplebar
3940 * Bootstrap Sidebar class
3942 * @cfg {String} brand what is brand
3943 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3944 * @cfg {String} brand_href href of the brand
3945 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3946 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3947 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3948 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3951 * Create a new Sidebar
3952 * @param {Object} config The config object
3956 Roo.bootstrap.NavHeaderbar = function(config){
3957 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3961 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3968 desktopCenter : false,
3971 getAutoCreate : function(){
3974 tag: this.nav || 'nav',
3981 if (this.desktopCenter) {
3982 cn.push({cls : 'container', cn : []});
3989 cls: 'navbar-header',
3994 cls: 'navbar-toggle',
3995 'data-toggle': 'collapse',
4000 html: 'Toggle navigation'
4022 cls: 'collapse navbar-collapse',
4026 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4028 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4029 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4031 // tag can override this..
4033 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4036 if (this.brand !== '') {
4039 href: this.brand_href ? this.brand_href : '#',
4040 cls: 'navbar-brand',
4048 cfg.cls += ' main-nav';
4056 getHeaderChildContainer : function()
4058 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4059 return this.el.select('.navbar-header',true).first();
4062 return this.getChildContainer();
4066 initEvents : function()
4068 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4070 if (this.autohide) {
4075 Roo.get(document).on('scroll',function(e) {
4076 var ns = Roo.get(document).getScroll().top;
4077 var os = prevScroll;
4081 ft.removeClass('slideDown');
4082 ft.addClass('slideUp');
4085 ft.removeClass('slideUp');
4086 ft.addClass('slideDown');
4107 * @class Roo.bootstrap.NavSidebar
4108 * @extends Roo.bootstrap.Navbar
4109 * Bootstrap Sidebar class
4112 * Create a new Sidebar
4113 * @param {Object} config The config object
4117 Roo.bootstrap.NavSidebar = function(config){
4118 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4121 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4123 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4125 getAutoCreate : function(){
4130 cls: 'sidebar sidebar-nav'
4152 * @class Roo.bootstrap.NavGroup
4153 * @extends Roo.bootstrap.Component
4154 * Bootstrap NavGroup class
4155 * @cfg {String} align (left|right)
4156 * @cfg {Boolean} inverse
4157 * @cfg {String} type (nav|pills|tab) default nav
4158 * @cfg {String} navId - reference Id for navbar.
4162 * Create a new nav group
4163 * @param {Object} config The config object
4166 Roo.bootstrap.NavGroup = function(config){
4167 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4170 Roo.bootstrap.NavGroup.register(this);
4174 * Fires when the active item changes
4175 * @param {Roo.bootstrap.NavGroup} this
4176 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4177 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4184 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4195 getAutoCreate : function()
4197 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4204 if (['tabs','pills'].indexOf(this.type)!==-1) {
4205 cfg.cls += ' nav-' + this.type
4207 if (this.type!=='nav') {
4208 Roo.log('nav type must be nav/tabs/pills')
4210 cfg.cls += ' navbar-nav'
4213 if (this.parent() && this.parent().sidebar) {
4216 cls: 'dashboard-menu sidebar-menu'
4222 if (this.form === true) {
4228 if (this.align === 'right') {
4229 cfg.cls += ' navbar-right';
4231 cfg.cls += ' navbar-left';
4235 if (this.align === 'right') {
4236 cfg.cls += ' navbar-right';
4240 cfg.cls += ' navbar-inverse';
4248 * sets the active Navigation item
4249 * @param {Roo.bootstrap.NavItem} the new current navitem
4251 setActiveItem : function(item)
4254 Roo.each(this.navItems, function(v){
4259 v.setActive(false, true);
4266 item.setActive(true, true);
4267 this.fireEvent('changed', this, item, prev);
4272 * gets the active Navigation item
4273 * @return {Roo.bootstrap.NavItem} the current navitem
4275 getActive : function()
4279 Roo.each(this.navItems, function(v){
4290 indexOfNav : function()
4294 Roo.each(this.navItems, function(v,i){
4305 * adds a Navigation item
4306 * @param {Roo.bootstrap.NavItem} the navitem to add
4308 addItem : function(cfg)
4310 var cn = new Roo.bootstrap.NavItem(cfg);
4312 cn.parentId = this.id;
4313 cn.onRender(this.el, null);
4317 * register a Navigation item
4318 * @param {Roo.bootstrap.NavItem} the navitem to add
4320 register : function(item)
4322 this.navItems.push( item);
4323 item.navId = this.navId;
4328 * clear all the Navigation item
4331 clearAll : function()
4334 this.el.dom.innerHTML = '';
4337 getNavItem: function(tabId)
4340 Roo.each(this.navItems, function(e) {
4341 if (e.tabId == tabId) {
4351 setActiveNext : function()
4353 var i = this.indexOfNav(this.getActive());
4354 if (i > this.navItems.length) {
4357 this.setActiveItem(this.navItems[i+1]);
4359 setActivePrev : function()
4361 var i = this.indexOfNav(this.getActive());
4365 this.setActiveItem(this.navItems[i-1]);
4367 clearWasActive : function(except) {
4368 Roo.each(this.navItems, function(e) {
4369 if (e.tabId != except.tabId && e.was_active) {
4370 e.was_active = false;
4377 getWasActive : function ()
4380 Roo.each(this.navItems, function(e) {
4395 Roo.apply(Roo.bootstrap.NavGroup, {
4399 * register a Navigation Group
4400 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4402 register : function(navgrp)
4404 this.groups[navgrp.navId] = navgrp;
4408 * fetch a Navigation Group based on the navigation ID
4409 * @param {string} the navgroup to add
4410 * @returns {Roo.bootstrap.NavGroup} the navgroup
4412 get: function(navId) {
4413 if (typeof(this.groups[navId]) == 'undefined') {
4415 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4417 return this.groups[navId] ;
4432 * @class Roo.bootstrap.NavItem
4433 * @extends Roo.bootstrap.Component
4434 * Bootstrap Navbar.NavItem class
4435 * @cfg {String} href link to
4436 * @cfg {String} html content of button
4437 * @cfg {String} badge text inside badge
4438 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4439 * @cfg {String} glyphicon name of glyphicon
4440 * @cfg {String} icon name of font awesome icon
4441 * @cfg {Boolean} active Is item active
4442 * @cfg {Boolean} disabled Is item disabled
4444 * @cfg {Boolean} preventDefault (true | false) default false
4445 * @cfg {String} tabId the tab that this item activates.
4446 * @cfg {String} tagtype (a|span) render as a href or span?
4447 * @cfg {Boolean} animateRef (true|false) link to element default false
4450 * Create a new Navbar Item
4451 * @param {Object} config The config object
4453 Roo.bootstrap.NavItem = function(config){
4454 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4459 * The raw click event for the entire grid.
4460 * @param {Roo.EventObject} e
4465 * Fires when the active item active state changes
4466 * @param {Roo.bootstrap.NavItem} this
4467 * @param {boolean} state the new state
4473 * Fires when scroll to element
4474 * @param {Roo.bootstrap.NavItem} this
4475 * @param {Object} options
4476 * @param {Roo.EventObject} e
4484 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4492 preventDefault : false,
4499 getAutoCreate : function(){
4508 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4510 if (this.disabled) {
4511 cfg.cls += ' disabled';
4514 if (this.href || this.html || this.glyphicon || this.icon) {
4518 href : this.href || "#",
4519 html: this.html || ''
4524 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4527 if(this.glyphicon) {
4528 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4533 cfg.cn[0].html += " <span class='caret'></span>";
4537 if (this.badge !== '') {
4539 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4547 initEvents: function()
4549 if (typeof (this.menu) != 'undefined') {
4550 this.menu.parentType = this.xtype;
4551 this.menu.triggerEl = this.el;
4552 this.menu = this.addxtype(Roo.apply({}, this.menu));
4555 this.el.select('a',true).on('click', this.onClick, this);
4557 if(this.tagtype == 'span'){
4558 this.el.select('span',true).on('click', this.onClick, this);
4561 // at this point parent should be available..
4562 this.parent().register(this);
4565 onClick : function(e)
4567 if (e.getTarget('.dropdown-menu-item')) {
4568 // did you click on a menu itemm.... - then don't trigger onclick..
4573 this.preventDefault ||
4576 Roo.log("NavItem - prevent Default?");
4580 if (this.disabled) {
4584 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4585 if (tg && tg.transition) {
4586 Roo.log("waiting for the transitionend");
4592 //Roo.log("fire event clicked");
4593 if(this.fireEvent('click', this, e) === false){
4597 if(this.tagtype == 'span'){
4601 //Roo.log(this.href);
4602 var ael = this.el.select('a',true).first();
4605 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4606 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4607 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4608 return; // ignore... - it's a 'hash' to another page.
4610 Roo.log("NavItem - prevent Default?");
4612 this.scrollToElement(e);
4616 var p = this.parent();
4618 if (['tabs','pills'].indexOf(p.type)!==-1) {
4619 if (typeof(p.setActiveItem) !== 'undefined') {
4620 p.setActiveItem(this);
4624 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4625 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4626 // remove the collapsed menu expand...
4627 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4631 isActive: function () {
4634 setActive : function(state, fire, is_was_active)
4636 if (this.active && !state && this.navId) {
4637 this.was_active = true;
4638 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4640 nv.clearWasActive(this);
4644 this.active = state;
4647 this.el.removeClass('active');
4648 } else if (!this.el.hasClass('active')) {
4649 this.el.addClass('active');
4652 this.fireEvent('changed', this, state);
4655 // show a panel if it's registered and related..
4657 if (!this.navId || !this.tabId || !state || is_was_active) {
4661 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4665 var pan = tg.getPanelByName(this.tabId);
4669 // if we can not flip to new panel - go back to old nav highlight..
4670 if (false == tg.showPanel(pan)) {
4671 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4673 var onav = nv.getWasActive();
4675 onav.setActive(true, false, true);
4684 // this should not be here...
4685 setDisabled : function(state)
4687 this.disabled = state;
4689 this.el.removeClass('disabled');
4690 } else if (!this.el.hasClass('disabled')) {
4691 this.el.addClass('disabled');
4697 * Fetch the element to display the tooltip on.
4698 * @return {Roo.Element} defaults to this.el
4700 tooltipEl : function()
4702 return this.el.select('' + this.tagtype + '', true).first();
4705 scrollToElement : function(e)
4707 var c = document.body;
4710 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4712 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4713 c = document.documentElement;
4716 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4722 var o = target.calcOffsetsTo(c);
4729 this.fireEvent('scrollto', this, options, e);
4731 Roo.get(c).scrollTo('top', options.value, true);
4744 * <span> icon </span>
4745 * <span> text </span>
4746 * <span>badge </span>
4750 * @class Roo.bootstrap.NavSidebarItem
4751 * @extends Roo.bootstrap.NavItem
4752 * Bootstrap Navbar.NavSidebarItem class
4753 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4754 * {Boolean} open is the menu open
4755 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4756 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4757 * {String} buttonSize (sm|md|lg)the extra classes for the button
4758 * {Boolean} showArrow show arrow next to the text (default true)
4760 * Create a new Navbar Button
4761 * @param {Object} config The config object
4763 Roo.bootstrap.NavSidebarItem = function(config){
4764 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4769 * The raw click event for the entire grid.
4770 * @param {Roo.EventObject} e
4775 * Fires when the active item active state changes
4776 * @param {Roo.bootstrap.NavSidebarItem} this
4777 * @param {boolean} state the new state
4785 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4787 badgeWeight : 'default',
4793 buttonWeight : 'default',
4799 getAutoCreate : function(){
4804 href : this.href || '#',
4810 if(this.buttonView){
4813 href : this.href || '#',
4814 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4827 cfg.cls += ' active';
4830 if (this.disabled) {
4831 cfg.cls += ' disabled';
4834 cfg.cls += ' open x-open';
4837 if (this.glyphicon || this.icon) {
4838 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4839 a.cn.push({ tag : 'i', cls : c }) ;
4842 if(!this.buttonView){
4845 html : this.html || ''
4852 if (this.badge !== '') {
4853 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4859 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4862 a.cls += ' dropdown-toggle treeview' ;
4868 initEvents : function()
4870 if (typeof (this.menu) != 'undefined') {
4871 this.menu.parentType = this.xtype;
4872 this.menu.triggerEl = this.el;
4873 this.menu = this.addxtype(Roo.apply({}, this.menu));
4876 this.el.on('click', this.onClick, this);
4878 if(this.badge !== ''){
4879 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4884 onClick : function(e)
4891 if(this.preventDefault){
4895 this.fireEvent('click', this);
4898 disable : function()
4900 this.setDisabled(true);
4905 this.setDisabled(false);
4908 setDisabled : function(state)
4910 if(this.disabled == state){
4914 this.disabled = state;
4917 this.el.addClass('disabled');
4921 this.el.removeClass('disabled');
4926 setActive : function(state)
4928 if(this.active == state){
4932 this.active = state;
4935 this.el.addClass('active');
4939 this.el.removeClass('active');
4944 isActive: function ()
4949 setBadge : function(str)
4955 this.badgeEl.dom.innerHTML = str;
4972 * @class Roo.bootstrap.Row
4973 * @extends Roo.bootstrap.Component
4974 * Bootstrap Row class (contains columns...)
4978 * @param {Object} config The config object
4981 Roo.bootstrap.Row = function(config){
4982 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4985 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4987 getAutoCreate : function(){
5006 * @class Roo.bootstrap.Element
5007 * @extends Roo.bootstrap.Component
5008 * Bootstrap Element class
5009 * @cfg {String} html contents of the element
5010 * @cfg {String} tag tag of the element
5011 * @cfg {String} cls class of the element
5012 * @cfg {Boolean} preventDefault (true|false) default false
5013 * @cfg {Boolean} clickable (true|false) default false
5016 * Create a new Element
5017 * @param {Object} config The config object
5020 Roo.bootstrap.Element = function(config){
5021 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5027 * When a element is chick
5028 * @param {Roo.bootstrap.Element} this
5029 * @param {Roo.EventObject} e
5035 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5040 preventDefault: false,
5043 getAutoCreate : function(){
5047 // cls: this.cls, double assign in parent class Component.js :: onRender
5054 initEvents: function()
5056 Roo.bootstrap.Element.superclass.initEvents.call(this);
5059 this.el.on('click', this.onClick, this);
5064 onClick : function(e)
5066 if(this.preventDefault){
5070 this.fireEvent('click', this, e);
5073 getValue : function()
5075 return this.el.dom.innerHTML;
5078 setValue : function(value)
5080 this.el.dom.innerHTML = value;
5095 * @class Roo.bootstrap.Pagination
5096 * @extends Roo.bootstrap.Component
5097 * Bootstrap Pagination class
5098 * @cfg {String} size xs | sm | md | lg
5099 * @cfg {Boolean} inverse false | true
5102 * Create a new Pagination
5103 * @param {Object} config The config object
5106 Roo.bootstrap.Pagination = function(config){
5107 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5110 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5116 getAutoCreate : function(){
5122 cfg.cls += ' inverse';
5128 cfg.cls += " " + this.cls;
5146 * @class Roo.bootstrap.PaginationItem
5147 * @extends Roo.bootstrap.Component
5148 * Bootstrap PaginationItem class
5149 * @cfg {String} html text
5150 * @cfg {String} href the link
5151 * @cfg {Boolean} preventDefault (true | false) default true
5152 * @cfg {Boolean} active (true | false) default false
5153 * @cfg {Boolean} disabled default false
5157 * Create a new PaginationItem
5158 * @param {Object} config The config object
5162 Roo.bootstrap.PaginationItem = function(config){
5163 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5168 * The raw click event for the entire grid.
5169 * @param {Roo.EventObject} e
5175 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5179 preventDefault: true,
5184 getAutoCreate : function(){
5190 href : this.href ? this.href : '#',
5191 html : this.html ? this.html : ''
5201 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5205 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5211 initEvents: function() {
5213 this.el.on('click', this.onClick, this);
5216 onClick : function(e)
5218 Roo.log('PaginationItem on click ');
5219 if(this.preventDefault){
5227 this.fireEvent('click', this, e);
5243 * @class Roo.bootstrap.Slider
5244 * @extends Roo.bootstrap.Component
5245 * Bootstrap Slider class
5248 * Create a new Slider
5249 * @param {Object} config The config object
5252 Roo.bootstrap.Slider = function(config){
5253 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5256 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5258 getAutoCreate : function(){
5262 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5266 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5278 * Ext JS Library 1.1.1
5279 * Copyright(c) 2006-2007, Ext JS, LLC.
5281 * Originally Released Under LGPL - original licence link has changed is not relivant.
5284 * <script type="text/javascript">
5289 * @class Roo.grid.ColumnModel
5290 * @extends Roo.util.Observable
5291 * This is the default implementation of a ColumnModel used by the Grid. It defines
5292 * the columns in the grid.
5295 var colModel = new Roo.grid.ColumnModel([
5296 {header: "Ticker", width: 60, sortable: true, locked: true},
5297 {header: "Company Name", width: 150, sortable: true},
5298 {header: "Market Cap.", width: 100, sortable: true},
5299 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5300 {header: "Employees", width: 100, sortable: true, resizable: false}
5305 * The config options listed for this class are options which may appear in each
5306 * individual column definition.
5307 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5309 * @param {Object} config An Array of column config objects. See this class's
5310 * config objects for details.
5312 Roo.grid.ColumnModel = function(config){
5314 * The config passed into the constructor
5316 this.config = config;
5319 // if no id, create one
5320 // if the column does not have a dataIndex mapping,
5321 // map it to the order it is in the config
5322 for(var i = 0, len = config.length; i < len; i++){
5324 if(typeof c.dataIndex == "undefined"){
5327 if(typeof c.renderer == "string"){
5328 c.renderer = Roo.util.Format[c.renderer];
5330 if(typeof c.id == "undefined"){
5333 if(c.editor && c.editor.xtype){
5334 c.editor = Roo.factory(c.editor, Roo.grid);
5336 if(c.editor && c.editor.isFormField){
5337 c.editor = new Roo.grid.GridEditor(c.editor);
5339 this.lookup[c.id] = c;
5343 * The width of columns which have no width specified (defaults to 100)
5346 this.defaultWidth = 100;
5349 * Default sortable of columns which have no sortable specified (defaults to false)
5352 this.defaultSortable = false;
5356 * @event widthchange
5357 * Fires when the width of a column changes.
5358 * @param {ColumnModel} this
5359 * @param {Number} columnIndex The column index
5360 * @param {Number} newWidth The new width
5362 "widthchange": true,
5364 * @event headerchange
5365 * Fires when the text of a header changes.
5366 * @param {ColumnModel} this
5367 * @param {Number} columnIndex The column index
5368 * @param {Number} newText The new header text
5370 "headerchange": true,
5372 * @event hiddenchange
5373 * Fires when a column is hidden or "unhidden".
5374 * @param {ColumnModel} this
5375 * @param {Number} columnIndex The column index
5376 * @param {Boolean} hidden true if hidden, false otherwise
5378 "hiddenchange": true,
5380 * @event columnmoved
5381 * Fires when a column is moved.
5382 * @param {ColumnModel} this
5383 * @param {Number} oldIndex
5384 * @param {Number} newIndex
5386 "columnmoved" : true,
5388 * @event columlockchange
5389 * Fires when a column's locked state is changed
5390 * @param {ColumnModel} this
5391 * @param {Number} colIndex
5392 * @param {Boolean} locked true if locked
5394 "columnlockchange" : true
5396 Roo.grid.ColumnModel.superclass.constructor.call(this);
5398 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5400 * @cfg {String} header The header text to display in the Grid view.
5403 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5404 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5405 * specified, the column's index is used as an index into the Record's data Array.
5408 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5409 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5412 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5413 * Defaults to the value of the {@link #defaultSortable} property.
5414 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5417 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5420 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5423 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5426 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5429 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5430 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5431 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5432 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5435 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5438 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5441 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5444 * @cfg {String} cursor (Optional)
5447 * @cfg {String} tooltip (Optional)
5450 * @cfg {Number} xs (Optional)
5453 * @cfg {Number} sm (Optional)
5456 * @cfg {Number} md (Optional)
5459 * @cfg {Number} lg (Optional)
5462 * Returns the id of the column at the specified index.
5463 * @param {Number} index The column index
5464 * @return {String} the id
5466 getColumnId : function(index){
5467 return this.config[index].id;
5471 * Returns the column for a specified id.
5472 * @param {String} id The column id
5473 * @return {Object} the column
5475 getColumnById : function(id){
5476 return this.lookup[id];
5481 * Returns the column for a specified dataIndex.
5482 * @param {String} dataIndex The column dataIndex
5483 * @return {Object|Boolean} the column or false if not found
5485 getColumnByDataIndex: function(dataIndex){
5486 var index = this.findColumnIndex(dataIndex);
5487 return index > -1 ? this.config[index] : false;
5491 * Returns the index for a specified column id.
5492 * @param {String} id The column id
5493 * @return {Number} the index, or -1 if not found
5495 getIndexById : function(id){
5496 for(var i = 0, len = this.config.length; i < len; i++){
5497 if(this.config[i].id == id){
5505 * Returns the index for a specified column dataIndex.
5506 * @param {String} dataIndex The column dataIndex
5507 * @return {Number} the index, or -1 if not found
5510 findColumnIndex : function(dataIndex){
5511 for(var i = 0, len = this.config.length; i < len; i++){
5512 if(this.config[i].dataIndex == dataIndex){
5520 moveColumn : function(oldIndex, newIndex){
5521 var c = this.config[oldIndex];
5522 this.config.splice(oldIndex, 1);
5523 this.config.splice(newIndex, 0, c);
5524 this.dataMap = null;
5525 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5528 isLocked : function(colIndex){
5529 return this.config[colIndex].locked === true;
5532 setLocked : function(colIndex, value, suppressEvent){
5533 if(this.isLocked(colIndex) == value){
5536 this.config[colIndex].locked = value;
5538 this.fireEvent("columnlockchange", this, colIndex, value);
5542 getTotalLockedWidth : function(){
5544 for(var i = 0; i < this.config.length; i++){
5545 if(this.isLocked(i) && !this.isHidden(i)){
5546 this.totalWidth += this.getColumnWidth(i);
5552 getLockedCount : function(){
5553 for(var i = 0, len = this.config.length; i < len; i++){
5554 if(!this.isLocked(i)){
5559 return this.config.length;
5563 * Returns the number of columns.
5566 getColumnCount : function(visibleOnly){
5567 if(visibleOnly === true){
5569 for(var i = 0, len = this.config.length; i < len; i++){
5570 if(!this.isHidden(i)){
5576 return this.config.length;
5580 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5581 * @param {Function} fn
5582 * @param {Object} scope (optional)
5583 * @return {Array} result
5585 getColumnsBy : function(fn, scope){
5587 for(var i = 0, len = this.config.length; i < len; i++){
5588 var c = this.config[i];
5589 if(fn.call(scope||this, c, i) === true){
5597 * Returns true if the specified column is sortable.
5598 * @param {Number} col The column index
5601 isSortable : function(col){
5602 if(typeof this.config[col].sortable == "undefined"){
5603 return this.defaultSortable;
5605 return this.config[col].sortable;
5609 * Returns the rendering (formatting) function defined for the column.
5610 * @param {Number} col The column index.
5611 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5613 getRenderer : function(col){
5614 if(!this.config[col].renderer){
5615 return Roo.grid.ColumnModel.defaultRenderer;
5617 return this.config[col].renderer;
5621 * Sets the rendering (formatting) function for a column.
5622 * @param {Number} col The column index
5623 * @param {Function} fn The function to use to process the cell's raw data
5624 * to return HTML markup for the grid view. The render function is called with
5625 * the following parameters:<ul>
5626 * <li>Data value.</li>
5627 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5628 * <li>css A CSS style string to apply to the table cell.</li>
5629 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5630 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5631 * <li>Row index</li>
5632 * <li>Column index</li>
5633 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5635 setRenderer : function(col, fn){
5636 this.config[col].renderer = fn;
5640 * Returns the width for the specified column.
5641 * @param {Number} col The column index
5644 getColumnWidth : function(col){
5645 return this.config[col].width * 1 || this.defaultWidth;
5649 * Sets the width for a column.
5650 * @param {Number} col The column index
5651 * @param {Number} width The new width
5653 setColumnWidth : function(col, width, suppressEvent){
5654 this.config[col].width = width;
5655 this.totalWidth = null;
5657 this.fireEvent("widthchange", this, col, width);
5662 * Returns the total width of all columns.
5663 * @param {Boolean} includeHidden True to include hidden column widths
5666 getTotalWidth : function(includeHidden){
5667 if(!this.totalWidth){
5668 this.totalWidth = 0;
5669 for(var i = 0, len = this.config.length; i < len; i++){
5670 if(includeHidden || !this.isHidden(i)){
5671 this.totalWidth += this.getColumnWidth(i);
5675 return this.totalWidth;
5679 * Returns the header for the specified column.
5680 * @param {Number} col The column index
5683 getColumnHeader : function(col){
5684 return this.config[col].header;
5688 * Sets the header for a column.
5689 * @param {Number} col The column index
5690 * @param {String} header The new header
5692 setColumnHeader : function(col, header){
5693 this.config[col].header = header;
5694 this.fireEvent("headerchange", this, col, header);
5698 * Returns the tooltip for the specified column.
5699 * @param {Number} col The column index
5702 getColumnTooltip : function(col){
5703 return this.config[col].tooltip;
5706 * Sets the tooltip for a column.
5707 * @param {Number} col The column index
5708 * @param {String} tooltip The new tooltip
5710 setColumnTooltip : function(col, tooltip){
5711 this.config[col].tooltip = tooltip;
5715 * Returns the dataIndex for the specified column.
5716 * @param {Number} col The column index
5719 getDataIndex : function(col){
5720 return this.config[col].dataIndex;
5724 * Sets the dataIndex for a column.
5725 * @param {Number} col The column index
5726 * @param {Number} dataIndex The new dataIndex
5728 setDataIndex : function(col, dataIndex){
5729 this.config[col].dataIndex = dataIndex;
5735 * Returns true if the cell is editable.
5736 * @param {Number} colIndex The column index
5737 * @param {Number} rowIndex The row index - this is nto actually used..?
5740 isCellEditable : function(colIndex, rowIndex){
5741 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5745 * Returns the editor defined for the cell/column.
5746 * return false or null to disable editing.
5747 * @param {Number} colIndex The column index
5748 * @param {Number} rowIndex The row index
5751 getCellEditor : function(colIndex, rowIndex){
5752 return this.config[colIndex].editor;
5756 * Sets if a column is editable.
5757 * @param {Number} col The column index
5758 * @param {Boolean} editable True if the column is editable
5760 setEditable : function(col, editable){
5761 this.config[col].editable = editable;
5766 * Returns true if the column is hidden.
5767 * @param {Number} colIndex The column index
5770 isHidden : function(colIndex){
5771 return this.config[colIndex].hidden;
5776 * Returns true if the column width cannot be changed
5778 isFixed : function(colIndex){
5779 return this.config[colIndex].fixed;
5783 * Returns true if the column can be resized
5786 isResizable : function(colIndex){
5787 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5790 * Sets if a column is hidden.
5791 * @param {Number} colIndex The column index
5792 * @param {Boolean} hidden True if the column is hidden
5794 setHidden : function(colIndex, hidden){
5795 this.config[colIndex].hidden = hidden;
5796 this.totalWidth = null;
5797 this.fireEvent("hiddenchange", this, colIndex, hidden);
5801 * Sets the editor for a column.
5802 * @param {Number} col The column index
5803 * @param {Object} editor The editor object
5805 setEditor : function(col, editor){
5806 this.config[col].editor = editor;
5810 Roo.grid.ColumnModel.defaultRenderer = function(value)
5812 if(typeof value == "object") {
5815 if(typeof value == "string" && value.length < 1){
5819 return String.format("{0}", value);
5822 // Alias for backwards compatibility
5823 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5826 * Ext JS Library 1.1.1
5827 * Copyright(c) 2006-2007, Ext JS, LLC.
5829 * Originally Released Under LGPL - original licence link has changed is not relivant.
5832 * <script type="text/javascript">
5836 * @class Roo.LoadMask
5837 * A simple utility class for generically masking elements while loading data. If the element being masked has
5838 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5839 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5840 * element's UpdateManager load indicator and will be destroyed after the initial load.
5842 * Create a new LoadMask
5843 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5844 * @param {Object} config The config object
5846 Roo.LoadMask = function(el, config){
5847 this.el = Roo.get(el);
5848 Roo.apply(this, config);
5850 this.store.on('beforeload', this.onBeforeLoad, this);
5851 this.store.on('load', this.onLoad, this);
5852 this.store.on('loadexception', this.onLoadException, this);
5853 this.removeMask = false;
5855 var um = this.el.getUpdateManager();
5856 um.showLoadIndicator = false; // disable the default indicator
5857 um.on('beforeupdate', this.onBeforeLoad, this);
5858 um.on('update', this.onLoad, this);
5859 um.on('failure', this.onLoad, this);
5860 this.removeMask = true;
5864 Roo.LoadMask.prototype = {
5866 * @cfg {Boolean} removeMask
5867 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5868 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5872 * The text to display in a centered loading message box (defaults to 'Loading...')
5876 * @cfg {String} msgCls
5877 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5879 msgCls : 'x-mask-loading',
5882 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5888 * Disables the mask to prevent it from being displayed
5890 disable : function(){
5891 this.disabled = true;
5895 * Enables the mask so that it can be displayed
5897 enable : function(){
5898 this.disabled = false;
5901 onLoadException : function()
5905 if (typeof(arguments[3]) != 'undefined') {
5906 Roo.MessageBox.alert("Error loading",arguments[3]);
5910 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5911 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5918 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5923 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5927 onBeforeLoad : function(){
5929 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5934 destroy : function(){
5936 this.store.un('beforeload', this.onBeforeLoad, this);
5937 this.store.un('load', this.onLoad, this);
5938 this.store.un('loadexception', this.onLoadException, this);
5940 var um = this.el.getUpdateManager();
5941 um.un('beforeupdate', this.onBeforeLoad, this);
5942 um.un('update', this.onLoad, this);
5943 um.un('failure', this.onLoad, this);
5954 * @class Roo.bootstrap.Table
5955 * @extends Roo.bootstrap.Component
5956 * Bootstrap Table class
5957 * @cfg {String} cls table class
5958 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5959 * @cfg {String} bgcolor Specifies the background color for a table
5960 * @cfg {Number} border Specifies whether the table cells should have borders or not
5961 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5962 * @cfg {Number} cellspacing Specifies the space between cells
5963 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5964 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5965 * @cfg {String} sortable Specifies that the table should be sortable
5966 * @cfg {String} summary Specifies a summary of the content of a table
5967 * @cfg {Number} width Specifies the width of a table
5968 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5970 * @cfg {boolean} striped Should the rows be alternative striped
5971 * @cfg {boolean} bordered Add borders to the table
5972 * @cfg {boolean} hover Add hover highlighting
5973 * @cfg {boolean} condensed Format condensed
5974 * @cfg {boolean} responsive Format condensed
5975 * @cfg {Boolean} loadMask (true|false) default false
5976 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5977 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5978 * @cfg {Boolean} rowSelection (true|false) default false
5979 * @cfg {Boolean} cellSelection (true|false) default false
5980 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5981 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5982 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5983 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5987 * Create a new Table
5988 * @param {Object} config The config object
5991 Roo.bootstrap.Table = function(config){
5992 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5997 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5998 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5999 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6000 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6002 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6004 this.sm.grid = this;
6005 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6006 this.sm = this.selModel;
6007 this.sm.xmodule = this.xmodule || false;
6010 if (this.cm && typeof(this.cm.config) == 'undefined') {
6011 this.colModel = new Roo.grid.ColumnModel(this.cm);
6012 this.cm = this.colModel;
6013 this.cm.xmodule = this.xmodule || false;
6016 this.store= Roo.factory(this.store, Roo.data);
6017 this.ds = this.store;
6018 this.ds.xmodule = this.xmodule || false;
6021 if (this.footer && this.store) {
6022 this.footer.dataSource = this.ds;
6023 this.footer = Roo.factory(this.footer);
6030 * Fires when a cell is clicked
6031 * @param {Roo.bootstrap.Table} this
6032 * @param {Roo.Element} el
6033 * @param {Number} rowIndex
6034 * @param {Number} columnIndex
6035 * @param {Roo.EventObject} e
6039 * @event celldblclick
6040 * Fires when a cell is double clicked
6041 * @param {Roo.bootstrap.Table} this
6042 * @param {Roo.Element} el
6043 * @param {Number} rowIndex
6044 * @param {Number} columnIndex
6045 * @param {Roo.EventObject} e
6047 "celldblclick" : true,
6050 * Fires when a row is clicked
6051 * @param {Roo.bootstrap.Table} this
6052 * @param {Roo.Element} el
6053 * @param {Number} rowIndex
6054 * @param {Roo.EventObject} e
6058 * @event rowdblclick
6059 * Fires when a row is double clicked
6060 * @param {Roo.bootstrap.Table} this
6061 * @param {Roo.Element} el
6062 * @param {Number} rowIndex
6063 * @param {Roo.EventObject} e
6065 "rowdblclick" : true,
6068 * Fires when a mouseover occur
6069 * @param {Roo.bootstrap.Table} this
6070 * @param {Roo.Element} el
6071 * @param {Number} rowIndex
6072 * @param {Number} columnIndex
6073 * @param {Roo.EventObject} e
6078 * Fires when a mouseout occur
6079 * @param {Roo.bootstrap.Table} this
6080 * @param {Roo.Element} el
6081 * @param {Number} rowIndex
6082 * @param {Number} columnIndex
6083 * @param {Roo.EventObject} e
6088 * Fires when a row is rendered, so you can change add a style to it.
6089 * @param {Roo.bootstrap.Table} this
6090 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6094 * @event rowsrendered
6095 * Fires when all the rows have been rendered
6096 * @param {Roo.bootstrap.Table} this
6098 'rowsrendered' : true,
6100 * @event contextmenu
6101 * The raw contextmenu event for the entire grid.
6102 * @param {Roo.EventObject} e
6104 "contextmenu" : true,
6106 * @event rowcontextmenu
6107 * Fires when a row is right clicked
6108 * @param {Roo.bootstrap.Table} this
6109 * @param {Number} rowIndex
6110 * @param {Roo.EventObject} e
6112 "rowcontextmenu" : true,
6114 * @event cellcontextmenu
6115 * Fires when a cell is right clicked
6116 * @param {Roo.bootstrap.Table} this
6117 * @param {Number} rowIndex
6118 * @param {Number} cellIndex
6119 * @param {Roo.EventObject} e
6121 "cellcontextmenu" : true,
6123 * @event headercontextmenu
6124 * Fires when a header is right clicked
6125 * @param {Roo.bootstrap.Table} this
6126 * @param {Number} columnIndex
6127 * @param {Roo.EventObject} e
6129 "headercontextmenu" : true
6133 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6159 rowSelection : false,
6160 cellSelection : false,
6163 // Roo.Element - the tbody
6165 // Roo.Element - thead element
6168 container: false, // used by gridpanel...
6174 auto_hide_footer : false,
6176 getAutoCreate : function()
6178 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6185 if (this.scrollBody) {
6186 cfg.cls += ' table-body-fixed';
6189 cfg.cls += ' table-striped';
6193 cfg.cls += ' table-hover';
6195 if (this.bordered) {
6196 cfg.cls += ' table-bordered';
6198 if (this.condensed) {
6199 cfg.cls += ' table-condensed';
6201 if (this.responsive) {
6202 cfg.cls += ' table-responsive';
6206 cfg.cls+= ' ' +this.cls;
6209 // this lot should be simplifed...
6222 ].forEach(function(k) {
6230 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6233 if(this.store || this.cm){
6234 if(this.headerShow){
6235 cfg.cn.push(this.renderHeader());
6238 cfg.cn.push(this.renderBody());
6240 if(this.footerShow){
6241 cfg.cn.push(this.renderFooter());
6243 // where does this come from?
6244 //cfg.cls+= ' TableGrid';
6247 return { cn : [ cfg ] };
6250 initEvents : function()
6252 if(!this.store || !this.cm){
6255 if (this.selModel) {
6256 this.selModel.initEvents();
6260 //Roo.log('initEvents with ds!!!!');
6262 this.mainBody = this.el.select('tbody', true).first();
6263 this.mainHead = this.el.select('thead', true).first();
6264 this.mainFoot = this.el.select('tfoot', true).first();
6270 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6271 e.on('click', _this.sort, _this);
6274 this.mainBody.on("click", this.onClick, this);
6275 this.mainBody.on("dblclick", this.onDblClick, this);
6277 // why is this done????? = it breaks dialogs??
6278 //this.parent().el.setStyle('position', 'relative');
6282 this.footer.parentId = this.id;
6283 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6286 this.el.select('tfoot tr td').first().addClass('hide');
6291 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6294 this.store.on('load', this.onLoad, this);
6295 this.store.on('beforeload', this.onBeforeLoad, this);
6296 this.store.on('update', this.onUpdate, this);
6297 this.store.on('add', this.onAdd, this);
6298 this.store.on("clear", this.clear, this);
6300 this.el.on("contextmenu", this.onContextMenu, this);
6302 this.mainBody.on('scroll', this.onBodyScroll, this);
6304 this.cm.on("headerchange", this.onHeaderChange, this);
6306 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6310 onContextMenu : function(e, t)
6312 this.processEvent("contextmenu", e);
6315 processEvent : function(name, e)
6317 if (name != 'touchstart' ) {
6318 this.fireEvent(name, e);
6321 var t = e.getTarget();
6323 var cell = Roo.get(t);
6329 if(cell.findParent('tfoot', false, true)){
6333 if(cell.findParent('thead', false, true)){
6335 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6336 cell = Roo.get(t).findParent('th', false, true);
6338 Roo.log("failed to find th in thead?");
6339 Roo.log(e.getTarget());
6344 var cellIndex = cell.dom.cellIndex;
6346 var ename = name == 'touchstart' ? 'click' : name;
6347 this.fireEvent("header" + ename, this, cellIndex, e);
6352 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6353 cell = Roo.get(t).findParent('td', false, true);
6355 Roo.log("failed to find th in tbody?");
6356 Roo.log(e.getTarget());
6361 var row = cell.findParent('tr', false, true);
6362 var cellIndex = cell.dom.cellIndex;
6363 var rowIndex = row.dom.rowIndex - 1;
6367 this.fireEvent("row" + name, this, rowIndex, e);
6371 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6377 onMouseover : function(e, el)
6379 var cell = Roo.get(el);
6385 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6386 cell = cell.findParent('td', false, true);
6389 var row = cell.findParent('tr', false, true);
6390 var cellIndex = cell.dom.cellIndex;
6391 var rowIndex = row.dom.rowIndex - 1; // start from 0
6393 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6397 onMouseout : function(e, el)
6399 var cell = Roo.get(el);
6405 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6406 cell = cell.findParent('td', false, true);
6409 var row = cell.findParent('tr', false, true);
6410 var cellIndex = cell.dom.cellIndex;
6411 var rowIndex = row.dom.rowIndex - 1; // start from 0
6413 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6417 onClick : function(e, el)
6419 var cell = Roo.get(el);
6421 if(!cell || (!this.cellSelection && !this.rowSelection)){
6425 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6426 cell = cell.findParent('td', false, true);
6429 if(!cell || typeof(cell) == 'undefined'){
6433 var row = cell.findParent('tr', false, true);
6435 if(!row || typeof(row) == 'undefined'){
6439 var cellIndex = cell.dom.cellIndex;
6440 var rowIndex = this.getRowIndex(row);
6442 // why??? - should these not be based on SelectionModel?
6443 if(this.cellSelection){
6444 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6447 if(this.rowSelection){
6448 this.fireEvent('rowclick', this, row, rowIndex, e);
6454 onDblClick : function(e,el)
6456 var cell = Roo.get(el);
6458 if(!cell || (!this.cellSelection && !this.rowSelection)){
6462 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6463 cell = cell.findParent('td', false, true);
6466 if(!cell || typeof(cell) == 'undefined'){
6470 var row = cell.findParent('tr', false, true);
6472 if(!row || typeof(row) == 'undefined'){
6476 var cellIndex = cell.dom.cellIndex;
6477 var rowIndex = this.getRowIndex(row);
6479 if(this.cellSelection){
6480 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6483 if(this.rowSelection){
6484 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6488 sort : function(e,el)
6490 var col = Roo.get(el);
6492 if(!col.hasClass('sortable')){
6496 var sort = col.attr('sort');
6499 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6503 this.store.sortInfo = {field : sort, direction : dir};
6506 Roo.log("calling footer first");
6507 this.footer.onClick('first');
6510 this.store.load({ params : { start : 0 } });
6514 renderHeader : function()
6522 this.totalWidth = 0;
6524 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6526 var config = cm.config[i];
6530 cls : 'x-hcol-' + i,
6532 html: cm.getColumnHeader(i)
6537 if(typeof(config.sortable) != 'undefined' && config.sortable){
6539 c.html = '<i class="glyphicon"></i>' + c.html;
6542 if(typeof(config.lgHeader) != 'undefined'){
6543 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6546 if(typeof(config.mdHeader) != 'undefined'){
6547 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6550 if(typeof(config.smHeader) != 'undefined'){
6551 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6554 if(typeof(config.xsHeader) != 'undefined'){
6555 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6562 if(typeof(config.tooltip) != 'undefined'){
6563 c.tooltip = config.tooltip;
6566 if(typeof(config.colspan) != 'undefined'){
6567 c.colspan = config.colspan;
6570 if(typeof(config.hidden) != 'undefined' && config.hidden){
6571 c.style += ' display:none;';
6574 if(typeof(config.dataIndex) != 'undefined'){
6575 c.sort = config.dataIndex;
6580 if(typeof(config.align) != 'undefined' && config.align.length){
6581 c.style += ' text-align:' + config.align + ';';
6584 if(typeof(config.width) != 'undefined'){
6585 c.style += ' width:' + config.width + 'px;';
6586 this.totalWidth += config.width;
6588 this.totalWidth += 100; // assume minimum of 100 per column?
6591 if(typeof(config.cls) != 'undefined'){
6592 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6595 ['xs','sm','md','lg'].map(function(size){
6597 if(typeof(config[size]) == 'undefined'){
6601 if (!config[size]) { // 0 = hidden
6602 c.cls += ' hidden-' + size;
6606 c.cls += ' col-' + size + '-' + config[size];
6616 renderBody : function()
6626 colspan : this.cm.getColumnCount()
6636 renderFooter : function()
6646 colspan : this.cm.getColumnCount()
6660 // Roo.log('ds onload');
6665 var ds = this.store;
6667 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6668 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6669 if (_this.store.sortInfo) {
6671 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6672 e.select('i', true).addClass(['glyphicon-arrow-up']);
6675 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6676 e.select('i', true).addClass(['glyphicon-arrow-down']);
6681 var tbody = this.mainBody;
6683 if(ds.getCount() > 0){
6684 ds.data.each(function(d,rowIndex){
6685 var row = this.renderRow(cm, ds, rowIndex);
6687 tbody.createChild(row);
6691 if(row.cellObjects.length){
6692 Roo.each(row.cellObjects, function(r){
6693 _this.renderCellObject(r);
6700 var tfoot = this.el.select('tfoot', true).first();
6702 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6704 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6706 var total = this.ds.getTotalCount();
6708 if(this.footer.pageSize < total){
6709 this.mainFoot.show();
6713 Roo.each(this.el.select('tbody td', true).elements, function(e){
6714 e.on('mouseover', _this.onMouseover, _this);
6717 Roo.each(this.el.select('tbody td', true).elements, function(e){
6718 e.on('mouseout', _this.onMouseout, _this);
6720 this.fireEvent('rowsrendered', this);
6726 onUpdate : function(ds,record)
6728 this.refreshRow(record);
6732 onRemove : function(ds, record, index, isUpdate){
6733 if(isUpdate !== true){
6734 this.fireEvent("beforerowremoved", this, index, record);
6736 var bt = this.mainBody.dom;
6738 var rows = this.el.select('tbody > tr', true).elements;
6740 if(typeof(rows[index]) != 'undefined'){
6741 bt.removeChild(rows[index].dom);
6744 // if(bt.rows[index]){
6745 // bt.removeChild(bt.rows[index]);
6748 if(isUpdate !== true){
6749 //this.stripeRows(index);
6750 //this.syncRowHeights(index, index);
6752 this.fireEvent("rowremoved", this, index, record);
6756 onAdd : function(ds, records, rowIndex)
6758 //Roo.log('on Add called');
6759 // - note this does not handle multiple adding very well..
6760 var bt = this.mainBody.dom;
6761 for (var i =0 ; i < records.length;i++) {
6762 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6763 //Roo.log(records[i]);
6764 //Roo.log(this.store.getAt(rowIndex+i));
6765 this.insertRow(this.store, rowIndex + i, false);
6772 refreshRow : function(record){
6773 var ds = this.store, index;
6774 if(typeof record == 'number'){
6776 record = ds.getAt(index);
6778 index = ds.indexOf(record);
6780 this.insertRow(ds, index, true);
6782 this.onRemove(ds, record, index+1, true);
6784 //this.syncRowHeights(index, index);
6786 this.fireEvent("rowupdated", this, index, record);
6789 insertRow : function(dm, rowIndex, isUpdate){
6792 this.fireEvent("beforerowsinserted", this, rowIndex);
6794 //var s = this.getScrollState();
6795 var row = this.renderRow(this.cm, this.store, rowIndex);
6796 // insert before rowIndex..
6797 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6801 if(row.cellObjects.length){
6802 Roo.each(row.cellObjects, function(r){
6803 _this.renderCellObject(r);
6808 this.fireEvent("rowsinserted", this, rowIndex);
6809 //this.syncRowHeights(firstRow, lastRow);
6810 //this.stripeRows(firstRow);
6817 getRowDom : function(rowIndex)
6819 var rows = this.el.select('tbody > tr', true).elements;
6821 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6824 // returns the object tree for a tr..
6827 renderRow : function(cm, ds, rowIndex)
6829 var d = ds.getAt(rowIndex);
6833 cls : 'x-row-' + rowIndex,
6837 var cellObjects = [];
6839 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6840 var config = cm.config[i];
6842 var renderer = cm.getRenderer(i);
6846 if(typeof(renderer) !== 'undefined'){
6847 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6849 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6850 // and are rendered into the cells after the row is rendered - using the id for the element.
6852 if(typeof(value) === 'object'){
6862 rowIndex : rowIndex,
6867 this.fireEvent('rowclass', this, rowcfg);
6871 cls : rowcfg.rowClass + ' x-col-' + i,
6873 html: (typeof(value) === 'object') ? '' : value
6880 if(typeof(config.colspan) != 'undefined'){
6881 td.colspan = config.colspan;
6884 if(typeof(config.hidden) != 'undefined' && config.hidden){
6885 td.style += ' display:none;';
6888 if(typeof(config.align) != 'undefined' && config.align.length){
6889 td.style += ' text-align:' + config.align + ';';
6891 if(typeof(config.valign) != 'undefined' && config.valign.length){
6892 td.style += ' vertical-align:' + config.valign + ';';
6895 if(typeof(config.width) != 'undefined'){
6896 td.style += ' width:' + config.width + 'px;';
6899 if(typeof(config.cursor) != 'undefined'){
6900 td.style += ' cursor:' + config.cursor + ';';
6903 if(typeof(config.cls) != 'undefined'){
6904 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6907 ['xs','sm','md','lg'].map(function(size){
6909 if(typeof(config[size]) == 'undefined'){
6913 if (!config[size]) { // 0 = hidden
6914 td.cls += ' hidden-' + size;
6918 td.cls += ' col-' + size + '-' + config[size];
6926 row.cellObjects = cellObjects;
6934 onBeforeLoad : function()
6943 this.el.select('tbody', true).first().dom.innerHTML = '';
6946 * Show or hide a row.
6947 * @param {Number} rowIndex to show or hide
6948 * @param {Boolean} state hide
6950 setRowVisibility : function(rowIndex, state)
6952 var bt = this.mainBody.dom;
6954 var rows = this.el.select('tbody > tr', true).elements;
6956 if(typeof(rows[rowIndex]) == 'undefined'){
6959 rows[rowIndex].dom.style.display = state ? '' : 'none';
6963 getSelectionModel : function(){
6965 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6967 return this.selModel;
6970 * Render the Roo.bootstrap object from renderder
6972 renderCellObject : function(r)
6976 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6978 var t = r.cfg.render(r.container);
6981 Roo.each(r.cfg.cn, function(c){
6983 container: t.getChildContainer(),
6986 _this.renderCellObject(child);
6991 getRowIndex : function(row)
6995 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7006 * Returns the grid's underlying element = used by panel.Grid
7007 * @return {Element} The element
7009 getGridEl : function(){
7013 * Forces a resize - used by panel.Grid
7014 * @return {Element} The element
7016 autoSize : function()
7018 //var ctr = Roo.get(this.container.dom.parentElement);
7019 var ctr = Roo.get(this.el.dom);
7021 var thd = this.getGridEl().select('thead',true).first();
7022 var tbd = this.getGridEl().select('tbody', true).first();
7023 var tfd = this.getGridEl().select('tfoot', true).first();
7025 var cw = ctr.getWidth();
7029 tbd.setSize(ctr.getWidth(),
7030 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7032 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7035 cw = Math.max(cw, this.totalWidth);
7036 this.getGridEl().select('tr',true).setWidth(cw);
7037 // resize 'expandable coloumn?
7039 return; // we doe not have a view in this design..
7042 onBodyScroll: function()
7044 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7046 this.mainHead.setStyle({
7047 'position' : 'relative',
7048 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7054 var scrollHeight = this.mainBody.dom.scrollHeight;
7056 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7058 var height = this.mainBody.getHeight();
7060 if(scrollHeight - height == scrollTop) {
7062 var total = this.ds.getTotalCount();
7064 if(this.footer.cursor + this.footer.pageSize < total){
7066 this.footer.ds.load({
7068 start : this.footer.cursor + this.footer.pageSize,
7069 limit : this.footer.pageSize
7079 onHeaderChange : function()
7081 var header = this.renderHeader();
7082 var table = this.el.select('table', true).first();
7084 this.mainHead.remove();
7085 this.mainHead = table.createChild(header, this.mainBody, false);
7088 onHiddenChange : function(colModel, colIndex, hidden)
7090 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7091 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7093 this.CSS.updateRule(thSelector, "display", "");
7094 this.CSS.updateRule(tdSelector, "display", "");
7097 this.CSS.updateRule(thSelector, "display", "none");
7098 this.CSS.updateRule(tdSelector, "display", "none");
7101 this.onHeaderChange();
7105 setColumnWidth: function(col_index, width)
7107 // width = "md-2 xs-2..."
7108 if(!this.colModel.config[col_index]) {
7112 var w = width.split(" ");
7114 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7116 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7119 for(var j = 0; j < w.length; j++) {
7125 var size_cls = w[j].split("-");
7127 if(!Number.isInteger(size_cls[1] * 1)) {
7131 if(!this.colModel.config[col_index][size_cls[0]]) {
7135 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7139 h_row[0].classList.replace(
7140 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7141 "col-"+size_cls[0]+"-"+size_cls[1]
7144 for(var i = 0; i < rows.length; i++) {
7146 var size_cls = w[j].split("-");
7148 if(!Number.isInteger(size_cls[1] * 1)) {
7152 if(!this.colModel.config[col_index][size_cls[0]]) {
7156 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7160 rows[i].classList.replace(
7161 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7162 "col-"+size_cls[0]+"-"+size_cls[1]
7166 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7181 * @class Roo.bootstrap.TableCell
7182 * @extends Roo.bootstrap.Component
7183 * Bootstrap TableCell class
7184 * @cfg {String} html cell contain text
7185 * @cfg {String} cls cell class
7186 * @cfg {String} tag cell tag (td|th) default td
7187 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7188 * @cfg {String} align Aligns the content in a cell
7189 * @cfg {String} axis Categorizes cells
7190 * @cfg {String} bgcolor Specifies the background color of a cell
7191 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7192 * @cfg {Number} colspan Specifies the number of columns a cell should span
7193 * @cfg {String} headers Specifies one or more header cells a cell is related to
7194 * @cfg {Number} height Sets the height of a cell
7195 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7196 * @cfg {Number} rowspan Sets the number of rows a cell should span
7197 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7198 * @cfg {String} valign Vertical aligns the content in a cell
7199 * @cfg {Number} width Specifies the width of a cell
7202 * Create a new TableCell
7203 * @param {Object} config The config object
7206 Roo.bootstrap.TableCell = function(config){
7207 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7210 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7230 getAutoCreate : function(){
7231 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7251 cfg.align=this.align
7257 cfg.bgcolor=this.bgcolor
7260 cfg.charoff=this.charoff
7263 cfg.colspan=this.colspan
7266 cfg.headers=this.headers
7269 cfg.height=this.height
7272 cfg.nowrap=this.nowrap
7275 cfg.rowspan=this.rowspan
7278 cfg.scope=this.scope
7281 cfg.valign=this.valign
7284 cfg.width=this.width
7303 * @class Roo.bootstrap.TableRow
7304 * @extends Roo.bootstrap.Component
7305 * Bootstrap TableRow class
7306 * @cfg {String} cls row class
7307 * @cfg {String} align Aligns the content in a table row
7308 * @cfg {String} bgcolor Specifies a background color for a table row
7309 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7310 * @cfg {String} valign Vertical aligns the content in a table row
7313 * Create a new TableRow
7314 * @param {Object} config The config object
7317 Roo.bootstrap.TableRow = function(config){
7318 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7321 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7329 getAutoCreate : function(){
7330 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7340 cfg.align = this.align;
7343 cfg.bgcolor = this.bgcolor;
7346 cfg.charoff = this.charoff;
7349 cfg.valign = this.valign;
7367 * @class Roo.bootstrap.TableBody
7368 * @extends Roo.bootstrap.Component
7369 * Bootstrap TableBody class
7370 * @cfg {String} cls element class
7371 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7372 * @cfg {String} align Aligns the content inside the element
7373 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7374 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7377 * Create a new TableBody
7378 * @param {Object} config The config object
7381 Roo.bootstrap.TableBody = function(config){
7382 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7385 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7393 getAutoCreate : function(){
7394 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7408 cfg.align = this.align;
7411 cfg.charoff = this.charoff;
7414 cfg.valign = this.valign;
7421 // initEvents : function()
7428 // this.store = Roo.factory(this.store, Roo.data);
7429 // this.store.on('load', this.onLoad, this);
7431 // this.store.load();
7435 // onLoad: function ()
7437 // this.fireEvent('load', this);
7447 * Ext JS Library 1.1.1
7448 * Copyright(c) 2006-2007, Ext JS, LLC.
7450 * Originally Released Under LGPL - original licence link has changed is not relivant.
7453 * <script type="text/javascript">
7456 // as we use this in bootstrap.
7457 Roo.namespace('Roo.form');
7459 * @class Roo.form.Action
7460 * Internal Class used to handle form actions
7462 * @param {Roo.form.BasicForm} el The form element or its id
7463 * @param {Object} config Configuration options
7468 // define the action interface
7469 Roo.form.Action = function(form, options){
7471 this.options = options || {};
7474 * Client Validation Failed
7477 Roo.form.Action.CLIENT_INVALID = 'client';
7479 * Server Validation Failed
7482 Roo.form.Action.SERVER_INVALID = 'server';
7484 * Connect to Server Failed
7487 Roo.form.Action.CONNECT_FAILURE = 'connect';
7489 * Reading Data from Server Failed
7492 Roo.form.Action.LOAD_FAILURE = 'load';
7494 Roo.form.Action.prototype = {
7496 failureType : undefined,
7497 response : undefined,
7501 run : function(options){
7506 success : function(response){
7511 handleResponse : function(response){
7515 // default connection failure
7516 failure : function(response){
7518 this.response = response;
7519 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7520 this.form.afterAction(this, false);
7523 processResponse : function(response){
7524 this.response = response;
7525 if(!response.responseText){
7528 this.result = this.handleResponse(response);
7532 // utility functions used internally
7533 getUrl : function(appendParams){
7534 var url = this.options.url || this.form.url || this.form.el.dom.action;
7536 var p = this.getParams();
7538 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7544 getMethod : function(){
7545 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7548 getParams : function(){
7549 var bp = this.form.baseParams;
7550 var p = this.options.params;
7552 if(typeof p == "object"){
7553 p = Roo.urlEncode(Roo.applyIf(p, bp));
7554 }else if(typeof p == 'string' && bp){
7555 p += '&' + Roo.urlEncode(bp);
7558 p = Roo.urlEncode(bp);
7563 createCallback : function(){
7565 success: this.success,
7566 failure: this.failure,
7568 timeout: (this.form.timeout*1000),
7569 upload: this.form.fileUpload ? this.success : undefined
7574 Roo.form.Action.Submit = function(form, options){
7575 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7578 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7581 haveProgress : false,
7582 uploadComplete : false,
7584 // uploadProgress indicator.
7585 uploadProgress : function()
7587 if (!this.form.progressUrl) {
7591 if (!this.haveProgress) {
7592 Roo.MessageBox.progress("Uploading", "Uploading");
7594 if (this.uploadComplete) {
7595 Roo.MessageBox.hide();
7599 this.haveProgress = true;
7601 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7603 var c = new Roo.data.Connection();
7605 url : this.form.progressUrl,
7610 success : function(req){
7611 //console.log(data);
7615 rdata = Roo.decode(req.responseText)
7617 Roo.log("Invalid data from server..");
7621 if (!rdata || !rdata.success) {
7623 Roo.MessageBox.alert(Roo.encode(rdata));
7626 var data = rdata.data;
7628 if (this.uploadComplete) {
7629 Roo.MessageBox.hide();
7634 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7635 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7638 this.uploadProgress.defer(2000,this);
7641 failure: function(data) {
7642 Roo.log('progress url failed ');
7653 // run get Values on the form, so it syncs any secondary forms.
7654 this.form.getValues();
7656 var o = this.options;
7657 var method = this.getMethod();
7658 var isPost = method == 'POST';
7659 if(o.clientValidation === false || this.form.isValid()){
7661 if (this.form.progressUrl) {
7662 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7663 (new Date() * 1) + '' + Math.random());
7668 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7669 form:this.form.el.dom,
7670 url:this.getUrl(!isPost),
7672 params:isPost ? this.getParams() : null,
7673 isUpload: this.form.fileUpload
7676 this.uploadProgress();
7678 }else if (o.clientValidation !== false){ // client validation failed
7679 this.failureType = Roo.form.Action.CLIENT_INVALID;
7680 this.form.afterAction(this, false);
7684 success : function(response)
7686 this.uploadComplete= true;
7687 if (this.haveProgress) {
7688 Roo.MessageBox.hide();
7692 var result = this.processResponse(response);
7693 if(result === true || result.success){
7694 this.form.afterAction(this, true);
7698 this.form.markInvalid(result.errors);
7699 this.failureType = Roo.form.Action.SERVER_INVALID;
7701 this.form.afterAction(this, false);
7703 failure : function(response)
7705 this.uploadComplete= true;
7706 if (this.haveProgress) {
7707 Roo.MessageBox.hide();
7710 this.response = response;
7711 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7712 this.form.afterAction(this, false);
7715 handleResponse : function(response){
7716 if(this.form.errorReader){
7717 var rs = this.form.errorReader.read(response);
7720 for(var i = 0, len = rs.records.length; i < len; i++) {
7721 var r = rs.records[i];
7725 if(errors.length < 1){
7729 success : rs.success,
7735 ret = Roo.decode(response.responseText);
7739 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7749 Roo.form.Action.Load = function(form, options){
7750 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7751 this.reader = this.form.reader;
7754 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7759 Roo.Ajax.request(Roo.apply(
7760 this.createCallback(), {
7761 method:this.getMethod(),
7762 url:this.getUrl(false),
7763 params:this.getParams()
7767 success : function(response){
7769 var result = this.processResponse(response);
7770 if(result === true || !result.success || !result.data){
7771 this.failureType = Roo.form.Action.LOAD_FAILURE;
7772 this.form.afterAction(this, false);
7775 this.form.clearInvalid();
7776 this.form.setValues(result.data);
7777 this.form.afterAction(this, true);
7780 handleResponse : function(response){
7781 if(this.form.reader){
7782 var rs = this.form.reader.read(response);
7783 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7785 success : rs.success,
7789 return Roo.decode(response.responseText);
7793 Roo.form.Action.ACTION_TYPES = {
7794 'load' : Roo.form.Action.Load,
7795 'submit' : Roo.form.Action.Submit
7804 * @class Roo.bootstrap.Form
7805 * @extends Roo.bootstrap.Component
7806 * Bootstrap Form class
7807 * @cfg {String} method GET | POST (default POST)
7808 * @cfg {String} labelAlign top | left (default top)
7809 * @cfg {String} align left | right - for navbars
7810 * @cfg {Boolean} loadMask load mask when submit (default true)
7815 * @param {Object} config The config object
7819 Roo.bootstrap.Form = function(config){
7821 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7823 Roo.bootstrap.Form.popover.apply();
7827 * @event clientvalidation
7828 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7829 * @param {Form} this
7830 * @param {Boolean} valid true if the form has passed client-side validation
7832 clientvalidation: true,
7834 * @event beforeaction
7835 * Fires before any action is performed. Return false to cancel the action.
7836 * @param {Form} this
7837 * @param {Action} action The action to be performed
7841 * @event actionfailed
7842 * Fires when an action fails.
7843 * @param {Form} this
7844 * @param {Action} action The action that failed
7846 actionfailed : true,
7848 * @event actioncomplete
7849 * Fires when an action is completed.
7850 * @param {Form} this
7851 * @param {Action} action The action that completed
7853 actioncomplete : true
7857 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7860 * @cfg {String} method
7861 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7866 * The URL to use for form actions if one isn't supplied in the action options.
7869 * @cfg {Boolean} fileUpload
7870 * Set to true if this form is a file upload.
7874 * @cfg {Object} baseParams
7875 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7879 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7883 * @cfg {Sting} align (left|right) for navbar forms
7888 activeAction : null,
7891 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7892 * element by passing it or its id or mask the form itself by passing in true.
7895 waitMsgTarget : false,
7900 * @cfg {Boolean} errorMask (true|false) default false
7905 * @cfg {Number} maskOffset Default 100
7910 * @cfg {Boolean} maskBody
7914 getAutoCreate : function(){
7918 method : this.method || 'POST',
7919 id : this.id || Roo.id(),
7922 if (this.parent().xtype.match(/^Nav/)) {
7923 cfg.cls = 'navbar-form navbar-' + this.align;
7927 if (this.labelAlign == 'left' ) {
7928 cfg.cls += ' form-horizontal';
7934 initEvents : function()
7936 this.el.on('submit', this.onSubmit, this);
7937 // this was added as random key presses on the form where triggering form submit.
7938 this.el.on('keypress', function(e) {
7939 if (e.getCharCode() != 13) {
7942 // we might need to allow it for textareas.. and some other items.
7943 // check e.getTarget().
7945 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7949 Roo.log("keypress blocked");
7957 onSubmit : function(e){
7962 * Returns true if client-side validation on the form is successful.
7965 isValid : function(){
7966 var items = this.getItems();
7970 items.each(function(f){
7976 Roo.log('invalid field: ' + f.name);
7980 if(!target && f.el.isVisible(true)){
7986 if(this.errorMask && !valid){
7987 Roo.bootstrap.Form.popover.mask(this, target);
7994 * Returns true if any fields in this form have changed since their original load.
7997 isDirty : function(){
7999 var items = this.getItems();
8000 items.each(function(f){
8010 * Performs a predefined action (submit or load) or custom actions you define on this form.
8011 * @param {String} actionName The name of the action type
8012 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8013 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8014 * accept other config options):
8016 Property Type Description
8017 ---------------- --------------- ----------------------------------------------------------------------------------
8018 url String The url for the action (defaults to the form's url)
8019 method String The form method to use (defaults to the form's method, or POST if not defined)
8020 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8021 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8022 validate the form on the client (defaults to false)
8024 * @return {BasicForm} this
8026 doAction : function(action, options){
8027 if(typeof action == 'string'){
8028 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8030 if(this.fireEvent('beforeaction', this, action) !== false){
8031 this.beforeAction(action);
8032 action.run.defer(100, action);
8038 beforeAction : function(action){
8039 var o = action.options;
8044 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8046 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8049 // not really supported yet.. ??
8051 //if(this.waitMsgTarget === true){
8052 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8053 //}else if(this.waitMsgTarget){
8054 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8055 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8057 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8063 afterAction : function(action, success){
8064 this.activeAction = null;
8065 var o = action.options;
8070 Roo.get(document.body).unmask();
8076 //if(this.waitMsgTarget === true){
8077 // this.el.unmask();
8078 //}else if(this.waitMsgTarget){
8079 // this.waitMsgTarget.unmask();
8081 // Roo.MessageBox.updateProgress(1);
8082 // Roo.MessageBox.hide();
8089 Roo.callback(o.success, o.scope, [this, action]);
8090 this.fireEvent('actioncomplete', this, action);
8094 // failure condition..
8095 // we have a scenario where updates need confirming.
8096 // eg. if a locking scenario exists..
8097 // we look for { errors : { needs_confirm : true }} in the response.
8099 (typeof(action.result) != 'undefined') &&
8100 (typeof(action.result.errors) != 'undefined') &&
8101 (typeof(action.result.errors.needs_confirm) != 'undefined')
8104 Roo.log("not supported yet");
8107 Roo.MessageBox.confirm(
8108 "Change requires confirmation",
8109 action.result.errorMsg,
8114 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8124 Roo.callback(o.failure, o.scope, [this, action]);
8125 // show an error message if no failed handler is set..
8126 if (!this.hasListener('actionfailed')) {
8127 Roo.log("need to add dialog support");
8129 Roo.MessageBox.alert("Error",
8130 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8131 action.result.errorMsg :
8132 "Saving Failed, please check your entries or try again"
8137 this.fireEvent('actionfailed', this, action);
8142 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8143 * @param {String} id The value to search for
8146 findField : function(id){
8147 var items = this.getItems();
8148 var field = items.get(id);
8150 items.each(function(f){
8151 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8158 return field || null;
8161 * Mark fields in this form invalid in bulk.
8162 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8163 * @return {BasicForm} this
8165 markInvalid : function(errors){
8166 if(errors instanceof Array){
8167 for(var i = 0, len = errors.length; i < len; i++){
8168 var fieldError = errors[i];
8169 var f = this.findField(fieldError.id);
8171 f.markInvalid(fieldError.msg);
8177 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8178 field.markInvalid(errors[id]);
8182 //Roo.each(this.childForms || [], function (f) {
8183 // f.markInvalid(errors);
8190 * Set values for fields in this form in bulk.
8191 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8192 * @return {BasicForm} this
8194 setValues : function(values){
8195 if(values instanceof Array){ // array of objects
8196 for(var i = 0, len = values.length; i < len; i++){
8198 var f = this.findField(v.id);
8200 f.setValue(v.value);
8201 if(this.trackResetOnLoad){
8202 f.originalValue = f.getValue();
8206 }else{ // object hash
8209 if(typeof values[id] != 'function' && (field = this.findField(id))){
8211 if (field.setFromData &&
8213 field.displayField &&
8214 // combos' with local stores can
8215 // be queried via setValue()
8216 // to set their value..
8217 (field.store && !field.store.isLocal)
8221 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8222 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8223 field.setFromData(sd);
8225 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8227 field.setFromData(values);
8230 field.setValue(values[id]);
8234 if(this.trackResetOnLoad){
8235 field.originalValue = field.getValue();
8241 //Roo.each(this.childForms || [], function (f) {
8242 // f.setValues(values);
8249 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8250 * they are returned as an array.
8251 * @param {Boolean} asString
8254 getValues : function(asString){
8255 //if (this.childForms) {
8256 // copy values from the child forms
8257 // Roo.each(this.childForms, function (f) {
8258 // this.setValues(f.getValues());
8264 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8265 if(asString === true){
8268 return Roo.urlDecode(fs);
8272 * Returns the fields in this form as an object with key/value pairs.
8273 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8276 getFieldValues : function(with_hidden)
8278 var items = this.getItems();
8280 items.each(function(f){
8286 var v = f.getValue();
8288 if (f.inputType =='radio') {
8289 if (typeof(ret[f.getName()]) == 'undefined') {
8290 ret[f.getName()] = ''; // empty..
8293 if (!f.el.dom.checked) {
8301 if(f.xtype == 'MoneyField'){
8302 ret[f.currencyName] = f.getCurrency();
8305 // not sure if this supported any more..
8306 if ((typeof(v) == 'object') && f.getRawValue) {
8307 v = f.getRawValue() ; // dates..
8309 // combo boxes where name != hiddenName...
8310 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8311 ret[f.name] = f.getRawValue();
8313 ret[f.getName()] = v;
8320 * Clears all invalid messages in this form.
8321 * @return {BasicForm} this
8323 clearInvalid : function(){
8324 var items = this.getItems();
8326 items.each(function(f){
8335 * @return {BasicForm} this
8338 var items = this.getItems();
8339 items.each(function(f){
8343 Roo.each(this.childForms || [], function (f) {
8351 getItems : function()
8353 var r=new Roo.util.MixedCollection(false, function(o){
8354 return o.id || (o.id = Roo.id());
8356 var iter = function(el) {
8363 Roo.each(el.items,function(e) {
8372 hideFields : function(items)
8374 Roo.each(items, function(i){
8376 var f = this.findField(i);
8387 showFields : function(items)
8389 Roo.each(items, function(i){
8391 var f = this.findField(i);
8404 Roo.apply(Roo.bootstrap.Form, {
8431 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8432 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8433 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8434 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8437 this.maskEl.top.enableDisplayMode("block");
8438 this.maskEl.left.enableDisplayMode("block");
8439 this.maskEl.bottom.enableDisplayMode("block");
8440 this.maskEl.right.enableDisplayMode("block");
8442 this.toolTip = new Roo.bootstrap.Tooltip({
8443 cls : 'roo-form-error-popover',
8445 'left' : ['r-l', [-2,0], 'right'],
8446 'right' : ['l-r', [2,0], 'left'],
8447 'bottom' : ['tl-bl', [0,2], 'top'],
8448 'top' : [ 'bl-tl', [0,-2], 'bottom']
8452 this.toolTip.render(Roo.get(document.body));
8454 this.toolTip.el.enableDisplayMode("block");
8456 Roo.get(document.body).on('click', function(){
8460 Roo.get(document.body).on('touchstart', function(){
8464 this.isApplied = true
8467 mask : function(form, target)
8471 this.target = target;
8473 if(!this.form.errorMask || !target.el){
8477 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8479 Roo.log(scrollable);
8481 var ot = this.target.el.calcOffsetsTo(scrollable);
8483 var scrollTo = ot[1] - this.form.maskOffset;
8485 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8487 scrollable.scrollTo('top', scrollTo);
8489 var box = this.target.el.getBox();
8491 var zIndex = Roo.bootstrap.Modal.zIndex++;
8494 this.maskEl.top.setStyle('position', 'absolute');
8495 this.maskEl.top.setStyle('z-index', zIndex);
8496 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8497 this.maskEl.top.setLeft(0);
8498 this.maskEl.top.setTop(0);
8499 this.maskEl.top.show();
8501 this.maskEl.left.setStyle('position', 'absolute');
8502 this.maskEl.left.setStyle('z-index', zIndex);
8503 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8504 this.maskEl.left.setLeft(0);
8505 this.maskEl.left.setTop(box.y - this.padding);
8506 this.maskEl.left.show();
8508 this.maskEl.bottom.setStyle('position', 'absolute');
8509 this.maskEl.bottom.setStyle('z-index', zIndex);
8510 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8511 this.maskEl.bottom.setLeft(0);
8512 this.maskEl.bottom.setTop(box.bottom + this.padding);
8513 this.maskEl.bottom.show();
8515 this.maskEl.right.setStyle('position', 'absolute');
8516 this.maskEl.right.setStyle('z-index', zIndex);
8517 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8518 this.maskEl.right.setLeft(box.right + this.padding);
8519 this.maskEl.right.setTop(box.y - this.padding);
8520 this.maskEl.right.show();
8522 this.toolTip.bindEl = this.target.el;
8524 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8526 var tip = this.target.blankText;
8528 if(this.target.getValue() !== '' ) {
8530 if (this.target.invalidText.length) {
8531 tip = this.target.invalidText;
8532 } else if (this.target.regexText.length){
8533 tip = this.target.regexText;
8537 this.toolTip.show(tip);
8539 this.intervalID = window.setInterval(function() {
8540 Roo.bootstrap.Form.popover.unmask();
8543 window.onwheel = function(){ return false;};
8545 (function(){ this.isMasked = true; }).defer(500, this);
8551 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8555 this.maskEl.top.setStyle('position', 'absolute');
8556 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8557 this.maskEl.top.hide();
8559 this.maskEl.left.setStyle('position', 'absolute');
8560 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8561 this.maskEl.left.hide();
8563 this.maskEl.bottom.setStyle('position', 'absolute');
8564 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8565 this.maskEl.bottom.hide();
8567 this.maskEl.right.setStyle('position', 'absolute');
8568 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8569 this.maskEl.right.hide();
8571 this.toolTip.hide();
8573 this.toolTip.el.hide();
8575 window.onwheel = function(){ return true;};
8577 if(this.intervalID){
8578 window.clearInterval(this.intervalID);
8579 this.intervalID = false;
8582 this.isMasked = false;
8592 * Ext JS Library 1.1.1
8593 * Copyright(c) 2006-2007, Ext JS, LLC.
8595 * Originally Released Under LGPL - original licence link has changed is not relivant.
8598 * <script type="text/javascript">
8601 * @class Roo.form.VTypes
8602 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8605 Roo.form.VTypes = function(){
8606 // closure these in so they are only created once.
8607 var alpha = /^[a-zA-Z_]+$/;
8608 var alphanum = /^[a-zA-Z0-9_]+$/;
8609 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8610 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8612 // All these messages and functions are configurable
8615 * The function used to validate email addresses
8616 * @param {String} value The email address
8618 'email' : function(v){
8619 return email.test(v);
8622 * The error text to display when the email validation function returns false
8625 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8627 * The keystroke filter mask to be applied on email input
8630 'emailMask' : /[a-z0-9_\.\-@]/i,
8633 * The function used to validate URLs
8634 * @param {String} value The URL
8636 'url' : function(v){
8640 * The error text to display when the url validation function returns false
8643 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8646 * The function used to validate alpha values
8647 * @param {String} value The value
8649 'alpha' : function(v){
8650 return alpha.test(v);
8653 * The error text to display when the alpha validation function returns false
8656 'alphaText' : 'This field should only contain letters and _',
8658 * The keystroke filter mask to be applied on alpha input
8661 'alphaMask' : /[a-z_]/i,
8664 * The function used to validate alphanumeric values
8665 * @param {String} value The value
8667 'alphanum' : function(v){
8668 return alphanum.test(v);
8671 * The error text to display when the alphanumeric validation function returns false
8674 'alphanumText' : 'This field should only contain letters, numbers and _',
8676 * The keystroke filter mask to be applied on alphanumeric input
8679 'alphanumMask' : /[a-z0-9_]/i
8689 * @class Roo.bootstrap.Input
8690 * @extends Roo.bootstrap.Component
8691 * Bootstrap Input class
8692 * @cfg {Boolean} disabled is it disabled
8693 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8694 * @cfg {String} name name of the input
8695 * @cfg {string} fieldLabel - the label associated
8696 * @cfg {string} placeholder - placeholder to put in text.
8697 * @cfg {string} before - input group add on before
8698 * @cfg {string} after - input group add on after
8699 * @cfg {string} size - (lg|sm) or leave empty..
8700 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8701 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8702 * @cfg {Number} md colspan out of 12 for computer-sized screens
8703 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8704 * @cfg {string} value default value of the input
8705 * @cfg {Number} labelWidth set the width of label
8706 * @cfg {Number} labellg set the width of label (1-12)
8707 * @cfg {Number} labelmd set the width of label (1-12)
8708 * @cfg {Number} labelsm set the width of label (1-12)
8709 * @cfg {Number} labelxs set the width of label (1-12)
8710 * @cfg {String} labelAlign (top|left)
8711 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8712 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8713 * @cfg {String} indicatorpos (left|right) default left
8714 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8715 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8717 * @cfg {String} align (left|center|right) Default left
8718 * @cfg {Boolean} forceFeedback (true|false) Default false
8721 * Create a new Input
8722 * @param {Object} config The config object
8725 Roo.bootstrap.Input = function(config){
8727 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8732 * Fires when this field receives input focus.
8733 * @param {Roo.form.Field} this
8738 * Fires when this field loses input focus.
8739 * @param {Roo.form.Field} this
8744 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8745 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8746 * @param {Roo.form.Field} this
8747 * @param {Roo.EventObject} e The event object
8752 * Fires just before the field blurs if the field value has changed.
8753 * @param {Roo.form.Field} this
8754 * @param {Mixed} newValue The new value
8755 * @param {Mixed} oldValue The original value
8760 * Fires after the field has been marked as invalid.
8761 * @param {Roo.form.Field} this
8762 * @param {String} msg The validation message
8767 * Fires after the field has been validated with no errors.
8768 * @param {Roo.form.Field} this
8773 * Fires after the key up
8774 * @param {Roo.form.Field} this
8775 * @param {Roo.EventObject} e The event Object
8781 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8783 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8784 automatic validation (defaults to "keyup").
8786 validationEvent : "keyup",
8788 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8790 validateOnBlur : true,
8792 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8794 validationDelay : 250,
8796 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8798 focusClass : "x-form-focus", // not needed???
8802 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8804 invalidClass : "has-warning",
8807 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8809 validClass : "has-success",
8812 * @cfg {Boolean} hasFeedback (true|false) default true
8817 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8819 invalidFeedbackClass : "glyphicon-warning-sign",
8822 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8824 validFeedbackClass : "glyphicon-ok",
8827 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8829 selectOnFocus : false,
8832 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8836 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8841 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8843 disableKeyFilter : false,
8846 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8850 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8854 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8856 blankText : "Please complete this mandatory field",
8859 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8863 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8865 maxLength : Number.MAX_VALUE,
8867 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8869 minLengthText : "The minimum length for this field is {0}",
8871 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8873 maxLengthText : "The maximum length for this field is {0}",
8877 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8878 * If available, this function will be called only after the basic validators all return true, and will be passed the
8879 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8883 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8884 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8885 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8889 * @cfg {String} regexText -- Depricated - use Invalid Text
8894 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8900 autocomplete: false,
8919 formatedValue : false,
8920 forceFeedback : false,
8922 indicatorpos : 'left',
8932 parentLabelAlign : function()
8935 while (parent.parent()) {
8936 parent = parent.parent();
8937 if (typeof(parent.labelAlign) !='undefined') {
8938 return parent.labelAlign;
8945 getAutoCreate : function()
8947 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8953 if(this.inputType != 'hidden'){
8954 cfg.cls = 'form-group' //input-group
8960 type : this.inputType,
8962 cls : 'form-control',
8963 placeholder : this.placeholder || '',
8964 autocomplete : this.autocomplete || 'new-password'
8967 if(this.capture.length){
8968 input.capture = this.capture;
8971 if(this.accept.length){
8972 input.accept = this.accept + "/*";
8976 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8979 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8980 input.maxLength = this.maxLength;
8983 if (this.disabled) {
8984 input.disabled=true;
8987 if (this.readOnly) {
8988 input.readonly=true;
8992 input.name = this.name;
8996 input.cls += ' input-' + this.size;
9000 ['xs','sm','md','lg'].map(function(size){
9001 if (settings[size]) {
9002 cfg.cls += ' col-' + size + '-' + settings[size];
9006 var inputblock = input;
9010 cls: 'glyphicon form-control-feedback'
9013 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9016 cls : 'has-feedback',
9024 if (this.before || this.after) {
9027 cls : 'input-group',
9031 if (this.before && typeof(this.before) == 'string') {
9033 inputblock.cn.push({
9035 cls : 'roo-input-before input-group-addon',
9039 if (this.before && typeof(this.before) == 'object') {
9040 this.before = Roo.factory(this.before);
9042 inputblock.cn.push({
9044 cls : 'roo-input-before input-group-' +
9045 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9049 inputblock.cn.push(input);
9051 if (this.after && typeof(this.after) == 'string') {
9052 inputblock.cn.push({
9054 cls : 'roo-input-after input-group-addon',
9058 if (this.after && typeof(this.after) == 'object') {
9059 this.after = Roo.factory(this.after);
9061 inputblock.cn.push({
9063 cls : 'roo-input-after input-group-' +
9064 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9068 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9069 inputblock.cls += ' has-feedback';
9070 inputblock.cn.push(feedback);
9074 if (align ==='left' && this.fieldLabel.length) {
9076 cfg.cls += ' roo-form-group-label-left';
9081 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9082 tooltip : 'This field is required'
9087 cls : 'control-label',
9088 html : this.fieldLabel
9099 var labelCfg = cfg.cn[1];
9100 var contentCfg = cfg.cn[2];
9102 if(this.indicatorpos == 'right'){
9107 cls : 'control-label',
9111 html : this.fieldLabel
9115 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9116 tooltip : 'This field is required'
9129 labelCfg = cfg.cn[0];
9130 contentCfg = cfg.cn[1];
9134 if(this.labelWidth > 12){
9135 labelCfg.style = "width: " + this.labelWidth + 'px';
9138 if(this.labelWidth < 13 && this.labelmd == 0){
9139 this.labelmd = this.labelWidth;
9142 if(this.labellg > 0){
9143 labelCfg.cls += ' col-lg-' + this.labellg;
9144 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9147 if(this.labelmd > 0){
9148 labelCfg.cls += ' col-md-' + this.labelmd;
9149 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9152 if(this.labelsm > 0){
9153 labelCfg.cls += ' col-sm-' + this.labelsm;
9154 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9157 if(this.labelxs > 0){
9158 labelCfg.cls += ' col-xs-' + this.labelxs;
9159 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9163 } else if ( this.fieldLabel.length) {
9168 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9169 tooltip : 'This field is required'
9173 //cls : 'input-group-addon',
9174 html : this.fieldLabel
9182 if(this.indicatorpos == 'right'){
9187 //cls : 'input-group-addon',
9188 html : this.fieldLabel
9193 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9194 tooltip : 'This field is required'
9214 if (this.parentType === 'Navbar' && this.parent().bar) {
9215 cfg.cls += ' navbar-form';
9218 if (this.parentType === 'NavGroup') {
9219 cfg.cls += ' navbar-form';
9227 * return the real input element.
9229 inputEl: function ()
9231 return this.el.select('input.form-control',true).first();
9234 tooltipEl : function()
9236 return this.inputEl();
9239 indicatorEl : function()
9241 var indicator = this.el.select('i.roo-required-indicator',true).first();
9251 setDisabled : function(v)
9253 var i = this.inputEl().dom;
9255 i.removeAttribute('disabled');
9259 i.setAttribute('disabled','true');
9261 initEvents : function()
9264 this.inputEl().on("keydown" , this.fireKey, this);
9265 this.inputEl().on("focus", this.onFocus, this);
9266 this.inputEl().on("blur", this.onBlur, this);
9268 this.inputEl().relayEvent('keyup', this);
9270 this.indicator = this.indicatorEl();
9273 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9276 // reference to original value for reset
9277 this.originalValue = this.getValue();
9278 //Roo.form.TextField.superclass.initEvents.call(this);
9279 if(this.validationEvent == 'keyup'){
9280 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9281 this.inputEl().on('keyup', this.filterValidation, this);
9283 else if(this.validationEvent !== false){
9284 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9287 if(this.selectOnFocus){
9288 this.on("focus", this.preFocus, this);
9291 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9292 this.inputEl().on("keypress", this.filterKeys, this);
9294 this.inputEl().relayEvent('keypress', this);
9297 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9298 this.el.on("click", this.autoSize, this);
9301 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9302 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9305 if (typeof(this.before) == 'object') {
9306 this.before.render(this.el.select('.roo-input-before',true).first());
9308 if (typeof(this.after) == 'object') {
9309 this.after.render(this.el.select('.roo-input-after',true).first());
9312 this.inputEl().on('change', this.onChange, this);
9315 filterValidation : function(e){
9316 if(!e.isNavKeyPress()){
9317 this.validationTask.delay(this.validationDelay);
9321 * Validates the field value
9322 * @return {Boolean} True if the value is valid, else false
9324 validate : function(){
9325 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9326 if(this.disabled || this.validateValue(this.getRawValue())){
9337 * Validates a value according to the field's validation rules and marks the field as invalid
9338 * if the validation fails
9339 * @param {Mixed} value The value to validate
9340 * @return {Boolean} True if the value is valid, else false
9342 validateValue : function(value)
9344 if(this.getVisibilityEl().hasClass('hidden')){
9348 if(value.length < 1) { // if it's blank
9349 if(this.allowBlank){
9355 if(value.length < this.minLength){
9358 if(value.length > this.maxLength){
9362 var vt = Roo.form.VTypes;
9363 if(!vt[this.vtype](value, this)){
9367 if(typeof this.validator == "function"){
9368 var msg = this.validator(value);
9372 if (typeof(msg) == 'string') {
9373 this.invalidText = msg;
9377 if(this.regex && !this.regex.test(value)){
9385 fireKey : function(e){
9386 //Roo.log('field ' + e.getKey());
9387 if(e.isNavKeyPress()){
9388 this.fireEvent("specialkey", this, e);
9391 focus : function (selectText){
9393 this.inputEl().focus();
9394 if(selectText === true){
9395 this.inputEl().dom.select();
9401 onFocus : function(){
9402 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9403 // this.el.addClass(this.focusClass);
9406 this.hasFocus = true;
9407 this.startValue = this.getValue();
9408 this.fireEvent("focus", this);
9412 beforeBlur : Roo.emptyFn,
9416 onBlur : function(){
9418 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9419 //this.el.removeClass(this.focusClass);
9421 this.hasFocus = false;
9422 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9425 var v = this.getValue();
9426 if(String(v) !== String(this.startValue)){
9427 this.fireEvent('change', this, v, this.startValue);
9429 this.fireEvent("blur", this);
9432 onChange : function(e)
9434 var v = this.getValue();
9435 if(String(v) !== String(this.startValue)){
9436 this.fireEvent('change', this, v, this.startValue);
9442 * Resets the current field value to the originally loaded value and clears any validation messages
9445 this.setValue(this.originalValue);
9449 * Returns the name of the field
9450 * @return {Mixed} name The name field
9452 getName: function(){
9456 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9457 * @return {Mixed} value The field value
9459 getValue : function(){
9461 var v = this.inputEl().getValue();
9466 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9467 * @return {Mixed} value The field value
9469 getRawValue : function(){
9470 var v = this.inputEl().getValue();
9476 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9477 * @param {Mixed} value The value to set
9479 setRawValue : function(v){
9480 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9483 selectText : function(start, end){
9484 var v = this.getRawValue();
9486 start = start === undefined ? 0 : start;
9487 end = end === undefined ? v.length : end;
9488 var d = this.inputEl().dom;
9489 if(d.setSelectionRange){
9490 d.setSelectionRange(start, end);
9491 }else if(d.createTextRange){
9492 var range = d.createTextRange();
9493 range.moveStart("character", start);
9494 range.moveEnd("character", v.length-end);
9501 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9502 * @param {Mixed} value The value to set
9504 setValue : function(v){
9507 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9513 processValue : function(value){
9514 if(this.stripCharsRe){
9515 var newValue = value.replace(this.stripCharsRe, '');
9516 if(newValue !== value){
9517 this.setRawValue(newValue);
9524 preFocus : function(){
9526 if(this.selectOnFocus){
9527 this.inputEl().dom.select();
9530 filterKeys : function(e){
9532 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9535 var c = e.getCharCode(), cc = String.fromCharCode(c);
9536 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9539 if(!this.maskRe.test(cc)){
9544 * Clear any invalid styles/messages for this field
9546 clearInvalid : function(){
9548 if(!this.el || this.preventMark){ // not rendered
9553 this.el.removeClass(this.invalidClass);
9555 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9557 var feedback = this.el.select('.form-control-feedback', true).first();
9560 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9566 this.indicator.removeClass('visible');
9567 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9570 this.fireEvent('valid', this);
9574 * Mark this field as valid
9576 markValid : function()
9578 if(!this.el || this.preventMark){ // not rendered...
9582 this.el.removeClass([this.invalidClass, this.validClass]);
9584 var feedback = this.el.select('.form-control-feedback', true).first();
9587 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9591 this.indicator.removeClass('visible');
9592 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9599 if(this.allowBlank && !this.getRawValue().length){
9603 this.el.addClass(this.validClass);
9605 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9607 var feedback = this.el.select('.form-control-feedback', true).first();
9610 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9611 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9616 this.fireEvent('valid', this);
9620 * Mark this field as invalid
9621 * @param {String} msg The validation message
9623 markInvalid : function(msg)
9625 if(!this.el || this.preventMark){ // not rendered
9629 this.el.removeClass([this.invalidClass, this.validClass]);
9631 var feedback = this.el.select('.form-control-feedback', true).first();
9634 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9641 if(this.allowBlank && !this.getRawValue().length){
9646 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9647 this.indicator.addClass('visible');
9650 this.el.addClass(this.invalidClass);
9652 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9654 var feedback = this.el.select('.form-control-feedback', true).first();
9657 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9659 if(this.getValue().length || this.forceFeedback){
9660 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9667 this.fireEvent('invalid', this, msg);
9670 SafariOnKeyDown : function(event)
9672 // this is a workaround for a password hang bug on chrome/ webkit.
9673 if (this.inputEl().dom.type != 'password') {
9677 var isSelectAll = false;
9679 if(this.inputEl().dom.selectionEnd > 0){
9680 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9682 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9683 event.preventDefault();
9688 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9690 event.preventDefault();
9691 // this is very hacky as keydown always get's upper case.
9693 var cc = String.fromCharCode(event.getCharCode());
9694 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9698 adjustWidth : function(tag, w){
9699 tag = tag.toLowerCase();
9700 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9701 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9705 if(tag == 'textarea'){
9708 }else if(Roo.isOpera){
9712 if(tag == 'textarea'){
9720 setFieldLabel : function(v)
9727 var ar = this.el.select('label > span',true);
9729 if (ar.elements.length) {
9730 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9731 this.fieldLabel = v;
9735 var br = this.el.select('label',true);
9737 if(br.elements.length) {
9738 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9739 this.fieldLabel = v;
9743 Roo.log('Cannot Found any of label > span || label in input');
9747 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9748 this.fieldLabel = v;
9763 * @class Roo.bootstrap.TextArea
9764 * @extends Roo.bootstrap.Input
9765 * Bootstrap TextArea class
9766 * @cfg {Number} cols Specifies the visible width of a text area
9767 * @cfg {Number} rows Specifies the visible number of lines in a text area
9768 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9769 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9770 * @cfg {string} html text
9773 * Create a new TextArea
9774 * @param {Object} config The config object
9777 Roo.bootstrap.TextArea = function(config){
9778 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9782 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9792 getAutoCreate : function(){
9794 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9800 if(this.inputType != 'hidden'){
9801 cfg.cls = 'form-group' //input-group
9809 value : this.value || '',
9810 html: this.html || '',
9811 cls : 'form-control',
9812 placeholder : this.placeholder || ''
9816 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9817 input.maxLength = this.maxLength;
9821 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9825 input.cols = this.cols;
9828 if (this.readOnly) {
9829 input.readonly = true;
9833 input.name = this.name;
9837 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9841 ['xs','sm','md','lg'].map(function(size){
9842 if (settings[size]) {
9843 cfg.cls += ' col-' + size + '-' + settings[size];
9847 var inputblock = input;
9849 if(this.hasFeedback && !this.allowBlank){
9853 cls: 'glyphicon form-control-feedback'
9857 cls : 'has-feedback',
9866 if (this.before || this.after) {
9869 cls : 'input-group',
9873 inputblock.cn.push({
9875 cls : 'input-group-addon',
9880 inputblock.cn.push(input);
9882 if(this.hasFeedback && !this.allowBlank){
9883 inputblock.cls += ' has-feedback';
9884 inputblock.cn.push(feedback);
9888 inputblock.cn.push({
9890 cls : 'input-group-addon',
9897 if (align ==='left' && this.fieldLabel.length) {
9902 cls : 'control-label',
9903 html : this.fieldLabel
9914 if(this.labelWidth > 12){
9915 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9918 if(this.labelWidth < 13 && this.labelmd == 0){
9919 this.labelmd = this.labelWidth;
9922 if(this.labellg > 0){
9923 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9924 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9927 if(this.labelmd > 0){
9928 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9929 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9932 if(this.labelsm > 0){
9933 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9934 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9937 if(this.labelxs > 0){
9938 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9939 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9942 } else if ( this.fieldLabel.length) {
9947 //cls : 'input-group-addon',
9948 html : this.fieldLabel
9966 if (this.disabled) {
9967 input.disabled=true;
9974 * return the real textarea element.
9976 inputEl: function ()
9978 return this.el.select('textarea.form-control',true).first();
9982 * Clear any invalid styles/messages for this field
9984 clearInvalid : function()
9987 if(!this.el || this.preventMark){ // not rendered
9991 var label = this.el.select('label', true).first();
9992 var icon = this.el.select('i.fa-star', true).first();
9998 this.el.removeClass(this.invalidClass);
10000 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10002 var feedback = this.el.select('.form-control-feedback', true).first();
10005 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10010 this.fireEvent('valid', this);
10014 * Mark this field as valid
10016 markValid : function()
10018 if(!this.el || this.preventMark){ // not rendered
10022 this.el.removeClass([this.invalidClass, this.validClass]);
10024 var feedback = this.el.select('.form-control-feedback', true).first();
10027 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10030 if(this.disabled || this.allowBlank){
10034 var label = this.el.select('label', true).first();
10035 var icon = this.el.select('i.fa-star', true).first();
10041 this.el.addClass(this.validClass);
10043 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10045 var feedback = this.el.select('.form-control-feedback', true).first();
10048 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10049 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10054 this.fireEvent('valid', this);
10058 * Mark this field as invalid
10059 * @param {String} msg The validation message
10061 markInvalid : function(msg)
10063 if(!this.el || this.preventMark){ // not rendered
10067 this.el.removeClass([this.invalidClass, this.validClass]);
10069 var feedback = this.el.select('.form-control-feedback', true).first();
10072 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10075 if(this.disabled || this.allowBlank){
10079 var label = this.el.select('label', true).first();
10080 var icon = this.el.select('i.fa-star', true).first();
10082 if(!this.getValue().length && label && !icon){
10083 this.el.createChild({
10085 cls : 'text-danger fa fa-lg fa-star',
10086 tooltip : 'This field is required',
10087 style : 'margin-right:5px;'
10091 this.el.addClass(this.invalidClass);
10093 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10095 var feedback = this.el.select('.form-control-feedback', true).first();
10098 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10100 if(this.getValue().length || this.forceFeedback){
10101 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10108 this.fireEvent('invalid', this, msg);
10116 * trigger field - base class for combo..
10121 * @class Roo.bootstrap.TriggerField
10122 * @extends Roo.bootstrap.Input
10123 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10124 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10125 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10126 * for which you can provide a custom implementation. For example:
10128 var trigger = new Roo.bootstrap.TriggerField();
10129 trigger.onTriggerClick = myTriggerFn;
10130 trigger.applyTo('my-field');
10133 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10134 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10135 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10136 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10137 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10140 * Create a new TriggerField.
10141 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10142 * to the base TextField)
10144 Roo.bootstrap.TriggerField = function(config){
10145 this.mimicing = false;
10146 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10149 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10151 * @cfg {String} triggerClass A CSS class to apply to the trigger
10154 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10159 * @cfg {Boolean} removable (true|false) special filter default false
10163 /** @cfg {Boolean} grow @hide */
10164 /** @cfg {Number} growMin @hide */
10165 /** @cfg {Number} growMax @hide */
10171 autoSize: Roo.emptyFn,
10175 deferHeight : true,
10178 actionMode : 'wrap',
10183 getAutoCreate : function(){
10185 var align = this.labelAlign || this.parentLabelAlign();
10190 cls: 'form-group' //input-group
10197 type : this.inputType,
10198 cls : 'form-control',
10199 autocomplete: 'new-password',
10200 placeholder : this.placeholder || ''
10204 input.name = this.name;
10207 input.cls += ' input-' + this.size;
10210 if (this.disabled) {
10211 input.disabled=true;
10214 var inputblock = input;
10216 if(this.hasFeedback && !this.allowBlank){
10220 cls: 'glyphicon form-control-feedback'
10223 if(this.removable && !this.editable && !this.tickable){
10225 cls : 'has-feedback',
10231 cls : 'roo-combo-removable-btn close'
10238 cls : 'has-feedback',
10247 if(this.removable && !this.editable && !this.tickable){
10249 cls : 'roo-removable',
10255 cls : 'roo-combo-removable-btn close'
10262 if (this.before || this.after) {
10265 cls : 'input-group',
10269 inputblock.cn.push({
10271 cls : 'input-group-addon',
10276 inputblock.cn.push(input);
10278 if(this.hasFeedback && !this.allowBlank){
10279 inputblock.cls += ' has-feedback';
10280 inputblock.cn.push(feedback);
10284 inputblock.cn.push({
10286 cls : 'input-group-addon',
10299 cls: 'form-hidden-field'
10313 cls: 'form-hidden-field'
10317 cls: 'roo-select2-choices',
10321 cls: 'roo-select2-search-field',
10334 cls: 'roo-select2-container input-group',
10339 // cls: 'typeahead typeahead-long dropdown-menu',
10340 // style: 'display:none'
10345 if(!this.multiple && this.showToggleBtn){
10351 if (this.caret != false) {
10354 cls: 'fa fa-' + this.caret
10361 cls : 'input-group-addon btn dropdown-toggle',
10366 cls: 'combobox-clear',
10380 combobox.cls += ' roo-select2-container-multi';
10383 if (align ==='left' && this.fieldLabel.length) {
10385 cfg.cls += ' roo-form-group-label-left';
10390 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10391 tooltip : 'This field is required'
10396 cls : 'control-label',
10397 html : this.fieldLabel
10409 var labelCfg = cfg.cn[1];
10410 var contentCfg = cfg.cn[2];
10412 if(this.indicatorpos == 'right'){
10417 cls : 'control-label',
10421 html : this.fieldLabel
10425 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10426 tooltip : 'This field is required'
10439 labelCfg = cfg.cn[0];
10440 contentCfg = cfg.cn[1];
10443 if(this.labelWidth > 12){
10444 labelCfg.style = "width: " + this.labelWidth + 'px';
10447 if(this.labelWidth < 13 && this.labelmd == 0){
10448 this.labelmd = this.labelWidth;
10451 if(this.labellg > 0){
10452 labelCfg.cls += ' col-lg-' + this.labellg;
10453 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10456 if(this.labelmd > 0){
10457 labelCfg.cls += ' col-md-' + this.labelmd;
10458 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10461 if(this.labelsm > 0){
10462 labelCfg.cls += ' col-sm-' + this.labelsm;
10463 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10466 if(this.labelxs > 0){
10467 labelCfg.cls += ' col-xs-' + this.labelxs;
10468 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10471 } else if ( this.fieldLabel.length) {
10472 // Roo.log(" label");
10476 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10477 tooltip : 'This field is required'
10481 //cls : 'input-group-addon',
10482 html : this.fieldLabel
10490 if(this.indicatorpos == 'right'){
10498 html : this.fieldLabel
10502 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10503 tooltip : 'This field is required'
10516 // Roo.log(" no label && no align");
10523 ['xs','sm','md','lg'].map(function(size){
10524 if (settings[size]) {
10525 cfg.cls += ' col-' + size + '-' + settings[size];
10536 onResize : function(w, h){
10537 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10538 // if(typeof w == 'number'){
10539 // var x = w - this.trigger.getWidth();
10540 // this.inputEl().setWidth(this.adjustWidth('input', x));
10541 // this.trigger.setStyle('left', x+'px');
10546 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10549 getResizeEl : function(){
10550 return this.inputEl();
10554 getPositionEl : function(){
10555 return this.inputEl();
10559 alignErrorIcon : function(){
10560 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10564 initEvents : function(){
10568 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10569 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10570 if(!this.multiple && this.showToggleBtn){
10571 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10572 if(this.hideTrigger){
10573 this.trigger.setDisplayed(false);
10575 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10579 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10582 if(this.removable && !this.editable && !this.tickable){
10583 var close = this.closeTriggerEl();
10586 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10587 close.on('click', this.removeBtnClick, this, close);
10591 //this.trigger.addClassOnOver('x-form-trigger-over');
10592 //this.trigger.addClassOnClick('x-form-trigger-click');
10595 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10599 closeTriggerEl : function()
10601 var close = this.el.select('.roo-combo-removable-btn', true).first();
10602 return close ? close : false;
10605 removeBtnClick : function(e, h, el)
10607 e.preventDefault();
10609 if(this.fireEvent("remove", this) !== false){
10611 this.fireEvent("afterremove", this)
10615 createList : function()
10617 this.list = Roo.get(document.body).createChild({
10619 cls: 'typeahead typeahead-long dropdown-menu',
10620 style: 'display:none'
10623 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10628 initTrigger : function(){
10633 onDestroy : function(){
10635 this.trigger.removeAllListeners();
10636 // this.trigger.remove();
10639 // this.wrap.remove();
10641 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10645 onFocus : function(){
10646 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10648 if(!this.mimicing){
10649 this.wrap.addClass('x-trigger-wrap-focus');
10650 this.mimicing = true;
10651 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10652 if(this.monitorTab){
10653 this.el.on("keydown", this.checkTab, this);
10660 checkTab : function(e){
10661 if(e.getKey() == e.TAB){
10662 this.triggerBlur();
10667 onBlur : function(){
10672 mimicBlur : function(e, t){
10674 if(!this.wrap.contains(t) && this.validateBlur()){
10675 this.triggerBlur();
10681 triggerBlur : function(){
10682 this.mimicing = false;
10683 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10684 if(this.monitorTab){
10685 this.el.un("keydown", this.checkTab, this);
10687 //this.wrap.removeClass('x-trigger-wrap-focus');
10688 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10692 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10693 validateBlur : function(e, t){
10698 onDisable : function(){
10699 this.inputEl().dom.disabled = true;
10700 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10702 // this.wrap.addClass('x-item-disabled');
10707 onEnable : function(){
10708 this.inputEl().dom.disabled = false;
10709 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10711 // this.el.removeClass('x-item-disabled');
10716 onShow : function(){
10717 var ae = this.getActionEl();
10720 ae.dom.style.display = '';
10721 ae.dom.style.visibility = 'visible';
10727 onHide : function(){
10728 var ae = this.getActionEl();
10729 ae.dom.style.display = 'none';
10733 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10734 * by an implementing function.
10736 * @param {EventObject} e
10738 onTriggerClick : Roo.emptyFn
10742 * Ext JS Library 1.1.1
10743 * Copyright(c) 2006-2007, Ext JS, LLC.
10745 * Originally Released Under LGPL - original licence link has changed is not relivant.
10748 * <script type="text/javascript">
10753 * @class Roo.data.SortTypes
10755 * Defines the default sorting (casting?) comparison functions used when sorting data.
10757 Roo.data.SortTypes = {
10759 * Default sort that does nothing
10760 * @param {Mixed} s The value being converted
10761 * @return {Mixed} The comparison value
10763 none : function(s){
10768 * The regular expression used to strip tags
10772 stripTagsRE : /<\/?[^>]+>/gi,
10775 * Strips all HTML tags to sort on text only
10776 * @param {Mixed} s The value being converted
10777 * @return {String} The comparison value
10779 asText : function(s){
10780 return String(s).replace(this.stripTagsRE, "");
10784 * Strips all HTML tags to sort on text only - Case insensitive
10785 * @param {Mixed} s The value being converted
10786 * @return {String} The comparison value
10788 asUCText : function(s){
10789 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10793 * Case insensitive string
10794 * @param {Mixed} s The value being converted
10795 * @return {String} The comparison value
10797 asUCString : function(s) {
10798 return String(s).toUpperCase();
10803 * @param {Mixed} s The value being converted
10804 * @return {Number} The comparison value
10806 asDate : function(s) {
10810 if(s instanceof Date){
10811 return s.getTime();
10813 return Date.parse(String(s));
10818 * @param {Mixed} s The value being converted
10819 * @return {Float} The comparison value
10821 asFloat : function(s) {
10822 var val = parseFloat(String(s).replace(/,/g, ""));
10831 * @param {Mixed} s The value being converted
10832 * @return {Number} The comparison value
10834 asInt : function(s) {
10835 var val = parseInt(String(s).replace(/,/g, ""));
10843 * Ext JS Library 1.1.1
10844 * Copyright(c) 2006-2007, Ext JS, LLC.
10846 * Originally Released Under LGPL - original licence link has changed is not relivant.
10849 * <script type="text/javascript">
10853 * @class Roo.data.Record
10854 * Instances of this class encapsulate both record <em>definition</em> information, and record
10855 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10856 * to access Records cached in an {@link Roo.data.Store} object.<br>
10858 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10859 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10862 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10864 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10865 * {@link #create}. The parameters are the same.
10866 * @param {Array} data An associative Array of data values keyed by the field name.
10867 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10868 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10869 * not specified an integer id is generated.
10871 Roo.data.Record = function(data, id){
10872 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10877 * Generate a constructor for a specific record layout.
10878 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10879 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10880 * Each field definition object may contain the following properties: <ul>
10881 * <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,
10882 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10883 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10884 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10885 * is being used, then this is a string containing the javascript expression to reference the data relative to
10886 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10887 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10888 * this may be omitted.</p></li>
10889 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10890 * <ul><li>auto (Default, implies no conversion)</li>
10895 * <li>date</li></ul></p></li>
10896 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10897 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10898 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10899 * by the Reader into an object that will be stored in the Record. It is passed the
10900 * following parameters:<ul>
10901 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10903 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10905 * <br>usage:<br><pre><code>
10906 var TopicRecord = Roo.data.Record.create(
10907 {name: 'title', mapping: 'topic_title'},
10908 {name: 'author', mapping: 'username'},
10909 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10910 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10911 {name: 'lastPoster', mapping: 'user2'},
10912 {name: 'excerpt', mapping: 'post_text'}
10915 var myNewRecord = new TopicRecord({
10916 title: 'Do my job please',
10919 lastPost: new Date(),
10920 lastPoster: 'Animal',
10921 excerpt: 'No way dude!'
10923 myStore.add(myNewRecord);
10928 Roo.data.Record.create = function(o){
10929 var f = function(){
10930 f.superclass.constructor.apply(this, arguments);
10932 Roo.extend(f, Roo.data.Record);
10933 var p = f.prototype;
10934 p.fields = new Roo.util.MixedCollection(false, function(field){
10937 for(var i = 0, len = o.length; i < len; i++){
10938 p.fields.add(new Roo.data.Field(o[i]));
10940 f.getField = function(name){
10941 return p.fields.get(name);
10946 Roo.data.Record.AUTO_ID = 1000;
10947 Roo.data.Record.EDIT = 'edit';
10948 Roo.data.Record.REJECT = 'reject';
10949 Roo.data.Record.COMMIT = 'commit';
10951 Roo.data.Record.prototype = {
10953 * Readonly flag - true if this record has been modified.
10962 join : function(store){
10963 this.store = store;
10967 * Set the named field to the specified value.
10968 * @param {String} name The name of the field to set.
10969 * @param {Object} value The value to set the field to.
10971 set : function(name, value){
10972 if(this.data[name] == value){
10976 if(!this.modified){
10977 this.modified = {};
10979 if(typeof this.modified[name] == 'undefined'){
10980 this.modified[name] = this.data[name];
10982 this.data[name] = value;
10983 if(!this.editing && this.store){
10984 this.store.afterEdit(this);
10989 * Get the value of the named field.
10990 * @param {String} name The name of the field to get the value of.
10991 * @return {Object} The value of the field.
10993 get : function(name){
10994 return this.data[name];
10998 beginEdit : function(){
10999 this.editing = true;
11000 this.modified = {};
11004 cancelEdit : function(){
11005 this.editing = false;
11006 delete this.modified;
11010 endEdit : function(){
11011 this.editing = false;
11012 if(this.dirty && this.store){
11013 this.store.afterEdit(this);
11018 * Usually called by the {@link Roo.data.Store} which owns the Record.
11019 * Rejects all changes made to the Record since either creation, or the last commit operation.
11020 * Modified fields are reverted to their original values.
11022 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11023 * of reject operations.
11025 reject : function(){
11026 var m = this.modified;
11028 if(typeof m[n] != "function"){
11029 this.data[n] = m[n];
11032 this.dirty = false;
11033 delete this.modified;
11034 this.editing = false;
11036 this.store.afterReject(this);
11041 * Usually called by the {@link Roo.data.Store} which owns the Record.
11042 * Commits all changes made to the Record since either creation, or the last commit operation.
11044 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11045 * of commit operations.
11047 commit : function(){
11048 this.dirty = false;
11049 delete this.modified;
11050 this.editing = false;
11052 this.store.afterCommit(this);
11057 hasError : function(){
11058 return this.error != null;
11062 clearError : function(){
11067 * Creates a copy of this record.
11068 * @param {String} id (optional) A new record id if you don't want to use this record's id
11071 copy : function(newId) {
11072 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11076 * Ext JS Library 1.1.1
11077 * Copyright(c) 2006-2007, Ext JS, LLC.
11079 * Originally Released Under LGPL - original licence link has changed is not relivant.
11082 * <script type="text/javascript">
11088 * @class Roo.data.Store
11089 * @extends Roo.util.Observable
11090 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11091 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11093 * 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
11094 * has no knowledge of the format of the data returned by the Proxy.<br>
11096 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11097 * instances from the data object. These records are cached and made available through accessor functions.
11099 * Creates a new Store.
11100 * @param {Object} config A config object containing the objects needed for the Store to access data,
11101 * and read the data into Records.
11103 Roo.data.Store = function(config){
11104 this.data = new Roo.util.MixedCollection(false);
11105 this.data.getKey = function(o){
11108 this.baseParams = {};
11110 this.paramNames = {
11115 "multisort" : "_multisort"
11118 if(config && config.data){
11119 this.inlineData = config.data;
11120 delete config.data;
11123 Roo.apply(this, config);
11125 if(this.reader){ // reader passed
11126 this.reader = Roo.factory(this.reader, Roo.data);
11127 this.reader.xmodule = this.xmodule || false;
11128 if(!this.recordType){
11129 this.recordType = this.reader.recordType;
11131 if(this.reader.onMetaChange){
11132 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11136 if(this.recordType){
11137 this.fields = this.recordType.prototype.fields;
11139 this.modified = [];
11143 * @event datachanged
11144 * Fires when the data cache has changed, and a widget which is using this Store
11145 * as a Record cache should refresh its view.
11146 * @param {Store} this
11148 datachanged : true,
11150 * @event metachange
11151 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11152 * @param {Store} this
11153 * @param {Object} meta The JSON metadata
11158 * Fires when Records have been added to the Store
11159 * @param {Store} this
11160 * @param {Roo.data.Record[]} records The array of Records added
11161 * @param {Number} index The index at which the record(s) were added
11166 * Fires when a Record has been removed from the Store
11167 * @param {Store} this
11168 * @param {Roo.data.Record} record The Record that was removed
11169 * @param {Number} index The index at which the record was removed
11174 * Fires when a Record has been updated
11175 * @param {Store} this
11176 * @param {Roo.data.Record} record The Record that was updated
11177 * @param {String} operation The update operation being performed. Value may be one of:
11179 Roo.data.Record.EDIT
11180 Roo.data.Record.REJECT
11181 Roo.data.Record.COMMIT
11187 * Fires when the data cache has been cleared.
11188 * @param {Store} this
11192 * @event beforeload
11193 * Fires before a request is made for a new data object. If the beforeload handler returns false
11194 * the load action will be canceled.
11195 * @param {Store} this
11196 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11200 * @event beforeloadadd
11201 * Fires after a new set of Records has been loaded.
11202 * @param {Store} this
11203 * @param {Roo.data.Record[]} records The Records that were loaded
11204 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11206 beforeloadadd : true,
11209 * Fires after a new set of Records has been loaded, before they are added to the store.
11210 * @param {Store} this
11211 * @param {Roo.data.Record[]} records The Records that were loaded
11212 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11213 * @params {Object} return from reader
11217 * @event loadexception
11218 * Fires if an exception occurs in the Proxy during loading.
11219 * Called with the signature of the Proxy's "loadexception" event.
11220 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11223 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11224 * @param {Object} load options
11225 * @param {Object} jsonData from your request (normally this contains the Exception)
11227 loadexception : true
11231 this.proxy = Roo.factory(this.proxy, Roo.data);
11232 this.proxy.xmodule = this.xmodule || false;
11233 this.relayEvents(this.proxy, ["loadexception"]);
11235 this.sortToggle = {};
11236 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11238 Roo.data.Store.superclass.constructor.call(this);
11240 if(this.inlineData){
11241 this.loadData(this.inlineData);
11242 delete this.inlineData;
11246 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11248 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11249 * without a remote query - used by combo/forms at present.
11253 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11256 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11259 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11260 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11263 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11264 * on any HTTP request
11267 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11270 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11274 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11275 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11277 remoteSort : false,
11280 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11281 * loaded or when a record is removed. (defaults to false).
11283 pruneModifiedRecords : false,
11286 lastOptions : null,
11289 * Add Records to the Store and fires the add event.
11290 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11292 add : function(records){
11293 records = [].concat(records);
11294 for(var i = 0, len = records.length; i < len; i++){
11295 records[i].join(this);
11297 var index = this.data.length;
11298 this.data.addAll(records);
11299 this.fireEvent("add", this, records, index);
11303 * Remove a Record from the Store and fires the remove event.
11304 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11306 remove : function(record){
11307 var index = this.data.indexOf(record);
11308 this.data.removeAt(index);
11310 if(this.pruneModifiedRecords){
11311 this.modified.remove(record);
11313 this.fireEvent("remove", this, record, index);
11317 * Remove all Records from the Store and fires the clear event.
11319 removeAll : function(){
11321 if(this.pruneModifiedRecords){
11322 this.modified = [];
11324 this.fireEvent("clear", this);
11328 * Inserts Records to the Store at the given index and fires the add event.
11329 * @param {Number} index The start index at which to insert the passed Records.
11330 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11332 insert : function(index, records){
11333 records = [].concat(records);
11334 for(var i = 0, len = records.length; i < len; i++){
11335 this.data.insert(index, records[i]);
11336 records[i].join(this);
11338 this.fireEvent("add", this, records, index);
11342 * Get the index within the cache of the passed Record.
11343 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11344 * @return {Number} The index of the passed Record. Returns -1 if not found.
11346 indexOf : function(record){
11347 return this.data.indexOf(record);
11351 * Get the index within the cache of the Record with the passed id.
11352 * @param {String} id The id of the Record to find.
11353 * @return {Number} The index of the Record. Returns -1 if not found.
11355 indexOfId : function(id){
11356 return this.data.indexOfKey(id);
11360 * Get the Record with the specified id.
11361 * @param {String} id The id of the Record to find.
11362 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11364 getById : function(id){
11365 return this.data.key(id);
11369 * Get the Record at the specified index.
11370 * @param {Number} index The index of the Record to find.
11371 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11373 getAt : function(index){
11374 return this.data.itemAt(index);
11378 * Returns a range of Records between specified indices.
11379 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11380 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11381 * @return {Roo.data.Record[]} An array of Records
11383 getRange : function(start, end){
11384 return this.data.getRange(start, end);
11388 storeOptions : function(o){
11389 o = Roo.apply({}, o);
11392 this.lastOptions = o;
11396 * Loads the Record cache from the configured Proxy using the configured Reader.
11398 * If using remote paging, then the first load call must specify the <em>start</em>
11399 * and <em>limit</em> properties in the options.params property to establish the initial
11400 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11402 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11403 * and this call will return before the new data has been loaded. Perform any post-processing
11404 * in a callback function, or in a "load" event handler.</strong>
11406 * @param {Object} options An object containing properties which control loading options:<ul>
11407 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11408 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11409 * passed the following arguments:<ul>
11410 * <li>r : Roo.data.Record[]</li>
11411 * <li>options: Options object from the load call</li>
11412 * <li>success: Boolean success indicator</li></ul></li>
11413 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11414 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11417 load : function(options){
11418 options = options || {};
11419 if(this.fireEvent("beforeload", this, options) !== false){
11420 this.storeOptions(options);
11421 var p = Roo.apply(options.params || {}, this.baseParams);
11422 // if meta was not loaded from remote source.. try requesting it.
11423 if (!this.reader.metaFromRemote) {
11424 p._requestMeta = 1;
11426 if(this.sortInfo && this.remoteSort){
11427 var pn = this.paramNames;
11428 p[pn["sort"]] = this.sortInfo.field;
11429 p[pn["dir"]] = this.sortInfo.direction;
11431 if (this.multiSort) {
11432 var pn = this.paramNames;
11433 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11436 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11441 * Reloads the Record cache from the configured Proxy using the configured Reader and
11442 * the options from the last load operation performed.
11443 * @param {Object} options (optional) An object containing properties which may override the options
11444 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11445 * the most recently used options are reused).
11447 reload : function(options){
11448 this.load(Roo.applyIf(options||{}, this.lastOptions));
11452 // Called as a callback by the Reader during a load operation.
11453 loadRecords : function(o, options, success){
11454 if(!o || success === false){
11455 if(success !== false){
11456 this.fireEvent("load", this, [], options, o);
11458 if(options.callback){
11459 options.callback.call(options.scope || this, [], options, false);
11463 // if data returned failure - throw an exception.
11464 if (o.success === false) {
11465 // show a message if no listener is registered.
11466 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11467 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11469 // loadmask wil be hooked into this..
11470 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11473 var r = o.records, t = o.totalRecords || r.length;
11475 this.fireEvent("beforeloadadd", this, r, options, o);
11477 if(!options || options.add !== true){
11478 if(this.pruneModifiedRecords){
11479 this.modified = [];
11481 for(var i = 0, len = r.length; i < len; i++){
11485 this.data = this.snapshot;
11486 delete this.snapshot;
11489 this.data.addAll(r);
11490 this.totalLength = t;
11492 this.fireEvent("datachanged", this);
11494 this.totalLength = Math.max(t, this.data.length+r.length);
11498 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11500 var e = new Roo.data.Record({});
11502 e.set(this.parent.displayField, this.parent.emptyTitle);
11503 e.set(this.parent.valueField, '');
11508 this.fireEvent("load", this, r, options, o);
11509 if(options.callback){
11510 options.callback.call(options.scope || this, r, options, true);
11516 * Loads data from a passed data block. A Reader which understands the format of the data
11517 * must have been configured in the constructor.
11518 * @param {Object} data The data block from which to read the Records. The format of the data expected
11519 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11520 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11522 loadData : function(o, append){
11523 var r = this.reader.readRecords(o);
11524 this.loadRecords(r, {add: append}, true);
11528 * Gets the number of cached records.
11530 * <em>If using paging, this may not be the total size of the dataset. If the data object
11531 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11532 * the data set size</em>
11534 getCount : function(){
11535 return this.data.length || 0;
11539 * Gets the total number of records in the dataset as returned by the server.
11541 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11542 * the dataset size</em>
11544 getTotalCount : function(){
11545 return this.totalLength || 0;
11549 * Returns the sort state of the Store as an object with two properties:
11551 field {String} The name of the field by which the Records are sorted
11552 direction {String} The sort order, "ASC" or "DESC"
11555 getSortState : function(){
11556 return this.sortInfo;
11560 applySort : function(){
11561 if(this.sortInfo && !this.remoteSort){
11562 var s = this.sortInfo, f = s.field;
11563 var st = this.fields.get(f).sortType;
11564 var fn = function(r1, r2){
11565 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11566 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11568 this.data.sort(s.direction, fn);
11569 if(this.snapshot && this.snapshot != this.data){
11570 this.snapshot.sort(s.direction, fn);
11576 * Sets the default sort column and order to be used by the next load operation.
11577 * @param {String} fieldName The name of the field to sort by.
11578 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11580 setDefaultSort : function(field, dir){
11581 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11585 * Sort the Records.
11586 * If remote sorting is used, the sort is performed on the server, and the cache is
11587 * reloaded. If local sorting is used, the cache is sorted internally.
11588 * @param {String} fieldName The name of the field to sort by.
11589 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11591 sort : function(fieldName, dir){
11592 var f = this.fields.get(fieldName);
11594 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11596 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11597 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11602 this.sortToggle[f.name] = dir;
11603 this.sortInfo = {field: f.name, direction: dir};
11604 if(!this.remoteSort){
11606 this.fireEvent("datachanged", this);
11608 this.load(this.lastOptions);
11613 * Calls the specified function for each of the Records in the cache.
11614 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11615 * Returning <em>false</em> aborts and exits the iteration.
11616 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11618 each : function(fn, scope){
11619 this.data.each(fn, scope);
11623 * Gets all records modified since the last commit. Modified records are persisted across load operations
11624 * (e.g., during paging).
11625 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11627 getModifiedRecords : function(){
11628 return this.modified;
11632 createFilterFn : function(property, value, anyMatch){
11633 if(!value.exec){ // not a regex
11634 value = String(value);
11635 if(value.length == 0){
11638 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11640 return function(r){
11641 return value.test(r.data[property]);
11646 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11647 * @param {String} property A field on your records
11648 * @param {Number} start The record index to start at (defaults to 0)
11649 * @param {Number} end The last record index to include (defaults to length - 1)
11650 * @return {Number} The sum
11652 sum : function(property, start, end){
11653 var rs = this.data.items, v = 0;
11654 start = start || 0;
11655 end = (end || end === 0) ? end : rs.length-1;
11657 for(var i = start; i <= end; i++){
11658 v += (rs[i].data[property] || 0);
11664 * Filter the records by a specified property.
11665 * @param {String} field A field on your records
11666 * @param {String/RegExp} value Either a string that the field
11667 * should start with or a RegExp to test against the field
11668 * @param {Boolean} anyMatch True to match any part not just the beginning
11670 filter : function(property, value, anyMatch){
11671 var fn = this.createFilterFn(property, value, anyMatch);
11672 return fn ? this.filterBy(fn) : this.clearFilter();
11676 * Filter by a function. The specified function will be called with each
11677 * record in this data source. If the function returns true the record is included,
11678 * otherwise it is filtered.
11679 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11680 * @param {Object} scope (optional) The scope of the function (defaults to this)
11682 filterBy : function(fn, scope){
11683 this.snapshot = this.snapshot || this.data;
11684 this.data = this.queryBy(fn, scope||this);
11685 this.fireEvent("datachanged", this);
11689 * Query the records by a specified property.
11690 * @param {String} field A field on your records
11691 * @param {String/RegExp} value Either a string that the field
11692 * should start with or a RegExp to test against the field
11693 * @param {Boolean} anyMatch True to match any part not just the beginning
11694 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11696 query : function(property, value, anyMatch){
11697 var fn = this.createFilterFn(property, value, anyMatch);
11698 return fn ? this.queryBy(fn) : this.data.clone();
11702 * Query by a function. The specified function will be called with each
11703 * record in this data source. If the function returns true the record is included
11705 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11706 * @param {Object} scope (optional) The scope of the function (defaults to this)
11707 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11709 queryBy : function(fn, scope){
11710 var data = this.snapshot || this.data;
11711 return data.filterBy(fn, scope||this);
11715 * Collects unique values for a particular dataIndex from this store.
11716 * @param {String} dataIndex The property to collect
11717 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11718 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11719 * @return {Array} An array of the unique values
11721 collect : function(dataIndex, allowNull, bypassFilter){
11722 var d = (bypassFilter === true && this.snapshot) ?
11723 this.snapshot.items : this.data.items;
11724 var v, sv, r = [], l = {};
11725 for(var i = 0, len = d.length; i < len; i++){
11726 v = d[i].data[dataIndex];
11728 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11737 * Revert to a view of the Record cache with no filtering applied.
11738 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11740 clearFilter : function(suppressEvent){
11741 if(this.snapshot && this.snapshot != this.data){
11742 this.data = this.snapshot;
11743 delete this.snapshot;
11744 if(suppressEvent !== true){
11745 this.fireEvent("datachanged", this);
11751 afterEdit : function(record){
11752 if(this.modified.indexOf(record) == -1){
11753 this.modified.push(record);
11755 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11759 afterReject : function(record){
11760 this.modified.remove(record);
11761 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11765 afterCommit : function(record){
11766 this.modified.remove(record);
11767 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11771 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11772 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11774 commitChanges : function(){
11775 var m = this.modified.slice(0);
11776 this.modified = [];
11777 for(var i = 0, len = m.length; i < len; i++){
11783 * Cancel outstanding changes on all changed records.
11785 rejectChanges : function(){
11786 var m = this.modified.slice(0);
11787 this.modified = [];
11788 for(var i = 0, len = m.length; i < len; i++){
11793 onMetaChange : function(meta, rtype, o){
11794 this.recordType = rtype;
11795 this.fields = rtype.prototype.fields;
11796 delete this.snapshot;
11797 this.sortInfo = meta.sortInfo || this.sortInfo;
11798 this.modified = [];
11799 this.fireEvent('metachange', this, this.reader.meta);
11802 moveIndex : function(data, type)
11804 var index = this.indexOf(data);
11806 var newIndex = index + type;
11810 this.insert(newIndex, data);
11815 * Ext JS Library 1.1.1
11816 * Copyright(c) 2006-2007, Ext JS, LLC.
11818 * Originally Released Under LGPL - original licence link has changed is not relivant.
11821 * <script type="text/javascript">
11825 * @class Roo.data.SimpleStore
11826 * @extends Roo.data.Store
11827 * Small helper class to make creating Stores from Array data easier.
11828 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11829 * @cfg {Array} fields An array of field definition objects, or field name strings.
11830 * @cfg {Array} data The multi-dimensional array of data
11832 * @param {Object} config
11834 Roo.data.SimpleStore = function(config){
11835 Roo.data.SimpleStore.superclass.constructor.call(this, {
11837 reader: new Roo.data.ArrayReader({
11840 Roo.data.Record.create(config.fields)
11842 proxy : new Roo.data.MemoryProxy(config.data)
11846 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11848 * Ext JS Library 1.1.1
11849 * Copyright(c) 2006-2007, Ext JS, LLC.
11851 * Originally Released Under LGPL - original licence link has changed is not relivant.
11854 * <script type="text/javascript">
11859 * @extends Roo.data.Store
11860 * @class Roo.data.JsonStore
11861 * Small helper class to make creating Stores for JSON data easier. <br/>
11863 var store = new Roo.data.JsonStore({
11864 url: 'get-images.php',
11866 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11869 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11870 * JsonReader and HttpProxy (unless inline data is provided).</b>
11871 * @cfg {Array} fields An array of field definition objects, or field name strings.
11873 * @param {Object} config
11875 Roo.data.JsonStore = function(c){
11876 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11877 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11878 reader: new Roo.data.JsonReader(c, c.fields)
11881 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11883 * Ext JS Library 1.1.1
11884 * Copyright(c) 2006-2007, Ext JS, LLC.
11886 * Originally Released Under LGPL - original licence link has changed is not relivant.
11889 * <script type="text/javascript">
11893 Roo.data.Field = function(config){
11894 if(typeof config == "string"){
11895 config = {name: config};
11897 Roo.apply(this, config);
11900 this.type = "auto";
11903 var st = Roo.data.SortTypes;
11904 // named sortTypes are supported, here we look them up
11905 if(typeof this.sortType == "string"){
11906 this.sortType = st[this.sortType];
11909 // set default sortType for strings and dates
11910 if(!this.sortType){
11913 this.sortType = st.asUCString;
11916 this.sortType = st.asDate;
11919 this.sortType = st.none;
11924 var stripRe = /[\$,%]/g;
11926 // prebuilt conversion function for this field, instead of
11927 // switching every time we're reading a value
11929 var cv, dateFormat = this.dateFormat;
11934 cv = function(v){ return v; };
11937 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11941 return v !== undefined && v !== null && v !== '' ?
11942 parseInt(String(v).replace(stripRe, ""), 10) : '';
11947 return v !== undefined && v !== null && v !== '' ?
11948 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11953 cv = function(v){ return v === true || v === "true" || v == 1; };
11960 if(v instanceof Date){
11964 if(dateFormat == "timestamp"){
11965 return new Date(v*1000);
11967 return Date.parseDate(v, dateFormat);
11969 var parsed = Date.parse(v);
11970 return parsed ? new Date(parsed) : null;
11979 Roo.data.Field.prototype = {
11987 * Ext JS Library 1.1.1
11988 * Copyright(c) 2006-2007, Ext JS, LLC.
11990 * Originally Released Under LGPL - original licence link has changed is not relivant.
11993 * <script type="text/javascript">
11996 // Base class for reading structured data from a data source. This class is intended to be
11997 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12000 * @class Roo.data.DataReader
12001 * Base class for reading structured data from a data source. This class is intended to be
12002 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12005 Roo.data.DataReader = function(meta, recordType){
12009 this.recordType = recordType instanceof Array ?
12010 Roo.data.Record.create(recordType) : recordType;
12013 Roo.data.DataReader.prototype = {
12015 * Create an empty record
12016 * @param {Object} data (optional) - overlay some values
12017 * @return {Roo.data.Record} record created.
12019 newRow : function(d) {
12021 this.recordType.prototype.fields.each(function(c) {
12023 case 'int' : da[c.name] = 0; break;
12024 case 'date' : da[c.name] = new Date(); break;
12025 case 'float' : da[c.name] = 0.0; break;
12026 case 'boolean' : da[c.name] = false; break;
12027 default : da[c.name] = ""; break;
12031 return new this.recordType(Roo.apply(da, d));
12036 * Ext JS Library 1.1.1
12037 * Copyright(c) 2006-2007, Ext JS, LLC.
12039 * Originally Released Under LGPL - original licence link has changed is not relivant.
12042 * <script type="text/javascript">
12046 * @class Roo.data.DataProxy
12047 * @extends Roo.data.Observable
12048 * This class is an abstract base class for implementations which provide retrieval of
12049 * unformatted data objects.<br>
12051 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12052 * (of the appropriate type which knows how to parse the data object) to provide a block of
12053 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12055 * Custom implementations must implement the load method as described in
12056 * {@link Roo.data.HttpProxy#load}.
12058 Roo.data.DataProxy = function(){
12061 * @event beforeload
12062 * Fires before a network request is made to retrieve a data object.
12063 * @param {Object} This DataProxy object.
12064 * @param {Object} params The params parameter to the load function.
12069 * Fires before the load method's callback is called.
12070 * @param {Object} This DataProxy object.
12071 * @param {Object} o The data object.
12072 * @param {Object} arg The callback argument object passed to the load function.
12076 * @event loadexception
12077 * Fires if an Exception occurs during data retrieval.
12078 * @param {Object} This DataProxy object.
12079 * @param {Object} o The data object.
12080 * @param {Object} arg The callback argument object passed to the load function.
12081 * @param {Object} e The Exception.
12083 loadexception : true
12085 Roo.data.DataProxy.superclass.constructor.call(this);
12088 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12091 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12095 * Ext JS Library 1.1.1
12096 * Copyright(c) 2006-2007, Ext JS, LLC.
12098 * Originally Released Under LGPL - original licence link has changed is not relivant.
12101 * <script type="text/javascript">
12104 * @class Roo.data.MemoryProxy
12105 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12106 * to the Reader when its load method is called.
12108 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12110 Roo.data.MemoryProxy = function(data){
12114 Roo.data.MemoryProxy.superclass.constructor.call(this);
12118 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12121 * Load data from the requested source (in this case an in-memory
12122 * data object passed to the constructor), read the data object into
12123 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12124 * process that block using the passed callback.
12125 * @param {Object} params This parameter is not used by the MemoryProxy class.
12126 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12127 * object into a block of Roo.data.Records.
12128 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12129 * The function must be passed <ul>
12130 * <li>The Record block object</li>
12131 * <li>The "arg" argument from the load function</li>
12132 * <li>A boolean success indicator</li>
12134 * @param {Object} scope The scope in which to call the callback
12135 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12137 load : function(params, reader, callback, scope, arg){
12138 params = params || {};
12141 result = reader.readRecords(this.data);
12143 this.fireEvent("loadexception", this, arg, null, e);
12144 callback.call(scope, null, arg, false);
12147 callback.call(scope, result, arg, true);
12151 update : function(params, records){
12156 * Ext JS Library 1.1.1
12157 * Copyright(c) 2006-2007, Ext JS, LLC.
12159 * Originally Released Under LGPL - original licence link has changed is not relivant.
12162 * <script type="text/javascript">
12165 * @class Roo.data.HttpProxy
12166 * @extends Roo.data.DataProxy
12167 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12168 * configured to reference a certain URL.<br><br>
12170 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12171 * from which the running page was served.<br><br>
12173 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12175 * Be aware that to enable the browser to parse an XML document, the server must set
12176 * the Content-Type header in the HTTP response to "text/xml".
12178 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12179 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12180 * will be used to make the request.
12182 Roo.data.HttpProxy = function(conn){
12183 Roo.data.HttpProxy.superclass.constructor.call(this);
12184 // is conn a conn config or a real conn?
12186 this.useAjax = !conn || !conn.events;
12190 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12191 // thse are take from connection...
12194 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12197 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12198 * extra parameters to each request made by this object. (defaults to undefined)
12201 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12202 * to each request made by this object. (defaults to undefined)
12205 * @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)
12208 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12211 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12217 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12221 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12222 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12223 * a finer-grained basis than the DataProxy events.
12225 getConnection : function(){
12226 return this.useAjax ? Roo.Ajax : this.conn;
12230 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12231 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12232 * process that block using the passed callback.
12233 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12234 * for the request to the remote server.
12235 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12236 * object into a block of Roo.data.Records.
12237 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12238 * The function must be passed <ul>
12239 * <li>The Record block object</li>
12240 * <li>The "arg" argument from the load function</li>
12241 * <li>A boolean success indicator</li>
12243 * @param {Object} scope The scope in which to call the callback
12244 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12246 load : function(params, reader, callback, scope, arg){
12247 if(this.fireEvent("beforeload", this, params) !== false){
12249 params : params || {},
12251 callback : callback,
12256 callback : this.loadResponse,
12260 Roo.applyIf(o, this.conn);
12261 if(this.activeRequest){
12262 Roo.Ajax.abort(this.activeRequest);
12264 this.activeRequest = Roo.Ajax.request(o);
12266 this.conn.request(o);
12269 callback.call(scope||this, null, arg, false);
12274 loadResponse : function(o, success, response){
12275 delete this.activeRequest;
12277 this.fireEvent("loadexception", this, o, response);
12278 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12283 result = o.reader.read(response);
12285 this.fireEvent("loadexception", this, o, response, e);
12286 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12290 this.fireEvent("load", this, o, o.request.arg);
12291 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12295 update : function(dataSet){
12300 updateResponse : function(dataSet){
12305 * Ext JS Library 1.1.1
12306 * Copyright(c) 2006-2007, Ext JS, LLC.
12308 * Originally Released Under LGPL - original licence link has changed is not relivant.
12311 * <script type="text/javascript">
12315 * @class Roo.data.ScriptTagProxy
12316 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12317 * other than the originating domain of the running page.<br><br>
12319 * <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
12320 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12322 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12323 * source code that is used as the source inside a <script> tag.<br><br>
12325 * In order for the browser to process the returned data, the server must wrap the data object
12326 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12327 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12328 * depending on whether the callback name was passed:
12331 boolean scriptTag = false;
12332 String cb = request.getParameter("callback");
12335 response.setContentType("text/javascript");
12337 response.setContentType("application/x-json");
12339 Writer out = response.getWriter();
12341 out.write(cb + "(");
12343 out.print(dataBlock.toJsonString());
12350 * @param {Object} config A configuration object.
12352 Roo.data.ScriptTagProxy = function(config){
12353 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12354 Roo.apply(this, config);
12355 this.head = document.getElementsByTagName("head")[0];
12358 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12360 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12362 * @cfg {String} url The URL from which to request the data object.
12365 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12369 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12370 * the server the name of the callback function set up by the load call to process the returned data object.
12371 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12372 * javascript output which calls this named function passing the data object as its only parameter.
12374 callbackParam : "callback",
12376 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12377 * name to the request.
12382 * Load data from the configured URL, read the data object into
12383 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12384 * process that block using the passed callback.
12385 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12386 * for the request to the remote server.
12387 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12388 * object into a block of Roo.data.Records.
12389 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12390 * The function must be passed <ul>
12391 * <li>The Record block object</li>
12392 * <li>The "arg" argument from the load function</li>
12393 * <li>A boolean success indicator</li>
12395 * @param {Object} scope The scope in which to call the callback
12396 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12398 load : function(params, reader, callback, scope, arg){
12399 if(this.fireEvent("beforeload", this, params) !== false){
12401 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12403 var url = this.url;
12404 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12406 url += "&_dc=" + (new Date().getTime());
12408 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12411 cb : "stcCallback"+transId,
12412 scriptId : "stcScript"+transId,
12416 callback : callback,
12422 window[trans.cb] = function(o){
12423 conn.handleResponse(o, trans);
12426 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12428 if(this.autoAbort !== false){
12432 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12434 var script = document.createElement("script");
12435 script.setAttribute("src", url);
12436 script.setAttribute("type", "text/javascript");
12437 script.setAttribute("id", trans.scriptId);
12438 this.head.appendChild(script);
12440 this.trans = trans;
12442 callback.call(scope||this, null, arg, false);
12447 isLoading : function(){
12448 return this.trans ? true : false;
12452 * Abort the current server request.
12454 abort : function(){
12455 if(this.isLoading()){
12456 this.destroyTrans(this.trans);
12461 destroyTrans : function(trans, isLoaded){
12462 this.head.removeChild(document.getElementById(trans.scriptId));
12463 clearTimeout(trans.timeoutId);
12465 window[trans.cb] = undefined;
12467 delete window[trans.cb];
12470 // if hasn't been loaded, wait for load to remove it to prevent script error
12471 window[trans.cb] = function(){
12472 window[trans.cb] = undefined;
12474 delete window[trans.cb];
12481 handleResponse : function(o, trans){
12482 this.trans = false;
12483 this.destroyTrans(trans, true);
12486 result = trans.reader.readRecords(o);
12488 this.fireEvent("loadexception", this, o, trans.arg, e);
12489 trans.callback.call(trans.scope||window, null, trans.arg, false);
12492 this.fireEvent("load", this, o, trans.arg);
12493 trans.callback.call(trans.scope||window, result, trans.arg, true);
12497 handleFailure : function(trans){
12498 this.trans = false;
12499 this.destroyTrans(trans, false);
12500 this.fireEvent("loadexception", this, null, trans.arg);
12501 trans.callback.call(trans.scope||window, null, trans.arg, false);
12505 * Ext JS Library 1.1.1
12506 * Copyright(c) 2006-2007, Ext JS, LLC.
12508 * Originally Released Under LGPL - original licence link has changed is not relivant.
12511 * <script type="text/javascript">
12515 * @class Roo.data.JsonReader
12516 * @extends Roo.data.DataReader
12517 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12518 * based on mappings in a provided Roo.data.Record constructor.
12520 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12521 * in the reply previously.
12526 var RecordDef = Roo.data.Record.create([
12527 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12528 {name: 'occupation'} // This field will use "occupation" as the mapping.
12530 var myReader = new Roo.data.JsonReader({
12531 totalProperty: "results", // The property which contains the total dataset size (optional)
12532 root: "rows", // The property which contains an Array of row objects
12533 id: "id" // The property within each row object that provides an ID for the record (optional)
12537 * This would consume a JSON file like this:
12539 { 'results': 2, 'rows': [
12540 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12541 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12544 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12545 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12546 * paged from the remote server.
12547 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12548 * @cfg {String} root name of the property which contains the Array of row objects.
12549 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12550 * @cfg {Array} fields Array of field definition objects
12552 * Create a new JsonReader
12553 * @param {Object} meta Metadata configuration options
12554 * @param {Object} recordType Either an Array of field definition objects,
12555 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12557 Roo.data.JsonReader = function(meta, recordType){
12560 // set some defaults:
12561 Roo.applyIf(meta, {
12562 totalProperty: 'total',
12563 successProperty : 'success',
12568 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12570 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12573 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12574 * Used by Store query builder to append _requestMeta to params.
12577 metaFromRemote : false,
12579 * This method is only used by a DataProxy which has retrieved data from a remote server.
12580 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12581 * @return {Object} data A data block which is used by an Roo.data.Store object as
12582 * a cache of Roo.data.Records.
12584 read : function(response){
12585 var json = response.responseText;
12587 var o = /* eval:var:o */ eval("("+json+")");
12589 throw {message: "JsonReader.read: Json object not found"};
12595 this.metaFromRemote = true;
12596 this.meta = o.metaData;
12597 this.recordType = Roo.data.Record.create(o.metaData.fields);
12598 this.onMetaChange(this.meta, this.recordType, o);
12600 return this.readRecords(o);
12603 // private function a store will implement
12604 onMetaChange : function(meta, recordType, o){
12611 simpleAccess: function(obj, subsc) {
12618 getJsonAccessor: function(){
12620 return function(expr) {
12622 return(re.test(expr))
12623 ? new Function("obj", "return obj." + expr)
12628 return Roo.emptyFn;
12633 * Create a data block containing Roo.data.Records from an XML document.
12634 * @param {Object} o An object which contains an Array of row objects in the property specified
12635 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12636 * which contains the total size of the dataset.
12637 * @return {Object} data A data block which is used by an Roo.data.Store object as
12638 * a cache of Roo.data.Records.
12640 readRecords : function(o){
12642 * After any data loads, the raw JSON data is available for further custom processing.
12646 var s = this.meta, Record = this.recordType,
12647 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12649 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12651 if(s.totalProperty) {
12652 this.getTotal = this.getJsonAccessor(s.totalProperty);
12654 if(s.successProperty) {
12655 this.getSuccess = this.getJsonAccessor(s.successProperty);
12657 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12659 var g = this.getJsonAccessor(s.id);
12660 this.getId = function(rec) {
12662 return (r === undefined || r === "") ? null : r;
12665 this.getId = function(){return null;};
12668 for(var jj = 0; jj < fl; jj++){
12670 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12671 this.ef[jj] = this.getJsonAccessor(map);
12675 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12676 if(s.totalProperty){
12677 var vt = parseInt(this.getTotal(o), 10);
12682 if(s.successProperty){
12683 var vs = this.getSuccess(o);
12684 if(vs === false || vs === 'false'){
12689 for(var i = 0; i < c; i++){
12692 var id = this.getId(n);
12693 for(var j = 0; j < fl; j++){
12695 var v = this.ef[j](n);
12697 Roo.log('missing convert for ' + f.name);
12701 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12703 var record = new Record(values, id);
12705 records[i] = record;
12711 totalRecords : totalRecords
12716 * Ext JS Library 1.1.1
12717 * Copyright(c) 2006-2007, Ext JS, LLC.
12719 * Originally Released Under LGPL - original licence link has changed is not relivant.
12722 * <script type="text/javascript">
12726 * @class Roo.data.ArrayReader
12727 * @extends Roo.data.DataReader
12728 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12729 * Each element of that Array represents a row of data fields. The
12730 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12731 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12735 var RecordDef = Roo.data.Record.create([
12736 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12737 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12739 var myReader = new Roo.data.ArrayReader({
12740 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12744 * This would consume an Array like this:
12746 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12748 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12750 * Create a new JsonReader
12751 * @param {Object} meta Metadata configuration options.
12752 * @param {Object} recordType Either an Array of field definition objects
12753 * as specified to {@link Roo.data.Record#create},
12754 * or an {@link Roo.data.Record} object
12755 * created using {@link Roo.data.Record#create}.
12757 Roo.data.ArrayReader = function(meta, recordType){
12758 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12761 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12763 * Create a data block containing Roo.data.Records from an XML document.
12764 * @param {Object} o An Array of row objects which represents the dataset.
12765 * @return {Object} data A data block which is used by an Roo.data.Store object as
12766 * a cache of Roo.data.Records.
12768 readRecords : function(o){
12769 var sid = this.meta ? this.meta.id : null;
12770 var recordType = this.recordType, fields = recordType.prototype.fields;
12773 for(var i = 0; i < root.length; i++){
12776 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12777 for(var j = 0, jlen = fields.length; j < jlen; j++){
12778 var f = fields.items[j];
12779 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12780 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12782 values[f.name] = v;
12784 var record = new recordType(values, id);
12786 records[records.length] = record;
12790 totalRecords : records.length
12799 * @class Roo.bootstrap.ComboBox
12800 * @extends Roo.bootstrap.TriggerField
12801 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12802 * @cfg {Boolean} append (true|false) default false
12803 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12804 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12805 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12806 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12807 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12808 * @cfg {Boolean} animate default true
12809 * @cfg {Boolean} emptyResultText only for touch device
12810 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12811 * @cfg {String} emptyTitle default ''
12813 * Create a new ComboBox.
12814 * @param {Object} config Configuration options
12816 Roo.bootstrap.ComboBox = function(config){
12817 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12821 * Fires when the dropdown list is expanded
12822 * @param {Roo.bootstrap.ComboBox} combo This combo box
12827 * Fires when the dropdown list is collapsed
12828 * @param {Roo.bootstrap.ComboBox} combo This combo box
12832 * @event beforeselect
12833 * Fires before a list item is selected. Return false to cancel the selection.
12834 * @param {Roo.bootstrap.ComboBox} combo This combo box
12835 * @param {Roo.data.Record} record The data record returned from the underlying store
12836 * @param {Number} index The index of the selected item in the dropdown list
12838 'beforeselect' : true,
12841 * Fires when a list item is selected
12842 * @param {Roo.bootstrap.ComboBox} combo This combo box
12843 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12844 * @param {Number} index The index of the selected item in the dropdown list
12848 * @event beforequery
12849 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12850 * The event object passed has these properties:
12851 * @param {Roo.bootstrap.ComboBox} combo This combo box
12852 * @param {String} query The query
12853 * @param {Boolean} forceAll true to force "all" query
12854 * @param {Boolean} cancel true to cancel the query
12855 * @param {Object} e The query event object
12857 'beforequery': true,
12860 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12861 * @param {Roo.bootstrap.ComboBox} combo This combo box
12866 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12867 * @param {Roo.bootstrap.ComboBox} combo This combo box
12868 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12873 * Fires when the remove value from the combobox array
12874 * @param {Roo.bootstrap.ComboBox} combo This combo box
12878 * @event afterremove
12879 * Fires when the remove value from the combobox array
12880 * @param {Roo.bootstrap.ComboBox} combo This combo box
12882 'afterremove' : true,
12884 * @event specialfilter
12885 * Fires when specialfilter
12886 * @param {Roo.bootstrap.ComboBox} combo This combo box
12888 'specialfilter' : true,
12891 * Fires when tick the element
12892 * @param {Roo.bootstrap.ComboBox} combo This combo box
12896 * @event touchviewdisplay
12897 * Fires when touch view require special display (default is using displayField)
12898 * @param {Roo.bootstrap.ComboBox} combo This combo box
12899 * @param {Object} cfg set html .
12901 'touchviewdisplay' : true
12906 this.tickItems = [];
12908 this.selectedIndex = -1;
12909 if(this.mode == 'local'){
12910 if(config.queryDelay === undefined){
12911 this.queryDelay = 10;
12913 if(config.minChars === undefined){
12919 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12922 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12923 * rendering into an Roo.Editor, defaults to false)
12926 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12927 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12930 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12933 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12934 * the dropdown list (defaults to undefined, with no header element)
12938 * @cfg {String/Roo.Template} tpl The template to use to render the output
12942 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12944 listWidth: undefined,
12946 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12947 * mode = 'remote' or 'text' if mode = 'local')
12949 displayField: undefined,
12952 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12953 * mode = 'remote' or 'value' if mode = 'local').
12954 * Note: use of a valueField requires the user make a selection
12955 * in order for a value to be mapped.
12957 valueField: undefined,
12959 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12964 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12965 * field's data value (defaults to the underlying DOM element's name)
12967 hiddenName: undefined,
12969 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12973 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12975 selectedClass: 'active',
12978 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12982 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12983 * anchor positions (defaults to 'tl-bl')
12985 listAlign: 'tl-bl?',
12987 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12991 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12992 * query specified by the allQuery config option (defaults to 'query')
12994 triggerAction: 'query',
12996 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12997 * (defaults to 4, does not apply if editable = false)
13001 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13002 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13006 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13007 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13011 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13012 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13016 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13017 * when editable = true (defaults to false)
13019 selectOnFocus:false,
13021 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13023 queryParam: 'query',
13025 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13026 * when mode = 'remote' (defaults to 'Loading...')
13028 loadingText: 'Loading...',
13030 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13034 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13038 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13039 * traditional select (defaults to true)
13043 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13047 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13051 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13052 * listWidth has a higher value)
13056 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13057 * allow the user to set arbitrary text into the field (defaults to false)
13059 forceSelection:false,
13061 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13062 * if typeAhead = true (defaults to 250)
13064 typeAheadDelay : 250,
13066 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13067 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13069 valueNotFoundText : undefined,
13071 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13073 blockFocus : false,
13076 * @cfg {Boolean} disableClear Disable showing of clear button.
13078 disableClear : false,
13080 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13082 alwaysQuery : false,
13085 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13090 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13092 invalidClass : "has-warning",
13095 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13097 validClass : "has-success",
13100 * @cfg {Boolean} specialFilter (true|false) special filter default false
13102 specialFilter : false,
13105 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13107 mobileTouchView : true,
13110 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13112 useNativeIOS : false,
13115 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13117 mobile_restrict_height : false,
13119 ios_options : false,
13131 btnPosition : 'right',
13132 triggerList : true,
13133 showToggleBtn : true,
13135 emptyResultText: 'Empty',
13136 triggerText : 'Select',
13139 // element that contains real text value.. (when hidden is used..)
13141 getAutoCreate : function()
13146 * Render classic select for iso
13149 if(Roo.isIOS && this.useNativeIOS){
13150 cfg = this.getAutoCreateNativeIOS();
13158 if(Roo.isTouch && this.mobileTouchView){
13159 cfg = this.getAutoCreateTouchView();
13166 if(!this.tickable){
13167 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13172 * ComboBox with tickable selections
13175 var align = this.labelAlign || this.parentLabelAlign();
13178 cls : 'form-group roo-combobox-tickable' //input-group
13181 var btn_text_select = '';
13182 var btn_text_done = '';
13183 var btn_text_cancel = '';
13185 if (this.btn_text_show) {
13186 btn_text_select = 'Select';
13187 btn_text_done = 'Done';
13188 btn_text_cancel = 'Cancel';
13193 cls : 'tickable-buttons',
13198 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13199 //html : this.triggerText
13200 html: btn_text_select
13206 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13208 html: btn_text_done
13214 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13216 html: btn_text_cancel
13222 buttons.cn.unshift({
13224 cls: 'roo-select2-search-field-input'
13230 Roo.each(buttons.cn, function(c){
13232 c.cls += ' btn-' + _this.size;
13235 if (_this.disabled) {
13246 cls: 'form-hidden-field'
13250 cls: 'roo-select2-choices',
13254 cls: 'roo-select2-search-field',
13265 cls: 'roo-select2-container input-group roo-select2-container-multi',
13270 // cls: 'typeahead typeahead-long dropdown-menu',
13271 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13276 if(this.hasFeedback && !this.allowBlank){
13280 cls: 'glyphicon form-control-feedback'
13283 combobox.cn.push(feedback);
13287 if (align ==='left' && this.fieldLabel.length) {
13289 cfg.cls += ' roo-form-group-label-left';
13294 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13295 tooltip : 'This field is required'
13300 cls : 'control-label',
13301 html : this.fieldLabel
13313 var labelCfg = cfg.cn[1];
13314 var contentCfg = cfg.cn[2];
13317 if(this.indicatorpos == 'right'){
13323 cls : 'control-label',
13327 html : this.fieldLabel
13331 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13332 tooltip : 'This field is required'
13347 labelCfg = cfg.cn[0];
13348 contentCfg = cfg.cn[1];
13352 if(this.labelWidth > 12){
13353 labelCfg.style = "width: " + this.labelWidth + 'px';
13356 if(this.labelWidth < 13 && this.labelmd == 0){
13357 this.labelmd = this.labelWidth;
13360 if(this.labellg > 0){
13361 labelCfg.cls += ' col-lg-' + this.labellg;
13362 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13365 if(this.labelmd > 0){
13366 labelCfg.cls += ' col-md-' + this.labelmd;
13367 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13370 if(this.labelsm > 0){
13371 labelCfg.cls += ' col-sm-' + this.labelsm;
13372 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13375 if(this.labelxs > 0){
13376 labelCfg.cls += ' col-xs-' + this.labelxs;
13377 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13381 } else if ( this.fieldLabel.length) {
13382 // Roo.log(" label");
13386 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13387 tooltip : 'This field is required'
13391 //cls : 'input-group-addon',
13392 html : this.fieldLabel
13397 if(this.indicatorpos == 'right'){
13401 //cls : 'input-group-addon',
13402 html : this.fieldLabel
13406 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13407 tooltip : 'This field is required'
13416 // Roo.log(" no label && no align");
13423 ['xs','sm','md','lg'].map(function(size){
13424 if (settings[size]) {
13425 cfg.cls += ' col-' + size + '-' + settings[size];
13433 _initEventsCalled : false,
13436 initEvents: function()
13438 if (this._initEventsCalled) { // as we call render... prevent looping...
13441 this._initEventsCalled = true;
13444 throw "can not find store for combo";
13447 this.indicator = this.indicatorEl();
13449 this.store = Roo.factory(this.store, Roo.data);
13450 this.store.parent = this;
13452 // if we are building from html. then this element is so complex, that we can not really
13453 // use the rendered HTML.
13454 // so we have to trash and replace the previous code.
13455 if (Roo.XComponent.build_from_html) {
13456 // remove this element....
13457 var e = this.el.dom, k=0;
13458 while (e ) { e = e.previousSibling; ++k;}
13463 this.rendered = false;
13465 this.render(this.parent().getChildContainer(true), k);
13468 if(Roo.isIOS && this.useNativeIOS){
13469 this.initIOSView();
13477 if(Roo.isTouch && this.mobileTouchView){
13478 this.initTouchView();
13483 this.initTickableEvents();
13487 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13489 if(this.hiddenName){
13491 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13493 this.hiddenField.dom.value =
13494 this.hiddenValue !== undefined ? this.hiddenValue :
13495 this.value !== undefined ? this.value : '';
13497 // prevent input submission
13498 this.el.dom.removeAttribute('name');
13499 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13504 // this.el.dom.setAttribute('autocomplete', 'off');
13507 var cls = 'x-combo-list';
13509 //this.list = new Roo.Layer({
13510 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13516 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13517 _this.list.setWidth(lw);
13520 this.list.on('mouseover', this.onViewOver, this);
13521 this.list.on('mousemove', this.onViewMove, this);
13522 this.list.on('scroll', this.onViewScroll, this);
13525 this.list.swallowEvent('mousewheel');
13526 this.assetHeight = 0;
13529 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13530 this.assetHeight += this.header.getHeight();
13533 this.innerList = this.list.createChild({cls:cls+'-inner'});
13534 this.innerList.on('mouseover', this.onViewOver, this);
13535 this.innerList.on('mousemove', this.onViewMove, this);
13536 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13538 if(this.allowBlank && !this.pageSize && !this.disableClear){
13539 this.footer = this.list.createChild({cls:cls+'-ft'});
13540 this.pageTb = new Roo.Toolbar(this.footer);
13544 this.footer = this.list.createChild({cls:cls+'-ft'});
13545 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13546 {pageSize: this.pageSize});
13550 if (this.pageTb && this.allowBlank && !this.disableClear) {
13552 this.pageTb.add(new Roo.Toolbar.Fill(), {
13553 cls: 'x-btn-icon x-btn-clear',
13555 handler: function()
13558 _this.clearValue();
13559 _this.onSelect(false, -1);
13564 this.assetHeight += this.footer.getHeight();
13569 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13572 this.view = new Roo.View(this.list, this.tpl, {
13573 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13575 //this.view.wrapEl.setDisplayed(false);
13576 this.view.on('click', this.onViewClick, this);
13579 this.store.on('beforeload', this.onBeforeLoad, this);
13580 this.store.on('load', this.onLoad, this);
13581 this.store.on('loadexception', this.onLoadException, this);
13583 if(this.resizable){
13584 this.resizer = new Roo.Resizable(this.list, {
13585 pinned:true, handles:'se'
13587 this.resizer.on('resize', function(r, w, h){
13588 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13589 this.listWidth = w;
13590 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13591 this.restrictHeight();
13593 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13596 if(!this.editable){
13597 this.editable = true;
13598 this.setEditable(false);
13603 if (typeof(this.events.add.listeners) != 'undefined') {
13605 this.addicon = this.wrap.createChild(
13606 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13608 this.addicon.on('click', function(e) {
13609 this.fireEvent('add', this);
13612 if (typeof(this.events.edit.listeners) != 'undefined') {
13614 this.editicon = this.wrap.createChild(
13615 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13616 if (this.addicon) {
13617 this.editicon.setStyle('margin-left', '40px');
13619 this.editicon.on('click', function(e) {
13621 // we fire even if inothing is selected..
13622 this.fireEvent('edit', this, this.lastData );
13628 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13629 "up" : function(e){
13630 this.inKeyMode = true;
13634 "down" : function(e){
13635 if(!this.isExpanded()){
13636 this.onTriggerClick();
13638 this.inKeyMode = true;
13643 "enter" : function(e){
13644 // this.onViewClick();
13648 if(this.fireEvent("specialkey", this, e)){
13649 this.onViewClick(false);
13655 "esc" : function(e){
13659 "tab" : function(e){
13662 if(this.fireEvent("specialkey", this, e)){
13663 this.onViewClick(false);
13671 doRelay : function(foo, bar, hname){
13672 if(hname == 'down' || this.scope.isExpanded()){
13673 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13682 this.queryDelay = Math.max(this.queryDelay || 10,
13683 this.mode == 'local' ? 10 : 250);
13686 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13688 if(this.typeAhead){
13689 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13691 if(this.editable !== false){
13692 this.inputEl().on("keyup", this.onKeyUp, this);
13694 if(this.forceSelection){
13695 this.inputEl().on('blur', this.doForce, this);
13699 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13700 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13704 initTickableEvents: function()
13708 if(this.hiddenName){
13710 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13712 this.hiddenField.dom.value =
13713 this.hiddenValue !== undefined ? this.hiddenValue :
13714 this.value !== undefined ? this.value : '';
13716 // prevent input submission
13717 this.el.dom.removeAttribute('name');
13718 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13723 // this.list = this.el.select('ul.dropdown-menu',true).first();
13725 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13726 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13727 if(this.triggerList){
13728 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13731 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13732 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13734 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13735 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13737 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13738 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13740 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13741 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13742 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13745 this.cancelBtn.hide();
13750 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13751 _this.list.setWidth(lw);
13754 this.list.on('mouseover', this.onViewOver, this);
13755 this.list.on('mousemove', this.onViewMove, this);
13757 this.list.on('scroll', this.onViewScroll, this);
13760 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13761 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13764 this.view = new Roo.View(this.list, this.tpl, {
13769 selectedClass: this.selectedClass
13772 //this.view.wrapEl.setDisplayed(false);
13773 this.view.on('click', this.onViewClick, this);
13777 this.store.on('beforeload', this.onBeforeLoad, this);
13778 this.store.on('load', this.onLoad, this);
13779 this.store.on('loadexception', this.onLoadException, this);
13782 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13783 "up" : function(e){
13784 this.inKeyMode = true;
13788 "down" : function(e){
13789 this.inKeyMode = true;
13793 "enter" : function(e){
13794 if(this.fireEvent("specialkey", this, e)){
13795 this.onViewClick(false);
13801 "esc" : function(e){
13802 this.onTickableFooterButtonClick(e, false, false);
13805 "tab" : function(e){
13806 this.fireEvent("specialkey", this, e);
13808 this.onTickableFooterButtonClick(e, false, false);
13815 doRelay : function(e, fn, key){
13816 if(this.scope.isExpanded()){
13817 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13826 this.queryDelay = Math.max(this.queryDelay || 10,
13827 this.mode == 'local' ? 10 : 250);
13830 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13832 if(this.typeAhead){
13833 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13836 if(this.editable !== false){
13837 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13840 this.indicator = this.indicatorEl();
13842 if(this.indicator){
13843 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13844 this.indicator.hide();
13849 onDestroy : function(){
13851 this.view.setStore(null);
13852 this.view.el.removeAllListeners();
13853 this.view.el.remove();
13854 this.view.purgeListeners();
13857 this.list.dom.innerHTML = '';
13861 this.store.un('beforeload', this.onBeforeLoad, this);
13862 this.store.un('load', this.onLoad, this);
13863 this.store.un('loadexception', this.onLoadException, this);
13865 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13869 fireKey : function(e){
13870 if(e.isNavKeyPress() && !this.list.isVisible()){
13871 this.fireEvent("specialkey", this, e);
13876 onResize: function(w, h){
13877 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13879 // if(typeof w != 'number'){
13880 // // we do not handle it!?!?
13883 // var tw = this.trigger.getWidth();
13884 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13885 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13887 // this.inputEl().setWidth( this.adjustWidth('input', x));
13889 // //this.trigger.setStyle('left', x+'px');
13891 // if(this.list && this.listWidth === undefined){
13892 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13893 // this.list.setWidth(lw);
13894 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13902 * Allow or prevent the user from directly editing the field text. If false is passed,
13903 * the user will only be able to select from the items defined in the dropdown list. This method
13904 * is the runtime equivalent of setting the 'editable' config option at config time.
13905 * @param {Boolean} value True to allow the user to directly edit the field text
13907 setEditable : function(value){
13908 if(value == this.editable){
13911 this.editable = value;
13913 this.inputEl().dom.setAttribute('readOnly', true);
13914 this.inputEl().on('mousedown', this.onTriggerClick, this);
13915 this.inputEl().addClass('x-combo-noedit');
13917 this.inputEl().dom.setAttribute('readOnly', false);
13918 this.inputEl().un('mousedown', this.onTriggerClick, this);
13919 this.inputEl().removeClass('x-combo-noedit');
13925 onBeforeLoad : function(combo,opts){
13926 if(!this.hasFocus){
13930 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13932 this.restrictHeight();
13933 this.selectedIndex = -1;
13937 onLoad : function(){
13939 this.hasQuery = false;
13941 if(!this.hasFocus){
13945 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13946 this.loading.hide();
13949 if(this.store.getCount() > 0){
13952 this.restrictHeight();
13953 if(this.lastQuery == this.allQuery){
13954 if(this.editable && !this.tickable){
13955 this.inputEl().dom.select();
13959 !this.selectByValue(this.value, true) &&
13962 !this.store.lastOptions ||
13963 typeof(this.store.lastOptions.add) == 'undefined' ||
13964 this.store.lastOptions.add != true
13967 this.select(0, true);
13970 if(this.autoFocus){
13973 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13974 this.taTask.delay(this.typeAheadDelay);
13978 this.onEmptyResults();
13984 onLoadException : function()
13986 this.hasQuery = false;
13988 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13989 this.loading.hide();
13992 if(this.tickable && this.editable){
13997 // only causes errors at present
13998 //Roo.log(this.store.reader.jsonData);
13999 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14001 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14007 onTypeAhead : function(){
14008 if(this.store.getCount() > 0){
14009 var r = this.store.getAt(0);
14010 var newValue = r.data[this.displayField];
14011 var len = newValue.length;
14012 var selStart = this.getRawValue().length;
14014 if(selStart != len){
14015 this.setRawValue(newValue);
14016 this.selectText(selStart, newValue.length);
14022 onSelect : function(record, index){
14024 if(this.fireEvent('beforeselect', this, record, index) !== false){
14026 this.setFromData(index > -1 ? record.data : false);
14029 this.fireEvent('select', this, record, index);
14034 * Returns the currently selected field value or empty string if no value is set.
14035 * @return {String} value The selected value
14037 getValue : function()
14039 if(Roo.isIOS && this.useNativeIOS){
14040 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14044 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14047 if(this.valueField){
14048 return typeof this.value != 'undefined' ? this.value : '';
14050 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14054 getRawValue : function()
14056 if(Roo.isIOS && this.useNativeIOS){
14057 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14060 var v = this.inputEl().getValue();
14066 * Clears any text/value currently set in the field
14068 clearValue : function(){
14070 if(this.hiddenField){
14071 this.hiddenField.dom.value = '';
14074 this.setRawValue('');
14075 this.lastSelectionText = '';
14076 this.lastData = false;
14078 var close = this.closeTriggerEl();
14089 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14090 * will be displayed in the field. If the value does not match the data value of an existing item,
14091 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14092 * Otherwise the field will be blank (although the value will still be set).
14093 * @param {String} value The value to match
14095 setValue : function(v)
14097 if(Roo.isIOS && this.useNativeIOS){
14098 this.setIOSValue(v);
14108 if(this.valueField){
14109 var r = this.findRecord(this.valueField, v);
14111 text = r.data[this.displayField];
14112 }else if(this.valueNotFoundText !== undefined){
14113 text = this.valueNotFoundText;
14116 this.lastSelectionText = text;
14117 if(this.hiddenField){
14118 this.hiddenField.dom.value = v;
14120 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14123 var close = this.closeTriggerEl();
14126 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14132 * @property {Object} the last set data for the element
14137 * Sets the value of the field based on a object which is related to the record format for the store.
14138 * @param {Object} value the value to set as. or false on reset?
14140 setFromData : function(o){
14147 var dv = ''; // display value
14148 var vv = ''; // value value..
14150 if (this.displayField) {
14151 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14153 // this is an error condition!!!
14154 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14157 if(this.valueField){
14158 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14161 var close = this.closeTriggerEl();
14164 if(dv.length || vv * 1 > 0){
14166 this.blockFocus=true;
14172 if(this.hiddenField){
14173 this.hiddenField.dom.value = vv;
14175 this.lastSelectionText = dv;
14176 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14180 // no hidden field.. - we store the value in 'value', but still display
14181 // display field!!!!
14182 this.lastSelectionText = dv;
14183 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14190 reset : function(){
14191 // overridden so that last data is reset..
14198 this.setValue(this.originalValue);
14199 //this.clearInvalid();
14200 this.lastData = false;
14202 this.view.clearSelections();
14208 findRecord : function(prop, value){
14210 if(this.store.getCount() > 0){
14211 this.store.each(function(r){
14212 if(r.data[prop] == value){
14222 getName: function()
14224 // returns hidden if it's set..
14225 if (!this.rendered) {return ''};
14226 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14230 onViewMove : function(e, t){
14231 this.inKeyMode = false;
14235 onViewOver : function(e, t){
14236 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14239 var item = this.view.findItemFromChild(t);
14242 var index = this.view.indexOf(item);
14243 this.select(index, false);
14248 onViewClick : function(view, doFocus, el, e)
14250 var index = this.view.getSelectedIndexes()[0];
14252 var r = this.store.getAt(index);
14256 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14263 Roo.each(this.tickItems, function(v,k){
14265 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14267 _this.tickItems.splice(k, 1);
14269 if(typeof(e) == 'undefined' && view == false){
14270 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14282 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14283 this.tickItems.push(r.data);
14286 if(typeof(e) == 'undefined' && view == false){
14287 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14294 this.onSelect(r, index);
14296 if(doFocus !== false && !this.blockFocus){
14297 this.inputEl().focus();
14302 restrictHeight : function(){
14303 //this.innerList.dom.style.height = '';
14304 //var inner = this.innerList.dom;
14305 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14306 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14307 //this.list.beginUpdate();
14308 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14309 this.list.alignTo(this.inputEl(), this.listAlign);
14310 this.list.alignTo(this.inputEl(), this.listAlign);
14311 //this.list.endUpdate();
14315 onEmptyResults : function(){
14317 if(this.tickable && this.editable){
14318 this.hasFocus = false;
14319 this.restrictHeight();
14327 * Returns true if the dropdown list is expanded, else false.
14329 isExpanded : function(){
14330 return this.list.isVisible();
14334 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14335 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14336 * @param {String} value The data value of the item to select
14337 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14338 * selected item if it is not currently in view (defaults to true)
14339 * @return {Boolean} True if the value matched an item in the list, else false
14341 selectByValue : function(v, scrollIntoView){
14342 if(v !== undefined && v !== null){
14343 var r = this.findRecord(this.valueField || this.displayField, v);
14345 this.select(this.store.indexOf(r), scrollIntoView);
14353 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14354 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14355 * @param {Number} index The zero-based index of the list item to select
14356 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14357 * selected item if it is not currently in view (defaults to true)
14359 select : function(index, scrollIntoView){
14360 this.selectedIndex = index;
14361 this.view.select(index);
14362 if(scrollIntoView !== false){
14363 var el = this.view.getNode(index);
14365 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14368 this.list.scrollChildIntoView(el, false);
14374 selectNext : function(){
14375 var ct = this.store.getCount();
14377 if(this.selectedIndex == -1){
14379 }else if(this.selectedIndex < ct-1){
14380 this.select(this.selectedIndex+1);
14386 selectPrev : function(){
14387 var ct = this.store.getCount();
14389 if(this.selectedIndex == -1){
14391 }else if(this.selectedIndex != 0){
14392 this.select(this.selectedIndex-1);
14398 onKeyUp : function(e){
14399 if(this.editable !== false && !e.isSpecialKey()){
14400 this.lastKey = e.getKey();
14401 this.dqTask.delay(this.queryDelay);
14406 validateBlur : function(){
14407 return !this.list || !this.list.isVisible();
14411 initQuery : function(){
14413 var v = this.getRawValue();
14415 if(this.tickable && this.editable){
14416 v = this.tickableInputEl().getValue();
14423 doForce : function(){
14424 if(this.inputEl().dom.value.length > 0){
14425 this.inputEl().dom.value =
14426 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14432 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14433 * query allowing the query action to be canceled if needed.
14434 * @param {String} query The SQL query to execute
14435 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14436 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14437 * saved in the current store (defaults to false)
14439 doQuery : function(q, forceAll){
14441 if(q === undefined || q === null){
14446 forceAll: forceAll,
14450 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14455 forceAll = qe.forceAll;
14456 if(forceAll === true || (q.length >= this.minChars)){
14458 this.hasQuery = true;
14460 if(this.lastQuery != q || this.alwaysQuery){
14461 this.lastQuery = q;
14462 if(this.mode == 'local'){
14463 this.selectedIndex = -1;
14465 this.store.clearFilter();
14468 if(this.specialFilter){
14469 this.fireEvent('specialfilter', this);
14474 this.store.filter(this.displayField, q);
14477 this.store.fireEvent("datachanged", this.store);
14484 this.store.baseParams[this.queryParam] = q;
14486 var options = {params : this.getParams(q)};
14489 options.add = true;
14490 options.params.start = this.page * this.pageSize;
14493 this.store.load(options);
14496 * this code will make the page width larger, at the beginning, the list not align correctly,
14497 * we should expand the list on onLoad
14498 * so command out it
14503 this.selectedIndex = -1;
14508 this.loadNext = false;
14512 getParams : function(q){
14514 //p[this.queryParam] = q;
14518 p.limit = this.pageSize;
14524 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14526 collapse : function(){
14527 if(!this.isExpanded()){
14533 this.hasFocus = false;
14537 this.cancelBtn.hide();
14538 this.trigger.show();
14541 this.tickableInputEl().dom.value = '';
14542 this.tickableInputEl().blur();
14547 Roo.get(document).un('mousedown', this.collapseIf, this);
14548 Roo.get(document).un('mousewheel', this.collapseIf, this);
14549 if (!this.editable) {
14550 Roo.get(document).un('keydown', this.listKeyPress, this);
14552 this.fireEvent('collapse', this);
14558 collapseIf : function(e){
14559 var in_combo = e.within(this.el);
14560 var in_list = e.within(this.list);
14561 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14563 if (in_combo || in_list || is_list) {
14564 //e.stopPropagation();
14569 this.onTickableFooterButtonClick(e, false, false);
14577 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14579 expand : function(){
14581 if(this.isExpanded() || !this.hasFocus){
14585 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14586 this.list.setWidth(lw);
14592 this.restrictHeight();
14596 this.tickItems = Roo.apply([], this.item);
14599 this.cancelBtn.show();
14600 this.trigger.hide();
14603 this.tickableInputEl().focus();
14608 Roo.get(document).on('mousedown', this.collapseIf, this);
14609 Roo.get(document).on('mousewheel', this.collapseIf, this);
14610 if (!this.editable) {
14611 Roo.get(document).on('keydown', this.listKeyPress, this);
14614 this.fireEvent('expand', this);
14618 // Implements the default empty TriggerField.onTriggerClick function
14619 onTriggerClick : function(e)
14621 Roo.log('trigger click');
14623 if(this.disabled || !this.triggerList){
14628 this.loadNext = false;
14630 if(this.isExpanded()){
14632 if (!this.blockFocus) {
14633 this.inputEl().focus();
14637 this.hasFocus = true;
14638 if(this.triggerAction == 'all') {
14639 this.doQuery(this.allQuery, true);
14641 this.doQuery(this.getRawValue());
14643 if (!this.blockFocus) {
14644 this.inputEl().focus();
14649 onTickableTriggerClick : function(e)
14656 this.loadNext = false;
14657 this.hasFocus = true;
14659 if(this.triggerAction == 'all') {
14660 this.doQuery(this.allQuery, true);
14662 this.doQuery(this.getRawValue());
14666 onSearchFieldClick : function(e)
14668 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14669 this.onTickableFooterButtonClick(e, false, false);
14673 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14678 this.loadNext = false;
14679 this.hasFocus = true;
14681 if(this.triggerAction == 'all') {
14682 this.doQuery(this.allQuery, true);
14684 this.doQuery(this.getRawValue());
14688 listKeyPress : function(e)
14690 //Roo.log('listkeypress');
14691 // scroll to first matching element based on key pres..
14692 if (e.isSpecialKey()) {
14695 var k = String.fromCharCode(e.getKey()).toUpperCase();
14698 var csel = this.view.getSelectedNodes();
14699 var cselitem = false;
14701 var ix = this.view.indexOf(csel[0]);
14702 cselitem = this.store.getAt(ix);
14703 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14709 this.store.each(function(v) {
14711 // start at existing selection.
14712 if (cselitem.id == v.id) {
14718 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14719 match = this.store.indexOf(v);
14725 if (match === false) {
14726 return true; // no more action?
14729 this.view.select(match);
14730 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14731 sn.scrollIntoView(sn.dom.parentNode, false);
14734 onViewScroll : function(e, t){
14736 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){
14740 this.hasQuery = true;
14742 this.loading = this.list.select('.loading', true).first();
14744 if(this.loading === null){
14745 this.list.createChild({
14747 cls: 'loading roo-select2-more-results roo-select2-active',
14748 html: 'Loading more results...'
14751 this.loading = this.list.select('.loading', true).first();
14753 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14755 this.loading.hide();
14758 this.loading.show();
14763 this.loadNext = true;
14765 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14770 addItem : function(o)
14772 var dv = ''; // display value
14774 if (this.displayField) {
14775 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14777 // this is an error condition!!!
14778 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14785 var choice = this.choices.createChild({
14787 cls: 'roo-select2-search-choice',
14796 cls: 'roo-select2-search-choice-close fa fa-times',
14801 }, this.searchField);
14803 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14805 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14813 this.inputEl().dom.value = '';
14818 onRemoveItem : function(e, _self, o)
14820 e.preventDefault();
14822 this.lastItem = Roo.apply([], this.item);
14824 var index = this.item.indexOf(o.data) * 1;
14827 Roo.log('not this item?!');
14831 this.item.splice(index, 1);
14836 this.fireEvent('remove', this, e);
14842 syncValue : function()
14844 if(!this.item.length){
14851 Roo.each(this.item, function(i){
14852 if(_this.valueField){
14853 value.push(i[_this.valueField]);
14860 this.value = value.join(',');
14862 if(this.hiddenField){
14863 this.hiddenField.dom.value = this.value;
14866 this.store.fireEvent("datachanged", this.store);
14871 clearItem : function()
14873 if(!this.multiple){
14879 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14887 if(this.tickable && !Roo.isTouch){
14888 this.view.refresh();
14892 inputEl: function ()
14894 if(Roo.isIOS && this.useNativeIOS){
14895 return this.el.select('select.roo-ios-select', true).first();
14898 if(Roo.isTouch && this.mobileTouchView){
14899 return this.el.select('input.form-control',true).first();
14903 return this.searchField;
14906 return this.el.select('input.form-control',true).first();
14909 onTickableFooterButtonClick : function(e, btn, el)
14911 e.preventDefault();
14913 this.lastItem = Roo.apply([], this.item);
14915 if(btn && btn.name == 'cancel'){
14916 this.tickItems = Roo.apply([], this.item);
14925 Roo.each(this.tickItems, function(o){
14933 validate : function()
14935 if(this.getVisibilityEl().hasClass('hidden')){
14939 var v = this.getRawValue();
14942 v = this.getValue();
14945 if(this.disabled || this.allowBlank || v.length){
14950 this.markInvalid();
14954 tickableInputEl : function()
14956 if(!this.tickable || !this.editable){
14957 return this.inputEl();
14960 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14964 getAutoCreateTouchView : function()
14969 cls: 'form-group' //input-group
14975 type : this.inputType,
14976 cls : 'form-control x-combo-noedit',
14977 autocomplete: 'new-password',
14978 placeholder : this.placeholder || '',
14983 input.name = this.name;
14987 input.cls += ' input-' + this.size;
14990 if (this.disabled) {
14991 input.disabled = true;
15002 inputblock.cls += ' input-group';
15004 inputblock.cn.unshift({
15006 cls : 'input-group-addon',
15011 if(this.removable && !this.multiple){
15012 inputblock.cls += ' roo-removable';
15014 inputblock.cn.push({
15017 cls : 'roo-combo-removable-btn close'
15021 if(this.hasFeedback && !this.allowBlank){
15023 inputblock.cls += ' has-feedback';
15025 inputblock.cn.push({
15027 cls: 'glyphicon form-control-feedback'
15034 inputblock.cls += (this.before) ? '' : ' input-group';
15036 inputblock.cn.push({
15038 cls : 'input-group-addon',
15049 cls: 'form-hidden-field'
15063 cls: 'form-hidden-field'
15067 cls: 'roo-select2-choices',
15071 cls: 'roo-select2-search-field',
15084 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15090 if(!this.multiple && this.showToggleBtn){
15097 if (this.caret != false) {
15100 cls: 'fa fa-' + this.caret
15107 cls : 'input-group-addon btn dropdown-toggle',
15112 cls: 'combobox-clear',
15126 combobox.cls += ' roo-select2-container-multi';
15129 var align = this.labelAlign || this.parentLabelAlign();
15131 if (align ==='left' && this.fieldLabel.length) {
15136 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15137 tooltip : 'This field is required'
15141 cls : 'control-label',
15142 html : this.fieldLabel
15153 var labelCfg = cfg.cn[1];
15154 var contentCfg = cfg.cn[2];
15157 if(this.indicatorpos == 'right'){
15162 cls : 'control-label',
15166 html : this.fieldLabel
15170 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15171 tooltip : 'This field is required'
15184 labelCfg = cfg.cn[0];
15185 contentCfg = cfg.cn[1];
15190 if(this.labelWidth > 12){
15191 labelCfg.style = "width: " + this.labelWidth + 'px';
15194 if(this.labelWidth < 13 && this.labelmd == 0){
15195 this.labelmd = this.labelWidth;
15198 if(this.labellg > 0){
15199 labelCfg.cls += ' col-lg-' + this.labellg;
15200 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15203 if(this.labelmd > 0){
15204 labelCfg.cls += ' col-md-' + this.labelmd;
15205 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15208 if(this.labelsm > 0){
15209 labelCfg.cls += ' col-sm-' + this.labelsm;
15210 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15213 if(this.labelxs > 0){
15214 labelCfg.cls += ' col-xs-' + this.labelxs;
15215 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15219 } else if ( this.fieldLabel.length) {
15223 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15224 tooltip : 'This field is required'
15228 cls : 'control-label',
15229 html : this.fieldLabel
15240 if(this.indicatorpos == 'right'){
15244 cls : 'control-label',
15245 html : this.fieldLabel,
15249 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15250 tooltip : 'This field is required'
15267 var settings = this;
15269 ['xs','sm','md','lg'].map(function(size){
15270 if (settings[size]) {
15271 cfg.cls += ' col-' + size + '-' + settings[size];
15278 initTouchView : function()
15280 this.renderTouchView();
15282 this.touchViewEl.on('scroll', function(){
15283 this.el.dom.scrollTop = 0;
15286 this.originalValue = this.getValue();
15288 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15290 this.inputEl().on("click", this.showTouchView, this);
15291 if (this.triggerEl) {
15292 this.triggerEl.on("click", this.showTouchView, this);
15296 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15297 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15299 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15301 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15302 this.store.on('load', this.onTouchViewLoad, this);
15303 this.store.on('loadexception', this.onTouchViewLoadException, this);
15305 if(this.hiddenName){
15307 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15309 this.hiddenField.dom.value =
15310 this.hiddenValue !== undefined ? this.hiddenValue :
15311 this.value !== undefined ? this.value : '';
15313 this.el.dom.removeAttribute('name');
15314 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15318 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15319 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15322 if(this.removable && !this.multiple){
15323 var close = this.closeTriggerEl();
15325 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15326 close.on('click', this.removeBtnClick, this, close);
15330 * fix the bug in Safari iOS8
15332 this.inputEl().on("focus", function(e){
15333 document.activeElement.blur();
15336 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15343 renderTouchView : function()
15345 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15346 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15348 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15349 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15351 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15352 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15353 this.touchViewBodyEl.setStyle('overflow', 'auto');
15355 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15356 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15358 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15359 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15363 showTouchView : function()
15369 this.touchViewHeaderEl.hide();
15371 if(this.modalTitle.length){
15372 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15373 this.touchViewHeaderEl.show();
15376 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15377 this.touchViewEl.show();
15379 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15381 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15382 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15384 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15386 if(this.modalTitle.length){
15387 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15390 this.touchViewBodyEl.setHeight(bodyHeight);
15394 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15396 this.touchViewEl.addClass('in');
15399 if(this._touchViewMask){
15400 Roo.get(document.body).addClass("x-body-masked");
15401 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15402 this._touchViewMask.setStyle('z-index', 10000);
15403 this._touchViewMask.addClass('show');
15406 this.doTouchViewQuery();
15410 hideTouchView : function()
15412 this.touchViewEl.removeClass('in');
15416 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15418 this.touchViewEl.setStyle('display', 'none');
15421 if(this._touchViewMask){
15422 this._touchViewMask.removeClass('show');
15423 Roo.get(document.body).removeClass("x-body-masked");
15427 setTouchViewValue : function()
15434 Roo.each(this.tickItems, function(o){
15439 this.hideTouchView();
15442 doTouchViewQuery : function()
15451 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15455 if(!this.alwaysQuery || this.mode == 'local'){
15456 this.onTouchViewLoad();
15463 onTouchViewBeforeLoad : function(combo,opts)
15469 onTouchViewLoad : function()
15471 if(this.store.getCount() < 1){
15472 this.onTouchViewEmptyResults();
15476 this.clearTouchView();
15478 var rawValue = this.getRawValue();
15480 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15482 this.tickItems = [];
15484 this.store.data.each(function(d, rowIndex){
15485 var row = this.touchViewListGroup.createChild(template);
15487 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15488 row.addClass(d.data.cls);
15491 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15494 html : d.data[this.displayField]
15497 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15498 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15501 row.removeClass('selected');
15502 if(!this.multiple && this.valueField &&
15503 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15506 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15507 row.addClass('selected');
15510 if(this.multiple && this.valueField &&
15511 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15515 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15516 this.tickItems.push(d.data);
15519 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15523 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15525 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15527 if(this.modalTitle.length){
15528 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15531 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15533 if(this.mobile_restrict_height && listHeight < bodyHeight){
15534 this.touchViewBodyEl.setHeight(listHeight);
15539 if(firstChecked && listHeight > bodyHeight){
15540 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15545 onTouchViewLoadException : function()
15547 this.hideTouchView();
15550 onTouchViewEmptyResults : function()
15552 this.clearTouchView();
15554 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15556 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15560 clearTouchView : function()
15562 this.touchViewListGroup.dom.innerHTML = '';
15565 onTouchViewClick : function(e, el, o)
15567 e.preventDefault();
15570 var rowIndex = o.rowIndex;
15572 var r = this.store.getAt(rowIndex);
15574 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15576 if(!this.multiple){
15577 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15578 c.dom.removeAttribute('checked');
15581 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15583 this.setFromData(r.data);
15585 var close = this.closeTriggerEl();
15591 this.hideTouchView();
15593 this.fireEvent('select', this, r, rowIndex);
15598 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15599 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15600 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15604 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15605 this.addItem(r.data);
15606 this.tickItems.push(r.data);
15610 getAutoCreateNativeIOS : function()
15613 cls: 'form-group' //input-group,
15618 cls : 'roo-ios-select'
15622 combobox.name = this.name;
15625 if (this.disabled) {
15626 combobox.disabled = true;
15629 var settings = this;
15631 ['xs','sm','md','lg'].map(function(size){
15632 if (settings[size]) {
15633 cfg.cls += ' col-' + size + '-' + settings[size];
15643 initIOSView : function()
15645 this.store.on('load', this.onIOSViewLoad, this);
15650 onIOSViewLoad : function()
15652 if(this.store.getCount() < 1){
15656 this.clearIOSView();
15658 if(this.allowBlank) {
15660 var default_text = '-- SELECT --';
15662 if(this.placeholder.length){
15663 default_text = this.placeholder;
15666 if(this.emptyTitle.length){
15667 default_text += ' - ' + this.emptyTitle + ' -';
15670 var opt = this.inputEl().createChild({
15673 html : default_text
15677 o[this.valueField] = 0;
15678 o[this.displayField] = default_text;
15680 this.ios_options.push({
15687 this.store.data.each(function(d, rowIndex){
15691 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15692 html = d.data[this.displayField];
15697 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15698 value = d.data[this.valueField];
15707 if(this.value == d.data[this.valueField]){
15708 option['selected'] = true;
15711 var opt = this.inputEl().createChild(option);
15713 this.ios_options.push({
15720 this.inputEl().on('change', function(){
15721 this.fireEvent('select', this);
15726 clearIOSView: function()
15728 this.inputEl().dom.innerHTML = '';
15730 this.ios_options = [];
15733 setIOSValue: function(v)
15737 if(!this.ios_options){
15741 Roo.each(this.ios_options, function(opts){
15743 opts.el.dom.removeAttribute('selected');
15745 if(opts.data[this.valueField] != v){
15749 opts.el.dom.setAttribute('selected', true);
15755 * @cfg {Boolean} grow
15759 * @cfg {Number} growMin
15763 * @cfg {Number} growMax
15772 Roo.apply(Roo.bootstrap.ComboBox, {
15776 cls: 'modal-header',
15798 cls: 'list-group-item',
15802 cls: 'roo-combobox-list-group-item-value'
15806 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15820 listItemCheckbox : {
15822 cls: 'list-group-item',
15826 cls: 'roo-combobox-list-group-item-value'
15830 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15846 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15851 cls: 'modal-footer',
15859 cls: 'col-xs-6 text-left',
15862 cls: 'btn btn-danger roo-touch-view-cancel',
15868 cls: 'col-xs-6 text-right',
15871 cls: 'btn btn-success roo-touch-view-ok',
15882 Roo.apply(Roo.bootstrap.ComboBox, {
15884 touchViewTemplate : {
15886 cls: 'modal fade roo-combobox-touch-view',
15890 cls: 'modal-dialog',
15891 style : 'position:fixed', // we have to fix position....
15895 cls: 'modal-content',
15897 Roo.bootstrap.ComboBox.header,
15898 Roo.bootstrap.ComboBox.body,
15899 Roo.bootstrap.ComboBox.footer
15908 * Ext JS Library 1.1.1
15909 * Copyright(c) 2006-2007, Ext JS, LLC.
15911 * Originally Released Under LGPL - original licence link has changed is not relivant.
15914 * <script type="text/javascript">
15919 * @extends Roo.util.Observable
15920 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15921 * This class also supports single and multi selection modes. <br>
15922 * Create a data model bound view:
15924 var store = new Roo.data.Store(...);
15926 var view = new Roo.View({
15928 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15930 singleSelect: true,
15931 selectedClass: "ydataview-selected",
15935 // listen for node click?
15936 view.on("click", function(vw, index, node, e){
15937 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15941 dataModel.load("foobar.xml");
15943 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15945 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15946 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15948 * Note: old style constructor is still suported (container, template, config)
15951 * Create a new View
15952 * @param {Object} config The config object
15955 Roo.View = function(config, depreciated_tpl, depreciated_config){
15957 this.parent = false;
15959 if (typeof(depreciated_tpl) == 'undefined') {
15960 // new way.. - universal constructor.
15961 Roo.apply(this, config);
15962 this.el = Roo.get(this.el);
15965 this.el = Roo.get(config);
15966 this.tpl = depreciated_tpl;
15967 Roo.apply(this, depreciated_config);
15969 this.wrapEl = this.el.wrap().wrap();
15970 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15973 if(typeof(this.tpl) == "string"){
15974 this.tpl = new Roo.Template(this.tpl);
15976 // support xtype ctors..
15977 this.tpl = new Roo.factory(this.tpl, Roo);
15981 this.tpl.compile();
15986 * @event beforeclick
15987 * Fires before a click is processed. Returns false to cancel the default action.
15988 * @param {Roo.View} this
15989 * @param {Number} index The index of the target node
15990 * @param {HTMLElement} node The target node
15991 * @param {Roo.EventObject} e The raw event object
15993 "beforeclick" : true,
15996 * Fires when a template node is clicked.
15997 * @param {Roo.View} this
15998 * @param {Number} index The index of the target node
15999 * @param {HTMLElement} node The target node
16000 * @param {Roo.EventObject} e The raw event object
16005 * Fires when a template node is double clicked.
16006 * @param {Roo.View} this
16007 * @param {Number} index The index of the target node
16008 * @param {HTMLElement} node The target node
16009 * @param {Roo.EventObject} e The raw event object
16013 * @event contextmenu
16014 * Fires when a template node is right clicked.
16015 * @param {Roo.View} this
16016 * @param {Number} index The index of the target node
16017 * @param {HTMLElement} node The target node
16018 * @param {Roo.EventObject} e The raw event object
16020 "contextmenu" : true,
16022 * @event selectionchange
16023 * Fires when the selected nodes change.
16024 * @param {Roo.View} this
16025 * @param {Array} selections Array of the selected nodes
16027 "selectionchange" : true,
16030 * @event beforeselect
16031 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16032 * @param {Roo.View} this
16033 * @param {HTMLElement} node The node to be selected
16034 * @param {Array} selections Array of currently selected nodes
16036 "beforeselect" : true,
16038 * @event preparedata
16039 * Fires on every row to render, to allow you to change the data.
16040 * @param {Roo.View} this
16041 * @param {Object} data to be rendered (change this)
16043 "preparedata" : true
16051 "click": this.onClick,
16052 "dblclick": this.onDblClick,
16053 "contextmenu": this.onContextMenu,
16057 this.selections = [];
16059 this.cmp = new Roo.CompositeElementLite([]);
16061 this.store = Roo.factory(this.store, Roo.data);
16062 this.setStore(this.store, true);
16065 if ( this.footer && this.footer.xtype) {
16067 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16069 this.footer.dataSource = this.store;
16070 this.footer.container = fctr;
16071 this.footer = Roo.factory(this.footer, Roo);
16072 fctr.insertFirst(this.el);
16074 // this is a bit insane - as the paging toolbar seems to detach the el..
16075 // dom.parentNode.parentNode.parentNode
16076 // they get detached?
16080 Roo.View.superclass.constructor.call(this);
16085 Roo.extend(Roo.View, Roo.util.Observable, {
16088 * @cfg {Roo.data.Store} store Data store to load data from.
16093 * @cfg {String|Roo.Element} el The container element.
16098 * @cfg {String|Roo.Template} tpl The template used by this View
16102 * @cfg {String} dataName the named area of the template to use as the data area
16103 * Works with domtemplates roo-name="name"
16107 * @cfg {String} selectedClass The css class to add to selected nodes
16109 selectedClass : "x-view-selected",
16111 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16116 * @cfg {String} text to display on mask (default Loading)
16120 * @cfg {Boolean} multiSelect Allow multiple selection
16122 multiSelect : false,
16124 * @cfg {Boolean} singleSelect Allow single selection
16126 singleSelect: false,
16129 * @cfg {Boolean} toggleSelect - selecting
16131 toggleSelect : false,
16134 * @cfg {Boolean} tickable - selecting
16139 * Returns the element this view is bound to.
16140 * @return {Roo.Element}
16142 getEl : function(){
16143 return this.wrapEl;
16149 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16151 refresh : function(){
16152 //Roo.log('refresh');
16155 // if we are using something like 'domtemplate', then
16156 // the what gets used is:
16157 // t.applySubtemplate(NAME, data, wrapping data..)
16158 // the outer template then get' applied with
16159 // the store 'extra data'
16160 // and the body get's added to the
16161 // roo-name="data" node?
16162 // <span class='roo-tpl-{name}'></span> ?????
16166 this.clearSelections();
16167 this.el.update("");
16169 var records = this.store.getRange();
16170 if(records.length < 1) {
16172 // is this valid?? = should it render a template??
16174 this.el.update(this.emptyText);
16178 if (this.dataName) {
16179 this.el.update(t.apply(this.store.meta)); //????
16180 el = this.el.child('.roo-tpl-' + this.dataName);
16183 for(var i = 0, len = records.length; i < len; i++){
16184 var data = this.prepareData(records[i].data, i, records[i]);
16185 this.fireEvent("preparedata", this, data, i, records[i]);
16187 var d = Roo.apply({}, data);
16190 Roo.apply(d, {'roo-id' : Roo.id()});
16194 Roo.each(this.parent.item, function(item){
16195 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16198 Roo.apply(d, {'roo-data-checked' : 'checked'});
16202 html[html.length] = Roo.util.Format.trim(
16204 t.applySubtemplate(this.dataName, d, this.store.meta) :
16211 el.update(html.join(""));
16212 this.nodes = el.dom.childNodes;
16213 this.updateIndexes(0);
16218 * Function to override to reformat the data that is sent to
16219 * the template for each node.
16220 * DEPRICATED - use the preparedata event handler.
16221 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16222 * a JSON object for an UpdateManager bound view).
16224 prepareData : function(data, index, record)
16226 this.fireEvent("preparedata", this, data, index, record);
16230 onUpdate : function(ds, record){
16231 // Roo.log('on update');
16232 this.clearSelections();
16233 var index = this.store.indexOf(record);
16234 var n = this.nodes[index];
16235 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16236 n.parentNode.removeChild(n);
16237 this.updateIndexes(index, index);
16243 onAdd : function(ds, records, index)
16245 //Roo.log(['on Add', ds, records, index] );
16246 this.clearSelections();
16247 if(this.nodes.length == 0){
16251 var n = this.nodes[index];
16252 for(var i = 0, len = records.length; i < len; i++){
16253 var d = this.prepareData(records[i].data, i, records[i]);
16255 this.tpl.insertBefore(n, d);
16258 this.tpl.append(this.el, d);
16261 this.updateIndexes(index);
16264 onRemove : function(ds, record, index){
16265 // Roo.log('onRemove');
16266 this.clearSelections();
16267 var el = this.dataName ?
16268 this.el.child('.roo-tpl-' + this.dataName) :
16271 el.dom.removeChild(this.nodes[index]);
16272 this.updateIndexes(index);
16276 * Refresh an individual node.
16277 * @param {Number} index
16279 refreshNode : function(index){
16280 this.onUpdate(this.store, this.store.getAt(index));
16283 updateIndexes : function(startIndex, endIndex){
16284 var ns = this.nodes;
16285 startIndex = startIndex || 0;
16286 endIndex = endIndex || ns.length - 1;
16287 for(var i = startIndex; i <= endIndex; i++){
16288 ns[i].nodeIndex = i;
16293 * Changes the data store this view uses and refresh the view.
16294 * @param {Store} store
16296 setStore : function(store, initial){
16297 if(!initial && this.store){
16298 this.store.un("datachanged", this.refresh);
16299 this.store.un("add", this.onAdd);
16300 this.store.un("remove", this.onRemove);
16301 this.store.un("update", this.onUpdate);
16302 this.store.un("clear", this.refresh);
16303 this.store.un("beforeload", this.onBeforeLoad);
16304 this.store.un("load", this.onLoad);
16305 this.store.un("loadexception", this.onLoad);
16309 store.on("datachanged", this.refresh, this);
16310 store.on("add", this.onAdd, this);
16311 store.on("remove", this.onRemove, this);
16312 store.on("update", this.onUpdate, this);
16313 store.on("clear", this.refresh, this);
16314 store.on("beforeload", this.onBeforeLoad, this);
16315 store.on("load", this.onLoad, this);
16316 store.on("loadexception", this.onLoad, this);
16324 * onbeforeLoad - masks the loading area.
16327 onBeforeLoad : function(store,opts)
16329 //Roo.log('onBeforeLoad');
16331 this.el.update("");
16333 this.el.mask(this.mask ? this.mask : "Loading" );
16335 onLoad : function ()
16342 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16343 * @param {HTMLElement} node
16344 * @return {HTMLElement} The template node
16346 findItemFromChild : function(node){
16347 var el = this.dataName ?
16348 this.el.child('.roo-tpl-' + this.dataName,true) :
16351 if(!node || node.parentNode == el){
16354 var p = node.parentNode;
16355 while(p && p != el){
16356 if(p.parentNode == el){
16365 onClick : function(e){
16366 var item = this.findItemFromChild(e.getTarget());
16368 var index = this.indexOf(item);
16369 if(this.onItemClick(item, index, e) !== false){
16370 this.fireEvent("click", this, index, item, e);
16373 this.clearSelections();
16378 onContextMenu : function(e){
16379 var item = this.findItemFromChild(e.getTarget());
16381 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16386 onDblClick : function(e){
16387 var item = this.findItemFromChild(e.getTarget());
16389 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16393 onItemClick : function(item, index, e)
16395 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16398 if (this.toggleSelect) {
16399 var m = this.isSelected(item) ? 'unselect' : 'select';
16402 _t[m](item, true, false);
16405 if(this.multiSelect || this.singleSelect){
16406 if(this.multiSelect && e.shiftKey && this.lastSelection){
16407 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16409 this.select(item, this.multiSelect && e.ctrlKey);
16410 this.lastSelection = item;
16413 if(!this.tickable){
16414 e.preventDefault();
16422 * Get the number of selected nodes.
16425 getSelectionCount : function(){
16426 return this.selections.length;
16430 * Get the currently selected nodes.
16431 * @return {Array} An array of HTMLElements
16433 getSelectedNodes : function(){
16434 return this.selections;
16438 * Get the indexes of the selected nodes.
16441 getSelectedIndexes : function(){
16442 var indexes = [], s = this.selections;
16443 for(var i = 0, len = s.length; i < len; i++){
16444 indexes.push(s[i].nodeIndex);
16450 * Clear all selections
16451 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16453 clearSelections : function(suppressEvent){
16454 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16455 this.cmp.elements = this.selections;
16456 this.cmp.removeClass(this.selectedClass);
16457 this.selections = [];
16458 if(!suppressEvent){
16459 this.fireEvent("selectionchange", this, this.selections);
16465 * Returns true if the passed node is selected
16466 * @param {HTMLElement/Number} node The node or node index
16467 * @return {Boolean}
16469 isSelected : function(node){
16470 var s = this.selections;
16474 node = this.getNode(node);
16475 return s.indexOf(node) !== -1;
16480 * @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
16481 * @param {Boolean} keepExisting (optional) true to keep existing selections
16482 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16484 select : function(nodeInfo, keepExisting, suppressEvent){
16485 if(nodeInfo instanceof Array){
16487 this.clearSelections(true);
16489 for(var i = 0, len = nodeInfo.length; i < len; i++){
16490 this.select(nodeInfo[i], true, true);
16494 var node = this.getNode(nodeInfo);
16495 if(!node || this.isSelected(node)){
16496 return; // already selected.
16499 this.clearSelections(true);
16502 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16503 Roo.fly(node).addClass(this.selectedClass);
16504 this.selections.push(node);
16505 if(!suppressEvent){
16506 this.fireEvent("selectionchange", this, this.selections);
16514 * @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
16515 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16516 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16518 unselect : function(nodeInfo, keepExisting, suppressEvent)
16520 if(nodeInfo instanceof Array){
16521 Roo.each(this.selections, function(s) {
16522 this.unselect(s, nodeInfo);
16526 var node = this.getNode(nodeInfo);
16527 if(!node || !this.isSelected(node)){
16528 //Roo.log("not selected");
16529 return; // not selected.
16533 Roo.each(this.selections, function(s) {
16535 Roo.fly(node).removeClass(this.selectedClass);
16542 this.selections= ns;
16543 this.fireEvent("selectionchange", this, this.selections);
16547 * Gets a template node.
16548 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16549 * @return {HTMLElement} The node or null if it wasn't found
16551 getNode : function(nodeInfo){
16552 if(typeof nodeInfo == "string"){
16553 return document.getElementById(nodeInfo);
16554 }else if(typeof nodeInfo == "number"){
16555 return this.nodes[nodeInfo];
16561 * Gets a range template nodes.
16562 * @param {Number} startIndex
16563 * @param {Number} endIndex
16564 * @return {Array} An array of nodes
16566 getNodes : function(start, end){
16567 var ns = this.nodes;
16568 start = start || 0;
16569 end = typeof end == "undefined" ? ns.length - 1 : end;
16572 for(var i = start; i <= end; i++){
16576 for(var i = start; i >= end; i--){
16584 * Finds the index of the passed node
16585 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16586 * @return {Number} The index of the node or -1
16588 indexOf : function(node){
16589 node = this.getNode(node);
16590 if(typeof node.nodeIndex == "number"){
16591 return node.nodeIndex;
16593 var ns = this.nodes;
16594 for(var i = 0, len = ns.length; i < len; i++){
16605 * based on jquery fullcalendar
16609 Roo.bootstrap = Roo.bootstrap || {};
16611 * @class Roo.bootstrap.Calendar
16612 * @extends Roo.bootstrap.Component
16613 * Bootstrap Calendar class
16614 * @cfg {Boolean} loadMask (true|false) default false
16615 * @cfg {Object} header generate the user specific header of the calendar, default false
16618 * Create a new Container
16619 * @param {Object} config The config object
16624 Roo.bootstrap.Calendar = function(config){
16625 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16629 * Fires when a date is selected
16630 * @param {DatePicker} this
16631 * @param {Date} date The selected date
16635 * @event monthchange
16636 * Fires when the displayed month changes
16637 * @param {DatePicker} this
16638 * @param {Date} date The selected month
16640 'monthchange': true,
16642 * @event evententer
16643 * Fires when mouse over an event
16644 * @param {Calendar} this
16645 * @param {event} Event
16647 'evententer': true,
16649 * @event eventleave
16650 * Fires when the mouse leaves an
16651 * @param {Calendar} this
16654 'eventleave': true,
16656 * @event eventclick
16657 * Fires when the mouse click an
16658 * @param {Calendar} this
16667 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16670 * @cfg {Number} startDay
16671 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16679 getAutoCreate : function(){
16682 var fc_button = function(name, corner, style, content ) {
16683 return Roo.apply({},{
16685 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16687 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16690 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16701 style : 'width:100%',
16708 cls : 'fc-header-left',
16710 fc_button('prev', 'left', 'arrow', '‹' ),
16711 fc_button('next', 'right', 'arrow', '›' ),
16712 { tag: 'span', cls: 'fc-header-space' },
16713 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16721 cls : 'fc-header-center',
16725 cls: 'fc-header-title',
16728 html : 'month / year'
16736 cls : 'fc-header-right',
16738 /* fc_button('month', 'left', '', 'month' ),
16739 fc_button('week', '', '', 'week' ),
16740 fc_button('day', 'right', '', 'day' )
16752 header = this.header;
16755 var cal_heads = function() {
16757 // fixme - handle this.
16759 for (var i =0; i < Date.dayNames.length; i++) {
16760 var d = Date.dayNames[i];
16763 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16764 html : d.substring(0,3)
16768 ret[0].cls += ' fc-first';
16769 ret[6].cls += ' fc-last';
16772 var cal_cell = function(n) {
16775 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16780 cls: 'fc-day-number',
16784 cls: 'fc-day-content',
16788 style: 'position: relative;' // height: 17px;
16800 var cal_rows = function() {
16803 for (var r = 0; r < 6; r++) {
16810 for (var i =0; i < Date.dayNames.length; i++) {
16811 var d = Date.dayNames[i];
16812 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16815 row.cn[0].cls+=' fc-first';
16816 row.cn[0].cn[0].style = 'min-height:90px';
16817 row.cn[6].cls+=' fc-last';
16821 ret[0].cls += ' fc-first';
16822 ret[4].cls += ' fc-prev-last';
16823 ret[5].cls += ' fc-last';
16830 cls: 'fc-border-separate',
16831 style : 'width:100%',
16839 cls : 'fc-first fc-last',
16857 cls : 'fc-content',
16858 style : "position: relative;",
16861 cls : 'fc-view fc-view-month fc-grid',
16862 style : 'position: relative',
16863 unselectable : 'on',
16866 cls : 'fc-event-container',
16867 style : 'position:absolute;z-index:8;top:0;left:0;'
16885 initEvents : function()
16888 throw "can not find store for calendar";
16894 style: "text-align:center",
16898 style: "background-color:white;width:50%;margin:250 auto",
16902 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16913 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16915 var size = this.el.select('.fc-content', true).first().getSize();
16916 this.maskEl.setSize(size.width, size.height);
16917 this.maskEl.enableDisplayMode("block");
16918 if(!this.loadMask){
16919 this.maskEl.hide();
16922 this.store = Roo.factory(this.store, Roo.data);
16923 this.store.on('load', this.onLoad, this);
16924 this.store.on('beforeload', this.onBeforeLoad, this);
16928 this.cells = this.el.select('.fc-day',true);
16929 //Roo.log(this.cells);
16930 this.textNodes = this.el.query('.fc-day-number');
16931 this.cells.addClassOnOver('fc-state-hover');
16933 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16934 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16935 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16936 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16938 this.on('monthchange', this.onMonthChange, this);
16940 this.update(new Date().clearTime());
16943 resize : function() {
16944 var sz = this.el.getSize();
16946 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16947 this.el.select('.fc-day-content div',true).setHeight(34);
16952 showPrevMonth : function(e){
16953 this.update(this.activeDate.add("mo", -1));
16955 showToday : function(e){
16956 this.update(new Date().clearTime());
16959 showNextMonth : function(e){
16960 this.update(this.activeDate.add("mo", 1));
16964 showPrevYear : function(){
16965 this.update(this.activeDate.add("y", -1));
16969 showNextYear : function(){
16970 this.update(this.activeDate.add("y", 1));
16975 update : function(date)
16977 var vd = this.activeDate;
16978 this.activeDate = date;
16979 // if(vd && this.el){
16980 // var t = date.getTime();
16981 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16982 // Roo.log('using add remove');
16984 // this.fireEvent('monthchange', this, date);
16986 // this.cells.removeClass("fc-state-highlight");
16987 // this.cells.each(function(c){
16988 // if(c.dateValue == t){
16989 // c.addClass("fc-state-highlight");
16990 // setTimeout(function(){
16991 // try{c.dom.firstChild.focus();}catch(e){}
17001 var days = date.getDaysInMonth();
17003 var firstOfMonth = date.getFirstDateOfMonth();
17004 var startingPos = firstOfMonth.getDay()-this.startDay;
17006 if(startingPos < this.startDay){
17010 var pm = date.add(Date.MONTH, -1);
17011 var prevStart = pm.getDaysInMonth()-startingPos;
17013 this.cells = this.el.select('.fc-day',true);
17014 this.textNodes = this.el.query('.fc-day-number');
17015 this.cells.addClassOnOver('fc-state-hover');
17017 var cells = this.cells.elements;
17018 var textEls = this.textNodes;
17020 Roo.each(cells, function(cell){
17021 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17024 days += startingPos;
17026 // convert everything to numbers so it's fast
17027 var day = 86400000;
17028 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17031 //Roo.log(prevStart);
17033 var today = new Date().clearTime().getTime();
17034 var sel = date.clearTime().getTime();
17035 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17036 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17037 var ddMatch = this.disabledDatesRE;
17038 var ddText = this.disabledDatesText;
17039 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17040 var ddaysText = this.disabledDaysText;
17041 var format = this.format;
17043 var setCellClass = function(cal, cell){
17047 //Roo.log('set Cell Class');
17049 var t = d.getTime();
17053 cell.dateValue = t;
17055 cell.className += " fc-today";
17056 cell.className += " fc-state-highlight";
17057 cell.title = cal.todayText;
17060 // disable highlight in other month..
17061 //cell.className += " fc-state-highlight";
17066 cell.className = " fc-state-disabled";
17067 cell.title = cal.minText;
17071 cell.className = " fc-state-disabled";
17072 cell.title = cal.maxText;
17076 if(ddays.indexOf(d.getDay()) != -1){
17077 cell.title = ddaysText;
17078 cell.className = " fc-state-disabled";
17081 if(ddMatch && format){
17082 var fvalue = d.dateFormat(format);
17083 if(ddMatch.test(fvalue)){
17084 cell.title = ddText.replace("%0", fvalue);
17085 cell.className = " fc-state-disabled";
17089 if (!cell.initialClassName) {
17090 cell.initialClassName = cell.dom.className;
17093 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17098 for(; i < startingPos; i++) {
17099 textEls[i].innerHTML = (++prevStart);
17100 d.setDate(d.getDate()+1);
17102 cells[i].className = "fc-past fc-other-month";
17103 setCellClass(this, cells[i]);
17108 for(; i < days; i++){
17109 intDay = i - startingPos + 1;
17110 textEls[i].innerHTML = (intDay);
17111 d.setDate(d.getDate()+1);
17113 cells[i].className = ''; // "x-date-active";
17114 setCellClass(this, cells[i]);
17118 for(; i < 42; i++) {
17119 textEls[i].innerHTML = (++extraDays);
17120 d.setDate(d.getDate()+1);
17122 cells[i].className = "fc-future fc-other-month";
17123 setCellClass(this, cells[i]);
17126 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17128 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17130 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17131 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17133 if(totalRows != 6){
17134 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17135 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17138 this.fireEvent('monthchange', this, date);
17142 if(!this.internalRender){
17143 var main = this.el.dom.firstChild;
17144 var w = main.offsetWidth;
17145 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17146 Roo.fly(main).setWidth(w);
17147 this.internalRender = true;
17148 // opera does not respect the auto grow header center column
17149 // then, after it gets a width opera refuses to recalculate
17150 // without a second pass
17151 if(Roo.isOpera && !this.secondPass){
17152 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17153 this.secondPass = true;
17154 this.update.defer(10, this, [date]);
17161 findCell : function(dt) {
17162 dt = dt.clearTime().getTime();
17164 this.cells.each(function(c){
17165 //Roo.log("check " +c.dateValue + '?=' + dt);
17166 if(c.dateValue == dt){
17176 findCells : function(ev) {
17177 var s = ev.start.clone().clearTime().getTime();
17179 var e= ev.end.clone().clearTime().getTime();
17182 this.cells.each(function(c){
17183 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17185 if(c.dateValue > e){
17188 if(c.dateValue < s){
17197 // findBestRow: function(cells)
17201 // for (var i =0 ; i < cells.length;i++) {
17202 // ret = Math.max(cells[i].rows || 0,ret);
17209 addItem : function(ev)
17211 // look for vertical location slot in
17212 var cells = this.findCells(ev);
17214 // ev.row = this.findBestRow(cells);
17216 // work out the location.
17220 for(var i =0; i < cells.length; i++) {
17222 cells[i].row = cells[0].row;
17225 cells[i].row = cells[i].row + 1;
17235 if (crow.start.getY() == cells[i].getY()) {
17237 crow.end = cells[i];
17254 cells[0].events.push(ev);
17256 this.calevents.push(ev);
17259 clearEvents: function() {
17261 if(!this.calevents){
17265 Roo.each(this.cells.elements, function(c){
17271 Roo.each(this.calevents, function(e) {
17272 Roo.each(e.els, function(el) {
17273 el.un('mouseenter' ,this.onEventEnter, this);
17274 el.un('mouseleave' ,this.onEventLeave, this);
17279 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17285 renderEvents: function()
17289 this.cells.each(function(c) {
17298 if(c.row != c.events.length){
17299 r = 4 - (4 - (c.row - c.events.length));
17302 c.events = ev.slice(0, r);
17303 c.more = ev.slice(r);
17305 if(c.more.length && c.more.length == 1){
17306 c.events.push(c.more.pop());
17309 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17313 this.cells.each(function(c) {
17315 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17318 for (var e = 0; e < c.events.length; e++){
17319 var ev = c.events[e];
17320 var rows = ev.rows;
17322 for(var i = 0; i < rows.length; i++) {
17324 // how many rows should it span..
17327 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17328 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17330 unselectable : "on",
17333 cls: 'fc-event-inner',
17337 // cls: 'fc-event-time',
17338 // html : cells.length > 1 ? '' : ev.time
17342 cls: 'fc-event-title',
17343 html : String.format('{0}', ev.title)
17350 cls: 'ui-resizable-handle ui-resizable-e',
17351 html : '  '
17358 cfg.cls += ' fc-event-start';
17360 if ((i+1) == rows.length) {
17361 cfg.cls += ' fc-event-end';
17364 var ctr = _this.el.select('.fc-event-container',true).first();
17365 var cg = ctr.createChild(cfg);
17367 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17368 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17370 var r = (c.more.length) ? 1 : 0;
17371 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17372 cg.setWidth(ebox.right - sbox.x -2);
17374 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17375 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17376 cg.on('click', _this.onEventClick, _this, ev);
17387 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17388 style : 'position: absolute',
17389 unselectable : "on",
17392 cls: 'fc-event-inner',
17396 cls: 'fc-event-title',
17404 cls: 'ui-resizable-handle ui-resizable-e',
17405 html : '  '
17411 var ctr = _this.el.select('.fc-event-container',true).first();
17412 var cg = ctr.createChild(cfg);
17414 var sbox = c.select('.fc-day-content',true).first().getBox();
17415 var ebox = c.select('.fc-day-content',true).first().getBox();
17417 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17418 cg.setWidth(ebox.right - sbox.x -2);
17420 cg.on('click', _this.onMoreEventClick, _this, c.more);
17430 onEventEnter: function (e, el,event,d) {
17431 this.fireEvent('evententer', this, el, event);
17434 onEventLeave: function (e, el,event,d) {
17435 this.fireEvent('eventleave', this, el, event);
17438 onEventClick: function (e, el,event,d) {
17439 this.fireEvent('eventclick', this, el, event);
17442 onMonthChange: function () {
17446 onMoreEventClick: function(e, el, more)
17450 this.calpopover.placement = 'right';
17451 this.calpopover.setTitle('More');
17453 this.calpopover.setContent('');
17455 var ctr = this.calpopover.el.select('.popover-content', true).first();
17457 Roo.each(more, function(m){
17459 cls : 'fc-event-hori fc-event-draggable',
17462 var cg = ctr.createChild(cfg);
17464 cg.on('click', _this.onEventClick, _this, m);
17467 this.calpopover.show(el);
17472 onLoad: function ()
17474 this.calevents = [];
17477 if(this.store.getCount() > 0){
17478 this.store.data.each(function(d){
17481 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17482 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17483 time : d.data.start_time,
17484 title : d.data.title,
17485 description : d.data.description,
17486 venue : d.data.venue
17491 this.renderEvents();
17493 if(this.calevents.length && this.loadMask){
17494 this.maskEl.hide();
17498 onBeforeLoad: function()
17500 this.clearEvents();
17502 this.maskEl.show();
17516 * @class Roo.bootstrap.Popover
17517 * @extends Roo.bootstrap.Component
17518 * Bootstrap Popover class
17519 * @cfg {String} html contents of the popover (or false to use children..)
17520 * @cfg {String} title of popover (or false to hide)
17521 * @cfg {String} placement how it is placed
17522 * @cfg {String} trigger click || hover (or false to trigger manually)
17523 * @cfg {String} over what (parent or false to trigger manually.)
17524 * @cfg {Number} delay - delay before showing
17527 * Create a new Popover
17528 * @param {Object} config The config object
17531 Roo.bootstrap.Popover = function(config){
17532 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17538 * After the popover show
17540 * @param {Roo.bootstrap.Popover} this
17545 * After the popover hide
17547 * @param {Roo.bootstrap.Popover} this
17553 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17555 title: 'Fill in a title',
17558 placement : 'right',
17559 trigger : 'hover', // hover
17565 can_build_overlaid : false,
17567 getChildContainer : function()
17569 return this.el.select('.popover-content',true).first();
17572 getAutoCreate : function(){
17575 cls : 'popover roo-dynamic',
17576 style: 'display:block',
17582 cls : 'popover-inner',
17586 cls: 'popover-title',
17590 cls : 'popover-content',
17601 setTitle: function(str)
17604 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17606 setContent: function(str)
17609 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17611 // as it get's added to the bottom of the page.
17612 onRender : function(ct, position)
17614 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17616 var cfg = Roo.apply({}, this.getAutoCreate());
17620 cfg.cls += ' ' + this.cls;
17623 cfg.style = this.style;
17625 //Roo.log("adding to ");
17626 this.el = Roo.get(document.body).createChild(cfg, position);
17627 // Roo.log(this.el);
17632 initEvents : function()
17634 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17635 this.el.enableDisplayMode('block');
17637 if (this.over === false) {
17640 if (this.triggers === false) {
17643 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17644 var triggers = this.trigger ? this.trigger.split(' ') : [];
17645 Roo.each(triggers, function(trigger) {
17647 if (trigger == 'click') {
17648 on_el.on('click', this.toggle, this);
17649 } else if (trigger != 'manual') {
17650 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17651 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17653 on_el.on(eventIn ,this.enter, this);
17654 on_el.on(eventOut, this.leave, this);
17665 toggle : function () {
17666 this.hoverState == 'in' ? this.leave() : this.enter();
17669 enter : function () {
17671 clearTimeout(this.timeout);
17673 this.hoverState = 'in';
17675 if (!this.delay || !this.delay.show) {
17680 this.timeout = setTimeout(function () {
17681 if (_t.hoverState == 'in') {
17684 }, this.delay.show)
17687 leave : function() {
17688 clearTimeout(this.timeout);
17690 this.hoverState = 'out';
17692 if (!this.delay || !this.delay.hide) {
17697 this.timeout = setTimeout(function () {
17698 if (_t.hoverState == 'out') {
17701 }, this.delay.hide)
17704 show : function (on_el)
17707 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17711 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17712 if (this.html !== false) {
17713 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17715 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17716 if (!this.title.length) {
17717 this.el.select('.popover-title',true).hide();
17720 var placement = typeof this.placement == 'function' ?
17721 this.placement.call(this, this.el, on_el) :
17724 var autoToken = /\s?auto?\s?/i;
17725 var autoPlace = autoToken.test(placement);
17727 placement = placement.replace(autoToken, '') || 'top';
17731 //this.el.setXY([0,0]);
17733 this.el.dom.style.display='block';
17734 this.el.addClass(placement);
17736 //this.el.appendTo(on_el);
17738 var p = this.getPosition();
17739 var box = this.el.getBox();
17744 var align = Roo.bootstrap.Popover.alignment[placement];
17747 this.el.alignTo(on_el, align[0],align[1]);
17748 //var arrow = this.el.select('.arrow',true).first();
17749 //arrow.set(align[2],
17751 this.el.addClass('in');
17754 if (this.el.hasClass('fade')) {
17758 this.hoverState = 'in';
17760 this.fireEvent('show', this);
17765 this.el.setXY([0,0]);
17766 this.el.removeClass('in');
17768 this.hoverState = null;
17770 this.fireEvent('hide', this);
17775 Roo.bootstrap.Popover.alignment = {
17776 'left' : ['r-l', [-10,0], 'right'],
17777 'right' : ['l-r', [10,0], 'left'],
17778 'bottom' : ['t-b', [0,10], 'top'],
17779 'top' : [ 'b-t', [0,-10], 'bottom']
17790 * @class Roo.bootstrap.Progress
17791 * @extends Roo.bootstrap.Component
17792 * Bootstrap Progress class
17793 * @cfg {Boolean} striped striped of the progress bar
17794 * @cfg {Boolean} active animated of the progress bar
17798 * Create a new Progress
17799 * @param {Object} config The config object
17802 Roo.bootstrap.Progress = function(config){
17803 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17806 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17811 getAutoCreate : function(){
17819 cfg.cls += ' progress-striped';
17823 cfg.cls += ' active';
17842 * @class Roo.bootstrap.ProgressBar
17843 * @extends Roo.bootstrap.Component
17844 * Bootstrap ProgressBar class
17845 * @cfg {Number} aria_valuenow aria-value now
17846 * @cfg {Number} aria_valuemin aria-value min
17847 * @cfg {Number} aria_valuemax aria-value max
17848 * @cfg {String} label label for the progress bar
17849 * @cfg {String} panel (success | info | warning | danger )
17850 * @cfg {String} role role of the progress bar
17851 * @cfg {String} sr_only text
17855 * Create a new ProgressBar
17856 * @param {Object} config The config object
17859 Roo.bootstrap.ProgressBar = function(config){
17860 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17863 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17867 aria_valuemax : 100,
17873 getAutoCreate : function()
17878 cls: 'progress-bar',
17879 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17891 cfg.role = this.role;
17894 if(this.aria_valuenow){
17895 cfg['aria-valuenow'] = this.aria_valuenow;
17898 if(this.aria_valuemin){
17899 cfg['aria-valuemin'] = this.aria_valuemin;
17902 if(this.aria_valuemax){
17903 cfg['aria-valuemax'] = this.aria_valuemax;
17906 if(this.label && !this.sr_only){
17907 cfg.html = this.label;
17911 cfg.cls += ' progress-bar-' + this.panel;
17917 update : function(aria_valuenow)
17919 this.aria_valuenow = aria_valuenow;
17921 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17936 * @class Roo.bootstrap.TabGroup
17937 * @extends Roo.bootstrap.Column
17938 * Bootstrap Column class
17939 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17940 * @cfg {Boolean} carousel true to make the group behave like a carousel
17941 * @cfg {Boolean} bullets show bullets for the panels
17942 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17943 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17944 * @cfg {Boolean} showarrow (true|false) show arrow default true
17947 * Create a new TabGroup
17948 * @param {Object} config The config object
17951 Roo.bootstrap.TabGroup = function(config){
17952 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17954 this.navId = Roo.id();
17957 Roo.bootstrap.TabGroup.register(this);
17961 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17964 transition : false,
17969 slideOnTouch : false,
17972 getAutoCreate : function()
17974 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17976 cfg.cls += ' tab-content';
17978 if (this.carousel) {
17979 cfg.cls += ' carousel slide';
17982 cls : 'carousel-inner',
17986 if(this.bullets && !Roo.isTouch){
17989 cls : 'carousel-bullets',
17993 if(this.bullets_cls){
17994 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18001 cfg.cn[0].cn.push(bullets);
18004 if(this.showarrow){
18005 cfg.cn[0].cn.push({
18007 class : 'carousel-arrow',
18011 class : 'carousel-prev',
18015 class : 'fa fa-chevron-left'
18021 class : 'carousel-next',
18025 class : 'fa fa-chevron-right'
18038 initEvents: function()
18040 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18041 // this.el.on("touchstart", this.onTouchStart, this);
18044 if(this.autoslide){
18047 this.slideFn = window.setInterval(function() {
18048 _this.showPanelNext();
18052 if(this.showarrow){
18053 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18054 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18060 // onTouchStart : function(e, el, o)
18062 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18066 // this.showPanelNext();
18070 getChildContainer : function()
18072 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18076 * register a Navigation item
18077 * @param {Roo.bootstrap.NavItem} the navitem to add
18079 register : function(item)
18081 this.tabs.push( item);
18082 item.navId = this.navId; // not really needed..
18087 getActivePanel : function()
18090 Roo.each(this.tabs, function(t) {
18100 getPanelByName : function(n)
18103 Roo.each(this.tabs, function(t) {
18104 if (t.tabId == n) {
18112 indexOfPanel : function(p)
18115 Roo.each(this.tabs, function(t,i) {
18116 if (t.tabId == p.tabId) {
18125 * show a specific panel
18126 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18127 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18129 showPanel : function (pan)
18131 if(this.transition || typeof(pan) == 'undefined'){
18132 Roo.log("waiting for the transitionend");
18136 if (typeof(pan) == 'number') {
18137 pan = this.tabs[pan];
18140 if (typeof(pan) == 'string') {
18141 pan = this.getPanelByName(pan);
18144 var cur = this.getActivePanel();
18147 Roo.log('pan or acitve pan is undefined');
18151 if (pan.tabId == this.getActivePanel().tabId) {
18155 if (false === cur.fireEvent('beforedeactivate')) {
18159 if(this.bullets > 0 && !Roo.isTouch){
18160 this.setActiveBullet(this.indexOfPanel(pan));
18163 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18165 this.transition = true;
18166 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18167 var lr = dir == 'next' ? 'left' : 'right';
18168 pan.el.addClass(dir); // or prev
18169 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18170 cur.el.addClass(lr); // or right
18171 pan.el.addClass(lr);
18174 cur.el.on('transitionend', function() {
18175 Roo.log("trans end?");
18177 pan.el.removeClass([lr,dir]);
18178 pan.setActive(true);
18180 cur.el.removeClass([lr]);
18181 cur.setActive(false);
18183 _this.transition = false;
18185 }, this, { single: true } );
18190 cur.setActive(false);
18191 pan.setActive(true);
18196 showPanelNext : function()
18198 var i = this.indexOfPanel(this.getActivePanel());
18200 if (i >= this.tabs.length - 1 && !this.autoslide) {
18204 if (i >= this.tabs.length - 1 && this.autoslide) {
18208 this.showPanel(this.tabs[i+1]);
18211 showPanelPrev : function()
18213 var i = this.indexOfPanel(this.getActivePanel());
18215 if (i < 1 && !this.autoslide) {
18219 if (i < 1 && this.autoslide) {
18220 i = this.tabs.length;
18223 this.showPanel(this.tabs[i-1]);
18227 addBullet: function()
18229 if(!this.bullets || Roo.isTouch){
18232 var ctr = this.el.select('.carousel-bullets',true).first();
18233 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18234 var bullet = ctr.createChild({
18235 cls : 'bullet bullet-' + i
18236 },ctr.dom.lastChild);
18241 bullet.on('click', (function(e, el, o, ii, t){
18243 e.preventDefault();
18245 this.showPanel(ii);
18247 if(this.autoslide && this.slideFn){
18248 clearInterval(this.slideFn);
18249 this.slideFn = window.setInterval(function() {
18250 _this.showPanelNext();
18254 }).createDelegate(this, [i, bullet], true));
18259 setActiveBullet : function(i)
18265 Roo.each(this.el.select('.bullet', true).elements, function(el){
18266 el.removeClass('selected');
18269 var bullet = this.el.select('.bullet-' + i, true).first();
18275 bullet.addClass('selected');
18286 Roo.apply(Roo.bootstrap.TabGroup, {
18290 * register a Navigation Group
18291 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18293 register : function(navgrp)
18295 this.groups[navgrp.navId] = navgrp;
18299 * fetch a Navigation Group based on the navigation ID
18300 * if one does not exist , it will get created.
18301 * @param {string} the navgroup to add
18302 * @returns {Roo.bootstrap.NavGroup} the navgroup
18304 get: function(navId) {
18305 if (typeof(this.groups[navId]) == 'undefined') {
18306 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18308 return this.groups[navId] ;
18323 * @class Roo.bootstrap.TabPanel
18324 * @extends Roo.bootstrap.Component
18325 * Bootstrap TabPanel class
18326 * @cfg {Boolean} active panel active
18327 * @cfg {String} html panel content
18328 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18329 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18330 * @cfg {String} href click to link..
18334 * Create a new TabPanel
18335 * @param {Object} config The config object
18338 Roo.bootstrap.TabPanel = function(config){
18339 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18343 * Fires when the active status changes
18344 * @param {Roo.bootstrap.TabPanel} this
18345 * @param {Boolean} state the new state
18350 * @event beforedeactivate
18351 * Fires before a tab is de-activated - can be used to do validation on a form.
18352 * @param {Roo.bootstrap.TabPanel} this
18353 * @return {Boolean} false if there is an error
18356 'beforedeactivate': true
18359 this.tabId = this.tabId || Roo.id();
18363 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18371 getAutoCreate : function(){
18374 // item is needed for carousel - not sure if it has any effect otherwise
18375 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18376 html: this.html || ''
18380 cfg.cls += ' active';
18384 cfg.tabId = this.tabId;
18391 initEvents: function()
18393 var p = this.parent();
18395 this.navId = this.navId || p.navId;
18397 if (typeof(this.navId) != 'undefined') {
18398 // not really needed.. but just in case.. parent should be a NavGroup.
18399 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18403 var i = tg.tabs.length - 1;
18405 if(this.active && tg.bullets > 0 && i < tg.bullets){
18406 tg.setActiveBullet(i);
18410 this.el.on('click', this.onClick, this);
18413 this.el.on("touchstart", this.onTouchStart, this);
18414 this.el.on("touchmove", this.onTouchMove, this);
18415 this.el.on("touchend", this.onTouchEnd, this);
18420 onRender : function(ct, position)
18422 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18425 setActive : function(state)
18427 Roo.log("panel - set active " + this.tabId + "=" + state);
18429 this.active = state;
18431 this.el.removeClass('active');
18433 } else if (!this.el.hasClass('active')) {
18434 this.el.addClass('active');
18437 this.fireEvent('changed', this, state);
18440 onClick : function(e)
18442 e.preventDefault();
18444 if(!this.href.length){
18448 window.location.href = this.href;
18457 onTouchStart : function(e)
18459 this.swiping = false;
18461 this.startX = e.browserEvent.touches[0].clientX;
18462 this.startY = e.browserEvent.touches[0].clientY;
18465 onTouchMove : function(e)
18467 this.swiping = true;
18469 this.endX = e.browserEvent.touches[0].clientX;
18470 this.endY = e.browserEvent.touches[0].clientY;
18473 onTouchEnd : function(e)
18480 var tabGroup = this.parent();
18482 if(this.endX > this.startX){ // swiping right
18483 tabGroup.showPanelPrev();
18487 if(this.startX > this.endX){ // swiping left
18488 tabGroup.showPanelNext();
18507 * @class Roo.bootstrap.DateField
18508 * @extends Roo.bootstrap.Input
18509 * Bootstrap DateField class
18510 * @cfg {Number} weekStart default 0
18511 * @cfg {String} viewMode default empty, (months|years)
18512 * @cfg {String} minViewMode default empty, (months|years)
18513 * @cfg {Number} startDate default -Infinity
18514 * @cfg {Number} endDate default Infinity
18515 * @cfg {Boolean} todayHighlight default false
18516 * @cfg {Boolean} todayBtn default false
18517 * @cfg {Boolean} calendarWeeks default false
18518 * @cfg {Object} daysOfWeekDisabled default empty
18519 * @cfg {Boolean} singleMode default false (true | false)
18521 * @cfg {Boolean} keyboardNavigation default true
18522 * @cfg {String} language default en
18525 * Create a new DateField
18526 * @param {Object} config The config object
18529 Roo.bootstrap.DateField = function(config){
18530 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18534 * Fires when this field show.
18535 * @param {Roo.bootstrap.DateField} this
18536 * @param {Mixed} date The date value
18541 * Fires when this field hide.
18542 * @param {Roo.bootstrap.DateField} this
18543 * @param {Mixed} date The date value
18548 * Fires when select a date.
18549 * @param {Roo.bootstrap.DateField} this
18550 * @param {Mixed} date The date value
18554 * @event beforeselect
18555 * Fires when before select a date.
18556 * @param {Roo.bootstrap.DateField} this
18557 * @param {Mixed} date The date value
18559 beforeselect : true
18563 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18566 * @cfg {String} format
18567 * The default date format string which can be overriden for localization support. The format must be
18568 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18572 * @cfg {String} altFormats
18573 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18574 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18576 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18584 todayHighlight : false,
18590 keyboardNavigation: true,
18592 calendarWeeks: false,
18594 startDate: -Infinity,
18598 daysOfWeekDisabled: [],
18602 singleMode : false,
18604 UTCDate: function()
18606 return new Date(Date.UTC.apply(Date, arguments));
18609 UTCToday: function()
18611 var today = new Date();
18612 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18615 getDate: function() {
18616 var d = this.getUTCDate();
18617 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18620 getUTCDate: function() {
18624 setDate: function(d) {
18625 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18628 setUTCDate: function(d) {
18630 this.setValue(this.formatDate(this.date));
18633 onRender: function(ct, position)
18636 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18638 this.language = this.language || 'en';
18639 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18640 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18642 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18643 this.format = this.format || 'm/d/y';
18644 this.isInline = false;
18645 this.isInput = true;
18646 this.component = this.el.select('.add-on', true).first() || false;
18647 this.component = (this.component && this.component.length === 0) ? false : this.component;
18648 this.hasInput = this.component && this.inputEl().length;
18650 if (typeof(this.minViewMode === 'string')) {
18651 switch (this.minViewMode) {
18653 this.minViewMode = 1;
18656 this.minViewMode = 2;
18659 this.minViewMode = 0;
18664 if (typeof(this.viewMode === 'string')) {
18665 switch (this.viewMode) {
18678 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18680 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18682 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18684 this.picker().on('mousedown', this.onMousedown, this);
18685 this.picker().on('click', this.onClick, this);
18687 this.picker().addClass('datepicker-dropdown');
18689 this.startViewMode = this.viewMode;
18691 if(this.singleMode){
18692 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18693 v.setVisibilityMode(Roo.Element.DISPLAY);
18697 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18698 v.setStyle('width', '189px');
18702 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18703 if(!this.calendarWeeks){
18708 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18709 v.attr('colspan', function(i, val){
18710 return parseInt(val) + 1;
18715 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18717 this.setStartDate(this.startDate);
18718 this.setEndDate(this.endDate);
18720 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18727 if(this.isInline) {
18732 picker : function()
18734 return this.pickerEl;
18735 // return this.el.select('.datepicker', true).first();
18738 fillDow: function()
18740 var dowCnt = this.weekStart;
18749 if(this.calendarWeeks){
18757 while (dowCnt < this.weekStart + 7) {
18761 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18765 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18768 fillMonths: function()
18771 var months = this.picker().select('>.datepicker-months td', true).first();
18773 months.dom.innerHTML = '';
18779 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18782 months.createChild(month);
18789 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;
18791 if (this.date < this.startDate) {
18792 this.viewDate = new Date(this.startDate);
18793 } else if (this.date > this.endDate) {
18794 this.viewDate = new Date(this.endDate);
18796 this.viewDate = new Date(this.date);
18804 var d = new Date(this.viewDate),
18805 year = d.getUTCFullYear(),
18806 month = d.getUTCMonth(),
18807 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18808 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18809 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18810 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18811 currentDate = this.date && this.date.valueOf(),
18812 today = this.UTCToday();
18814 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18816 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18818 // this.picker.select('>tfoot th.today').
18819 // .text(dates[this.language].today)
18820 // .toggle(this.todayBtn !== false);
18822 this.updateNavArrows();
18825 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18827 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18829 prevMonth.setUTCDate(day);
18831 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18833 var nextMonth = new Date(prevMonth);
18835 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18837 nextMonth = nextMonth.valueOf();
18839 var fillMonths = false;
18841 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18843 while(prevMonth.valueOf() <= nextMonth) {
18846 if (prevMonth.getUTCDay() === this.weekStart) {
18848 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18856 if(this.calendarWeeks){
18857 // ISO 8601: First week contains first thursday.
18858 // ISO also states week starts on Monday, but we can be more abstract here.
18860 // Start of current week: based on weekstart/current date
18861 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18862 // Thursday of this week
18863 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18864 // First Thursday of year, year from thursday
18865 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18866 // Calendar week: ms between thursdays, div ms per day, div 7 days
18867 calWeek = (th - yth) / 864e5 / 7 + 1;
18869 fillMonths.cn.push({
18877 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18879 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18882 if (this.todayHighlight &&
18883 prevMonth.getUTCFullYear() == today.getFullYear() &&
18884 prevMonth.getUTCMonth() == today.getMonth() &&
18885 prevMonth.getUTCDate() == today.getDate()) {
18886 clsName += ' today';
18889 if (currentDate && prevMonth.valueOf() === currentDate) {
18890 clsName += ' active';
18893 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18894 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18895 clsName += ' disabled';
18898 fillMonths.cn.push({
18900 cls: 'day ' + clsName,
18901 html: prevMonth.getDate()
18904 prevMonth.setDate(prevMonth.getDate()+1);
18907 var currentYear = this.date && this.date.getUTCFullYear();
18908 var currentMonth = this.date && this.date.getUTCMonth();
18910 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18912 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18913 v.removeClass('active');
18915 if(currentYear === year && k === currentMonth){
18916 v.addClass('active');
18919 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18920 v.addClass('disabled');
18926 year = parseInt(year/10, 10) * 10;
18928 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18930 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18933 for (var i = -1; i < 11; i++) {
18934 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18936 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18944 showMode: function(dir)
18947 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18950 Roo.each(this.picker().select('>div',true).elements, function(v){
18951 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18954 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18959 if(this.isInline) {
18963 this.picker().removeClass(['bottom', 'top']);
18965 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18967 * place to the top of element!
18971 this.picker().addClass('top');
18972 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18977 this.picker().addClass('bottom');
18979 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18982 parseDate : function(value)
18984 if(!value || value instanceof Date){
18987 var v = Date.parseDate(value, this.format);
18988 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18989 v = Date.parseDate(value, 'Y-m-d');
18991 if(!v && this.altFormats){
18992 if(!this.altFormatsArray){
18993 this.altFormatsArray = this.altFormats.split("|");
18995 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18996 v = Date.parseDate(value, this.altFormatsArray[i]);
19002 formatDate : function(date, fmt)
19004 return (!date || !(date instanceof Date)) ?
19005 date : date.dateFormat(fmt || this.format);
19008 onFocus : function()
19010 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19014 onBlur : function()
19016 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19018 var d = this.inputEl().getValue();
19025 showPopup : function()
19027 this.picker().show();
19031 this.fireEvent('showpopup', this, this.date);
19034 hidePopup : function()
19036 if(this.isInline) {
19039 this.picker().hide();
19040 this.viewMode = this.startViewMode;
19043 this.fireEvent('hidepopup', this, this.date);
19047 onMousedown: function(e)
19049 e.stopPropagation();
19050 e.preventDefault();
19055 Roo.bootstrap.DateField.superclass.keyup.call(this);
19059 setValue: function(v)
19061 if(this.fireEvent('beforeselect', this, v) !== false){
19062 var d = new Date(this.parseDate(v) ).clearTime();
19064 if(isNaN(d.getTime())){
19065 this.date = this.viewDate = '';
19066 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19070 v = this.formatDate(d);
19072 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19074 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19078 this.fireEvent('select', this, this.date);
19082 getValue: function()
19084 return this.formatDate(this.date);
19087 fireKey: function(e)
19089 if (!this.picker().isVisible()){
19090 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19096 var dateChanged = false,
19098 newDate, newViewDate;
19103 e.preventDefault();
19107 if (!this.keyboardNavigation) {
19110 dir = e.keyCode == 37 ? -1 : 1;
19113 newDate = this.moveYear(this.date, dir);
19114 newViewDate = this.moveYear(this.viewDate, dir);
19115 } else if (e.shiftKey){
19116 newDate = this.moveMonth(this.date, dir);
19117 newViewDate = this.moveMonth(this.viewDate, dir);
19119 newDate = new Date(this.date);
19120 newDate.setUTCDate(this.date.getUTCDate() + dir);
19121 newViewDate = new Date(this.viewDate);
19122 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19124 if (this.dateWithinRange(newDate)){
19125 this.date = newDate;
19126 this.viewDate = newViewDate;
19127 this.setValue(this.formatDate(this.date));
19129 e.preventDefault();
19130 dateChanged = true;
19135 if (!this.keyboardNavigation) {
19138 dir = e.keyCode == 38 ? -1 : 1;
19140 newDate = this.moveYear(this.date, dir);
19141 newViewDate = this.moveYear(this.viewDate, dir);
19142 } else if (e.shiftKey){
19143 newDate = this.moveMonth(this.date, dir);
19144 newViewDate = this.moveMonth(this.viewDate, dir);
19146 newDate = new Date(this.date);
19147 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19148 newViewDate = new Date(this.viewDate);
19149 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19151 if (this.dateWithinRange(newDate)){
19152 this.date = newDate;
19153 this.viewDate = newViewDate;
19154 this.setValue(this.formatDate(this.date));
19156 e.preventDefault();
19157 dateChanged = true;
19161 this.setValue(this.formatDate(this.date));
19163 e.preventDefault();
19166 this.setValue(this.formatDate(this.date));
19180 onClick: function(e)
19182 e.stopPropagation();
19183 e.preventDefault();
19185 var target = e.getTarget();
19187 if(target.nodeName.toLowerCase() === 'i'){
19188 target = Roo.get(target).dom.parentNode;
19191 var nodeName = target.nodeName;
19192 var className = target.className;
19193 var html = target.innerHTML;
19194 //Roo.log(nodeName);
19196 switch(nodeName.toLowerCase()) {
19198 switch(className) {
19204 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19205 switch(this.viewMode){
19207 this.viewDate = this.moveMonth(this.viewDate, dir);
19211 this.viewDate = this.moveYear(this.viewDate, dir);
19217 var date = new Date();
19218 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19220 this.setValue(this.formatDate(this.date));
19227 if (className.indexOf('disabled') < 0) {
19228 this.viewDate.setUTCDate(1);
19229 if (className.indexOf('month') > -1) {
19230 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19232 var year = parseInt(html, 10) || 0;
19233 this.viewDate.setUTCFullYear(year);
19237 if(this.singleMode){
19238 this.setValue(this.formatDate(this.viewDate));
19249 //Roo.log(className);
19250 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19251 var day = parseInt(html, 10) || 1;
19252 var year = this.viewDate.getUTCFullYear(),
19253 month = this.viewDate.getUTCMonth();
19255 if (className.indexOf('old') > -1) {
19262 } else if (className.indexOf('new') > -1) {
19270 //Roo.log([year,month,day]);
19271 this.date = this.UTCDate(year, month, day,0,0,0,0);
19272 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19274 //Roo.log(this.formatDate(this.date));
19275 this.setValue(this.formatDate(this.date));
19282 setStartDate: function(startDate)
19284 this.startDate = startDate || -Infinity;
19285 if (this.startDate !== -Infinity) {
19286 this.startDate = this.parseDate(this.startDate);
19289 this.updateNavArrows();
19292 setEndDate: function(endDate)
19294 this.endDate = endDate || Infinity;
19295 if (this.endDate !== Infinity) {
19296 this.endDate = this.parseDate(this.endDate);
19299 this.updateNavArrows();
19302 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19304 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19305 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19306 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19308 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19309 return parseInt(d, 10);
19312 this.updateNavArrows();
19315 updateNavArrows: function()
19317 if(this.singleMode){
19321 var d = new Date(this.viewDate),
19322 year = d.getUTCFullYear(),
19323 month = d.getUTCMonth();
19325 Roo.each(this.picker().select('.prev', true).elements, function(v){
19327 switch (this.viewMode) {
19330 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19336 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19343 Roo.each(this.picker().select('.next', true).elements, function(v){
19345 switch (this.viewMode) {
19348 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19354 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19362 moveMonth: function(date, dir)
19367 var new_date = new Date(date.valueOf()),
19368 day = new_date.getUTCDate(),
19369 month = new_date.getUTCMonth(),
19370 mag = Math.abs(dir),
19372 dir = dir > 0 ? 1 : -1;
19375 // If going back one month, make sure month is not current month
19376 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19378 return new_date.getUTCMonth() == month;
19380 // If going forward one month, make sure month is as expected
19381 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19383 return new_date.getUTCMonth() != new_month;
19385 new_month = month + dir;
19386 new_date.setUTCMonth(new_month);
19387 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19388 if (new_month < 0 || new_month > 11) {
19389 new_month = (new_month + 12) % 12;
19392 // For magnitudes >1, move one month at a time...
19393 for (var i=0; i<mag; i++) {
19394 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19395 new_date = this.moveMonth(new_date, dir);
19397 // ...then reset the day, keeping it in the new month
19398 new_month = new_date.getUTCMonth();
19399 new_date.setUTCDate(day);
19401 return new_month != new_date.getUTCMonth();
19404 // Common date-resetting loop -- if date is beyond end of month, make it
19407 new_date.setUTCDate(--day);
19408 new_date.setUTCMonth(new_month);
19413 moveYear: function(date, dir)
19415 return this.moveMonth(date, dir*12);
19418 dateWithinRange: function(date)
19420 return date >= this.startDate && date <= this.endDate;
19426 this.picker().remove();
19429 validateValue : function(value)
19431 if(this.getVisibilityEl().hasClass('hidden')){
19435 if(value.length < 1) {
19436 if(this.allowBlank){
19442 if(value.length < this.minLength){
19445 if(value.length > this.maxLength){
19449 var vt = Roo.form.VTypes;
19450 if(!vt[this.vtype](value, this)){
19454 if(typeof this.validator == "function"){
19455 var msg = this.validator(value);
19461 if(this.regex && !this.regex.test(value)){
19465 if(typeof(this.parseDate(value)) == 'undefined'){
19469 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19473 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19483 this.date = this.viewDate = '';
19485 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19490 Roo.apply(Roo.bootstrap.DateField, {
19501 html: '<i class="fa fa-arrow-left"/>'
19511 html: '<i class="fa fa-arrow-right"/>'
19553 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19554 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19555 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19556 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19557 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19570 navFnc: 'FullYear',
19575 navFnc: 'FullYear',
19580 Roo.apply(Roo.bootstrap.DateField, {
19584 cls: 'datepicker dropdown-menu roo-dynamic',
19588 cls: 'datepicker-days',
19592 cls: 'table-condensed',
19594 Roo.bootstrap.DateField.head,
19598 Roo.bootstrap.DateField.footer
19605 cls: 'datepicker-months',
19609 cls: 'table-condensed',
19611 Roo.bootstrap.DateField.head,
19612 Roo.bootstrap.DateField.content,
19613 Roo.bootstrap.DateField.footer
19620 cls: 'datepicker-years',
19624 cls: 'table-condensed',
19626 Roo.bootstrap.DateField.head,
19627 Roo.bootstrap.DateField.content,
19628 Roo.bootstrap.DateField.footer
19647 * @class Roo.bootstrap.TimeField
19648 * @extends Roo.bootstrap.Input
19649 * Bootstrap DateField class
19653 * Create a new TimeField
19654 * @param {Object} config The config object
19657 Roo.bootstrap.TimeField = function(config){
19658 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19662 * Fires when this field show.
19663 * @param {Roo.bootstrap.DateField} thisthis
19664 * @param {Mixed} date The date value
19669 * Fires when this field hide.
19670 * @param {Roo.bootstrap.DateField} this
19671 * @param {Mixed} date The date value
19676 * Fires when select a date.
19677 * @param {Roo.bootstrap.DateField} this
19678 * @param {Mixed} date The date value
19684 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19687 * @cfg {String} format
19688 * The default time format string which can be overriden for localization support. The format must be
19689 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19693 onRender: function(ct, position)
19696 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19698 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19700 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19702 this.pop = this.picker().select('>.datepicker-time',true).first();
19703 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19705 this.picker().on('mousedown', this.onMousedown, this);
19706 this.picker().on('click', this.onClick, this);
19708 this.picker().addClass('datepicker-dropdown');
19713 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19714 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19715 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19716 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19717 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19718 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19722 fireKey: function(e){
19723 if (!this.picker().isVisible()){
19724 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19730 e.preventDefault();
19738 this.onTogglePeriod();
19741 this.onIncrementMinutes();
19744 this.onDecrementMinutes();
19753 onClick: function(e) {
19754 e.stopPropagation();
19755 e.preventDefault();
19758 picker : function()
19760 return this.el.select('.datepicker', true).first();
19763 fillTime: function()
19765 var time = this.pop.select('tbody', true).first();
19767 time.dom.innerHTML = '';
19782 cls: 'hours-up glyphicon glyphicon-chevron-up'
19802 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19823 cls: 'timepicker-hour',
19838 cls: 'timepicker-minute',
19853 cls: 'btn btn-primary period',
19875 cls: 'hours-down glyphicon glyphicon-chevron-down'
19895 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19913 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19920 var hours = this.time.getHours();
19921 var minutes = this.time.getMinutes();
19934 hours = hours - 12;
19938 hours = '0' + hours;
19942 minutes = '0' + minutes;
19945 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19946 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19947 this.pop.select('button', true).first().dom.innerHTML = period;
19953 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19955 var cls = ['bottom'];
19957 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19964 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19969 this.picker().addClass(cls.join('-'));
19973 Roo.each(cls, function(c){
19975 _this.picker().setTop(_this.inputEl().getHeight());
19979 _this.picker().setTop(0 - _this.picker().getHeight());
19984 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19988 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19995 onFocus : function()
19997 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20001 onBlur : function()
20003 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20009 this.picker().show();
20014 this.fireEvent('show', this, this.date);
20019 this.picker().hide();
20022 this.fireEvent('hide', this, this.date);
20025 setTime : function()
20028 this.setValue(this.time.format(this.format));
20030 this.fireEvent('select', this, this.date);
20035 onMousedown: function(e){
20036 e.stopPropagation();
20037 e.preventDefault();
20040 onIncrementHours: function()
20042 Roo.log('onIncrementHours');
20043 this.time = this.time.add(Date.HOUR, 1);
20048 onDecrementHours: function()
20050 Roo.log('onDecrementHours');
20051 this.time = this.time.add(Date.HOUR, -1);
20055 onIncrementMinutes: function()
20057 Roo.log('onIncrementMinutes');
20058 this.time = this.time.add(Date.MINUTE, 1);
20062 onDecrementMinutes: function()
20064 Roo.log('onDecrementMinutes');
20065 this.time = this.time.add(Date.MINUTE, -1);
20069 onTogglePeriod: function()
20071 Roo.log('onTogglePeriod');
20072 this.time = this.time.add(Date.HOUR, 12);
20079 Roo.apply(Roo.bootstrap.TimeField, {
20109 cls: 'btn btn-info ok',
20121 Roo.apply(Roo.bootstrap.TimeField, {
20125 cls: 'datepicker dropdown-menu',
20129 cls: 'datepicker-time',
20133 cls: 'table-condensed',
20135 Roo.bootstrap.TimeField.content,
20136 Roo.bootstrap.TimeField.footer
20155 * @class Roo.bootstrap.MonthField
20156 * @extends Roo.bootstrap.Input
20157 * Bootstrap MonthField class
20159 * @cfg {String} language default en
20162 * Create a new MonthField
20163 * @param {Object} config The config object
20166 Roo.bootstrap.MonthField = function(config){
20167 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20172 * Fires when this field show.
20173 * @param {Roo.bootstrap.MonthField} this
20174 * @param {Mixed} date The date value
20179 * Fires when this field hide.
20180 * @param {Roo.bootstrap.MonthField} this
20181 * @param {Mixed} date The date value
20186 * Fires when select a date.
20187 * @param {Roo.bootstrap.MonthField} this
20188 * @param {String} oldvalue The old value
20189 * @param {String} newvalue The new value
20195 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20197 onRender: function(ct, position)
20200 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20202 this.language = this.language || 'en';
20203 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20204 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20206 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20207 this.isInline = false;
20208 this.isInput = true;
20209 this.component = this.el.select('.add-on', true).first() || false;
20210 this.component = (this.component && this.component.length === 0) ? false : this.component;
20211 this.hasInput = this.component && this.inputEL().length;
20213 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20215 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20217 this.picker().on('mousedown', this.onMousedown, this);
20218 this.picker().on('click', this.onClick, this);
20220 this.picker().addClass('datepicker-dropdown');
20222 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20223 v.setStyle('width', '189px');
20230 if(this.isInline) {
20236 setValue: function(v, suppressEvent)
20238 var o = this.getValue();
20240 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20244 if(suppressEvent !== true){
20245 this.fireEvent('select', this, o, v);
20250 getValue: function()
20255 onClick: function(e)
20257 e.stopPropagation();
20258 e.preventDefault();
20260 var target = e.getTarget();
20262 if(target.nodeName.toLowerCase() === 'i'){
20263 target = Roo.get(target).dom.parentNode;
20266 var nodeName = target.nodeName;
20267 var className = target.className;
20268 var html = target.innerHTML;
20270 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20274 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20276 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20282 picker : function()
20284 return this.pickerEl;
20287 fillMonths: function()
20290 var months = this.picker().select('>.datepicker-months td', true).first();
20292 months.dom.innerHTML = '';
20298 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20301 months.createChild(month);
20310 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20311 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20314 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20315 e.removeClass('active');
20317 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20318 e.addClass('active');
20325 if(this.isInline) {
20329 this.picker().removeClass(['bottom', 'top']);
20331 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20333 * place to the top of element!
20337 this.picker().addClass('top');
20338 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20343 this.picker().addClass('bottom');
20345 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20348 onFocus : function()
20350 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20354 onBlur : function()
20356 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20358 var d = this.inputEl().getValue();
20367 this.picker().show();
20368 this.picker().select('>.datepicker-months', true).first().show();
20372 this.fireEvent('show', this, this.date);
20377 if(this.isInline) {
20380 this.picker().hide();
20381 this.fireEvent('hide', this, this.date);
20385 onMousedown: function(e)
20387 e.stopPropagation();
20388 e.preventDefault();
20393 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20397 fireKey: function(e)
20399 if (!this.picker().isVisible()){
20400 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20411 e.preventDefault();
20415 dir = e.keyCode == 37 ? -1 : 1;
20417 this.vIndex = this.vIndex + dir;
20419 if(this.vIndex < 0){
20423 if(this.vIndex > 11){
20427 if(isNaN(this.vIndex)){
20431 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20437 dir = e.keyCode == 38 ? -1 : 1;
20439 this.vIndex = this.vIndex + dir * 4;
20441 if(this.vIndex < 0){
20445 if(this.vIndex > 11){
20449 if(isNaN(this.vIndex)){
20453 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20458 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20459 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20463 e.preventDefault();
20466 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20467 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20483 this.picker().remove();
20488 Roo.apply(Roo.bootstrap.MonthField, {
20507 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20508 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20513 Roo.apply(Roo.bootstrap.MonthField, {
20517 cls: 'datepicker dropdown-menu roo-dynamic',
20521 cls: 'datepicker-months',
20525 cls: 'table-condensed',
20527 Roo.bootstrap.DateField.content
20547 * @class Roo.bootstrap.CheckBox
20548 * @extends Roo.bootstrap.Input
20549 * Bootstrap CheckBox class
20551 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20552 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20553 * @cfg {String} boxLabel The text that appears beside the checkbox
20554 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20555 * @cfg {Boolean} checked initnal the element
20556 * @cfg {Boolean} inline inline the element (default false)
20557 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20558 * @cfg {String} tooltip label tooltip
20561 * Create a new CheckBox
20562 * @param {Object} config The config object
20565 Roo.bootstrap.CheckBox = function(config){
20566 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20571 * Fires when the element is checked or unchecked.
20572 * @param {Roo.bootstrap.CheckBox} this This input
20573 * @param {Boolean} checked The new checked value
20578 * Fires when the element is click.
20579 * @param {Roo.bootstrap.CheckBox} this This input
20586 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20588 inputType: 'checkbox',
20597 getAutoCreate : function()
20599 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20605 cfg.cls = 'form-group ' + this.inputType; //input-group
20608 cfg.cls += ' ' + this.inputType + '-inline';
20614 type : this.inputType,
20615 value : this.inputValue,
20616 cls : 'roo-' + this.inputType, //'form-box',
20617 placeholder : this.placeholder || ''
20621 if(this.inputType != 'radio'){
20625 cls : 'roo-hidden-value',
20626 value : this.checked ? this.inputValue : this.valueOff
20631 if (this.weight) { // Validity check?
20632 cfg.cls += " " + this.inputType + "-" + this.weight;
20635 if (this.disabled) {
20636 input.disabled=true;
20640 input.checked = this.checked;
20645 input.name = this.name;
20647 if(this.inputType != 'radio'){
20648 hidden.name = this.name;
20649 input.name = '_hidden_' + this.name;
20654 input.cls += ' input-' + this.size;
20659 ['xs','sm','md','lg'].map(function(size){
20660 if (settings[size]) {
20661 cfg.cls += ' col-' + size + '-' + settings[size];
20665 var inputblock = input;
20667 if (this.before || this.after) {
20670 cls : 'input-group',
20675 inputblock.cn.push({
20677 cls : 'input-group-addon',
20682 inputblock.cn.push(input);
20684 if(this.inputType != 'radio'){
20685 inputblock.cn.push(hidden);
20689 inputblock.cn.push({
20691 cls : 'input-group-addon',
20698 if (align ==='left' && this.fieldLabel.length) {
20699 // Roo.log("left and has label");
20704 cls : 'control-label',
20705 html : this.fieldLabel
20715 if(this.labelWidth > 12){
20716 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20719 if(this.labelWidth < 13 && this.labelmd == 0){
20720 this.labelmd = this.labelWidth;
20723 if(this.labellg > 0){
20724 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20725 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20728 if(this.labelmd > 0){
20729 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20730 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20733 if(this.labelsm > 0){
20734 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20735 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20738 if(this.labelxs > 0){
20739 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20740 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20743 } else if ( this.fieldLabel.length) {
20744 // Roo.log(" label");
20748 tag: this.boxLabel ? 'span' : 'label',
20750 cls: 'control-label box-input-label',
20751 //cls : 'input-group-addon',
20752 html : this.fieldLabel
20761 // Roo.log(" no label && no align");
20762 cfg.cn = [ inputblock ] ;
20768 var boxLabelCfg = {
20770 //'for': id, // box label is handled by onclick - so no for...
20772 html: this.boxLabel
20776 boxLabelCfg.tooltip = this.tooltip;
20779 cfg.cn.push(boxLabelCfg);
20782 if(this.inputType != 'radio'){
20783 cfg.cn.push(hidden);
20791 * return the real input element.
20793 inputEl: function ()
20795 return this.el.select('input.roo-' + this.inputType,true).first();
20797 hiddenEl: function ()
20799 return this.el.select('input.roo-hidden-value',true).first();
20802 labelEl: function()
20804 return this.el.select('label.control-label',true).first();
20806 /* depricated... */
20810 return this.labelEl();
20813 boxLabelEl: function()
20815 return this.el.select('label.box-label',true).first();
20818 initEvents : function()
20820 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20822 this.inputEl().on('click', this.onClick, this);
20824 if (this.boxLabel) {
20825 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20828 this.startValue = this.getValue();
20831 Roo.bootstrap.CheckBox.register(this);
20835 onClick : function(e)
20837 if(this.fireEvent('click', this, e) !== false){
20838 this.setChecked(!this.checked);
20843 setChecked : function(state,suppressEvent)
20845 this.startValue = this.getValue();
20847 if(this.inputType == 'radio'){
20849 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20850 e.dom.checked = false;
20853 this.inputEl().dom.checked = true;
20855 this.inputEl().dom.value = this.inputValue;
20857 if(suppressEvent !== true){
20858 this.fireEvent('check', this, true);
20866 this.checked = state;
20868 this.inputEl().dom.checked = state;
20871 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20873 if(suppressEvent !== true){
20874 this.fireEvent('check', this, state);
20880 getValue : function()
20882 if(this.inputType == 'radio'){
20883 return this.getGroupValue();
20886 return this.hiddenEl().dom.value;
20890 getGroupValue : function()
20892 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20896 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20899 setValue : function(v,suppressEvent)
20901 if(this.inputType == 'radio'){
20902 this.setGroupValue(v, suppressEvent);
20906 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20911 setGroupValue : function(v, suppressEvent)
20913 this.startValue = this.getValue();
20915 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20916 e.dom.checked = false;
20918 if(e.dom.value == v){
20919 e.dom.checked = true;
20923 if(suppressEvent !== true){
20924 this.fireEvent('check', this, true);
20932 validate : function()
20934 if(this.getVisibilityEl().hasClass('hidden')){
20940 (this.inputType == 'radio' && this.validateRadio()) ||
20941 (this.inputType == 'checkbox' && this.validateCheckbox())
20947 this.markInvalid();
20951 validateRadio : function()
20953 if(this.getVisibilityEl().hasClass('hidden')){
20957 if(this.allowBlank){
20963 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20964 if(!e.dom.checked){
20976 validateCheckbox : function()
20979 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20980 //return (this.getValue() == this.inputValue) ? true : false;
20983 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20991 for(var i in group){
20992 if(group[i].el.isVisible(true)){
21000 for(var i in group){
21005 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21012 * Mark this field as valid
21014 markValid : function()
21018 this.fireEvent('valid', this);
21020 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21023 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21030 if(this.inputType == 'radio'){
21031 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21032 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21033 e.findParent('.form-group', false, true).addClass(_this.validClass);
21040 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21041 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21045 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21051 for(var i in group){
21052 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21053 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21058 * Mark this field as invalid
21059 * @param {String} msg The validation message
21061 markInvalid : function(msg)
21063 if(this.allowBlank){
21069 this.fireEvent('invalid', this, msg);
21071 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21074 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21078 label.markInvalid();
21081 if(this.inputType == 'radio'){
21082 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21083 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21084 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21091 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21092 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21096 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21102 for(var i in group){
21103 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21104 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21109 clearInvalid : function()
21111 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21113 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21115 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21117 if (label && label.iconEl) {
21118 label.iconEl.removeClass(label.validClass);
21119 label.iconEl.removeClass(label.invalidClass);
21123 disable : function()
21125 if(this.inputType != 'radio'){
21126 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21133 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21134 _this.getActionEl().addClass(this.disabledClass);
21135 e.dom.disabled = true;
21139 this.disabled = true;
21140 this.fireEvent("disable", this);
21144 enable : function()
21146 if(this.inputType != 'radio'){
21147 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21154 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21155 _this.getActionEl().removeClass(this.disabledClass);
21156 e.dom.disabled = false;
21160 this.disabled = false;
21161 this.fireEvent("enable", this);
21165 setBoxLabel : function(v)
21170 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21176 Roo.apply(Roo.bootstrap.CheckBox, {
21181 * register a CheckBox Group
21182 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21184 register : function(checkbox)
21186 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21187 this.groups[checkbox.groupId] = {};
21190 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21194 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21198 * fetch a CheckBox Group based on the group ID
21199 * @param {string} the group ID
21200 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21202 get: function(groupId) {
21203 if (typeof(this.groups[groupId]) == 'undefined') {
21207 return this.groups[groupId] ;
21220 * @class Roo.bootstrap.Radio
21221 * @extends Roo.bootstrap.Component
21222 * Bootstrap Radio class
21223 * @cfg {String} boxLabel - the label associated
21224 * @cfg {String} value - the value of radio
21227 * Create a new Radio
21228 * @param {Object} config The config object
21230 Roo.bootstrap.Radio = function(config){
21231 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21235 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21241 getAutoCreate : function()
21245 cls : 'form-group radio',
21250 html : this.boxLabel
21258 initEvents : function()
21260 this.parent().register(this);
21262 this.el.on('click', this.onClick, this);
21266 onClick : function(e)
21268 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21269 this.setChecked(true);
21273 setChecked : function(state, suppressEvent)
21275 this.parent().setValue(this.value, suppressEvent);
21279 setBoxLabel : function(v)
21284 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21299 * @class Roo.bootstrap.SecurePass
21300 * @extends Roo.bootstrap.Input
21301 * Bootstrap SecurePass class
21305 * Create a new SecurePass
21306 * @param {Object} config The config object
21309 Roo.bootstrap.SecurePass = function (config) {
21310 // these go here, so the translation tool can replace them..
21312 PwdEmpty: "Please type a password, and then retype it to confirm.",
21313 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21314 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21315 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21316 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21317 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21318 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21319 TooWeak: "Your password is Too Weak."
21321 this.meterLabel = "Password strength:";
21322 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21323 this.meterClass = [
21324 "roo-password-meter-tooweak",
21325 "roo-password-meter-weak",
21326 "roo-password-meter-medium",
21327 "roo-password-meter-strong",
21328 "roo-password-meter-grey"
21333 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21336 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21338 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21340 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21341 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21342 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21343 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21344 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21345 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21346 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21356 * @cfg {String/Object} Label for the strength meter (defaults to
21357 * 'Password strength:')
21362 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21363 * ['Weak', 'Medium', 'Strong'])
21366 pwdStrengths: false,
21379 initEvents: function ()
21381 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21383 if (this.el.is('input[type=password]') && Roo.isSafari) {
21384 this.el.on('keydown', this.SafariOnKeyDown, this);
21387 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21390 onRender: function (ct, position)
21392 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21393 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21394 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21396 this.trigger.createChild({
21401 cls: 'roo-password-meter-grey col-xs-12',
21404 //width: this.meterWidth + 'px'
21408 cls: 'roo-password-meter-text'
21414 if (this.hideTrigger) {
21415 this.trigger.setDisplayed(false);
21417 this.setSize(this.width || '', this.height || '');
21420 onDestroy: function ()
21422 if (this.trigger) {
21423 this.trigger.removeAllListeners();
21424 this.trigger.remove();
21427 this.wrap.remove();
21429 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21432 checkStrength: function ()
21434 var pwd = this.inputEl().getValue();
21435 if (pwd == this._lastPwd) {
21440 if (this.ClientSideStrongPassword(pwd)) {
21442 } else if (this.ClientSideMediumPassword(pwd)) {
21444 } else if (this.ClientSideWeakPassword(pwd)) {
21450 Roo.log('strength1: ' + strength);
21452 //var pm = this.trigger.child('div/div/div').dom;
21453 var pm = this.trigger.child('div/div');
21454 pm.removeClass(this.meterClass);
21455 pm.addClass(this.meterClass[strength]);
21458 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21460 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21462 this._lastPwd = pwd;
21466 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21468 this._lastPwd = '';
21470 var pm = this.trigger.child('div/div');
21471 pm.removeClass(this.meterClass);
21472 pm.addClass('roo-password-meter-grey');
21475 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21478 this.inputEl().dom.type='password';
21481 validateValue: function (value)
21484 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21487 if (value.length == 0) {
21488 if (this.allowBlank) {
21489 this.clearInvalid();
21493 this.markInvalid(this.errors.PwdEmpty);
21494 this.errorMsg = this.errors.PwdEmpty;
21502 if ('[\x21-\x7e]*'.match(value)) {
21503 this.markInvalid(this.errors.PwdBadChar);
21504 this.errorMsg = this.errors.PwdBadChar;
21507 if (value.length < 6) {
21508 this.markInvalid(this.errors.PwdShort);
21509 this.errorMsg = this.errors.PwdShort;
21512 if (value.length > 16) {
21513 this.markInvalid(this.errors.PwdLong);
21514 this.errorMsg = this.errors.PwdLong;
21518 if (this.ClientSideStrongPassword(value)) {
21520 } else if (this.ClientSideMediumPassword(value)) {
21522 } else if (this.ClientSideWeakPassword(value)) {
21529 if (strength < 2) {
21530 //this.markInvalid(this.errors.TooWeak);
21531 this.errorMsg = this.errors.TooWeak;
21536 console.log('strength2: ' + strength);
21538 //var pm = this.trigger.child('div/div/div').dom;
21540 var pm = this.trigger.child('div/div');
21541 pm.removeClass(this.meterClass);
21542 pm.addClass(this.meterClass[strength]);
21544 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21546 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21548 this.errorMsg = '';
21552 CharacterSetChecks: function (type)
21555 this.fResult = false;
21558 isctype: function (character, type)
21561 case this.kCapitalLetter:
21562 if (character >= 'A' && character <= 'Z') {
21567 case this.kSmallLetter:
21568 if (character >= 'a' && character <= 'z') {
21574 if (character >= '0' && character <= '9') {
21579 case this.kPunctuation:
21580 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21591 IsLongEnough: function (pwd, size)
21593 return !(pwd == null || isNaN(size) || pwd.length < size);
21596 SpansEnoughCharacterSets: function (word, nb)
21598 if (!this.IsLongEnough(word, nb))
21603 var characterSetChecks = new Array(
21604 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21605 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21608 for (var index = 0; index < word.length; ++index) {
21609 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21610 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21611 characterSetChecks[nCharSet].fResult = true;
21618 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21619 if (characterSetChecks[nCharSet].fResult) {
21624 if (nCharSets < nb) {
21630 ClientSideStrongPassword: function (pwd)
21632 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21635 ClientSideMediumPassword: function (pwd)
21637 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21640 ClientSideWeakPassword: function (pwd)
21642 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21645 })//<script type="text/javascript">
21648 * Based Ext JS Library 1.1.1
21649 * Copyright(c) 2006-2007, Ext JS, LLC.
21655 * @class Roo.HtmlEditorCore
21656 * @extends Roo.Component
21657 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21659 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21662 Roo.HtmlEditorCore = function(config){
21665 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21670 * @event initialize
21671 * Fires when the editor is fully initialized (including the iframe)
21672 * @param {Roo.HtmlEditorCore} this
21677 * Fires when the editor is first receives the focus. Any insertion must wait
21678 * until after this event.
21679 * @param {Roo.HtmlEditorCore} this
21683 * @event beforesync
21684 * Fires before the textarea is updated with content from the editor iframe. Return false
21685 * to cancel the sync.
21686 * @param {Roo.HtmlEditorCore} this
21687 * @param {String} html
21691 * @event beforepush
21692 * Fires before the iframe editor is updated with content from the textarea. Return false
21693 * to cancel the push.
21694 * @param {Roo.HtmlEditorCore} this
21695 * @param {String} html
21700 * Fires when the textarea is updated with content from the editor iframe.
21701 * @param {Roo.HtmlEditorCore} this
21702 * @param {String} html
21707 * Fires when the iframe editor is updated with content from the textarea.
21708 * @param {Roo.HtmlEditorCore} this
21709 * @param {String} html
21714 * @event editorevent
21715 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21716 * @param {Roo.HtmlEditorCore} this
21722 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21724 // defaults : white / black...
21725 this.applyBlacklists();
21732 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21736 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21742 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21747 * @cfg {Number} height (in pixels)
21751 * @cfg {Number} width (in pixels)
21756 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21759 stylesheets: false,
21764 // private properties
21765 validationEvent : false,
21767 initialized : false,
21769 sourceEditMode : false,
21770 onFocus : Roo.emptyFn,
21772 hideMode:'offsets',
21776 // blacklist + whitelisted elements..
21783 * Protected method that will not generally be called directly. It
21784 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21785 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21787 getDocMarkup : function(){
21791 // inherit styels from page...??
21792 if (this.stylesheets === false) {
21794 Roo.get(document.head).select('style').each(function(node) {
21795 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21798 Roo.get(document.head).select('link').each(function(node) {
21799 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21802 } else if (!this.stylesheets.length) {
21804 st = '<style type="text/css">' +
21805 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21808 st = '<style type="text/css">' +
21813 st += '<style type="text/css">' +
21814 'IMG { cursor: pointer } ' +
21817 var cls = 'roo-htmleditor-body';
21819 if(this.bodyCls.length){
21820 cls += ' ' + this.bodyCls;
21823 return '<html><head>' + st +
21824 //<style type="text/css">' +
21825 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21827 ' </head><body class="' + cls + '"></body></html>';
21831 onRender : function(ct, position)
21834 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21835 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21838 this.el.dom.style.border = '0 none';
21839 this.el.dom.setAttribute('tabIndex', -1);
21840 this.el.addClass('x-hidden hide');
21844 if(Roo.isIE){ // fix IE 1px bogus margin
21845 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21849 this.frameId = Roo.id();
21853 var iframe = this.owner.wrap.createChild({
21855 cls: 'form-control', // bootstrap..
21857 name: this.frameId,
21858 frameBorder : 'no',
21859 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21864 this.iframe = iframe.dom;
21866 this.assignDocWin();
21868 this.doc.designMode = 'on';
21871 this.doc.write(this.getDocMarkup());
21875 var task = { // must defer to wait for browser to be ready
21877 //console.log("run task?" + this.doc.readyState);
21878 this.assignDocWin();
21879 if(this.doc.body || this.doc.readyState == 'complete'){
21881 this.doc.designMode="on";
21885 Roo.TaskMgr.stop(task);
21886 this.initEditor.defer(10, this);
21893 Roo.TaskMgr.start(task);
21898 onResize : function(w, h)
21900 Roo.log('resize: ' +w + ',' + h );
21901 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21905 if(typeof w == 'number'){
21907 this.iframe.style.width = w + 'px';
21909 if(typeof h == 'number'){
21911 this.iframe.style.height = h + 'px';
21913 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21920 * Toggles the editor between standard and source edit mode.
21921 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21923 toggleSourceEdit : function(sourceEditMode){
21925 this.sourceEditMode = sourceEditMode === true;
21927 if(this.sourceEditMode){
21929 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21932 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21933 //this.iframe.className = '';
21936 //this.setSize(this.owner.wrap.getSize());
21937 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21944 * Protected method that will not generally be called directly. If you need/want
21945 * custom HTML cleanup, this is the method you should override.
21946 * @param {String} html The HTML to be cleaned
21947 * return {String} The cleaned HTML
21949 cleanHtml : function(html){
21950 html = String(html);
21951 if(html.length > 5){
21952 if(Roo.isSafari){ // strip safari nonsense
21953 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21956 if(html == ' '){
21963 * HTML Editor -> Textarea
21964 * Protected method that will not generally be called directly. Syncs the contents
21965 * of the editor iframe with the textarea.
21967 syncValue : function(){
21968 if(this.initialized){
21969 var bd = (this.doc.body || this.doc.documentElement);
21970 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21971 var html = bd.innerHTML;
21973 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21974 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21976 html = '<div style="'+m[0]+'">' + html + '</div>';
21979 html = this.cleanHtml(html);
21980 // fix up the special chars.. normaly like back quotes in word...
21981 // however we do not want to do this with chinese..
21982 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21983 var cc = b.charCodeAt();
21985 (cc >= 0x4E00 && cc < 0xA000 ) ||
21986 (cc >= 0x3400 && cc < 0x4E00 ) ||
21987 (cc >= 0xf900 && cc < 0xfb00 )
21993 if(this.owner.fireEvent('beforesync', this, html) !== false){
21994 this.el.dom.value = html;
21995 this.owner.fireEvent('sync', this, html);
22001 * Protected method that will not generally be called directly. Pushes the value of the textarea
22002 * into the iframe editor.
22004 pushValue : function(){
22005 if(this.initialized){
22006 var v = this.el.dom.value.trim();
22008 // if(v.length < 1){
22012 if(this.owner.fireEvent('beforepush', this, v) !== false){
22013 var d = (this.doc.body || this.doc.documentElement);
22015 this.cleanUpPaste();
22016 this.el.dom.value = d.innerHTML;
22017 this.owner.fireEvent('push', this, v);
22023 deferFocus : function(){
22024 this.focus.defer(10, this);
22028 focus : function(){
22029 if(this.win && !this.sourceEditMode){
22036 assignDocWin: function()
22038 var iframe = this.iframe;
22041 this.doc = iframe.contentWindow.document;
22042 this.win = iframe.contentWindow;
22044 // if (!Roo.get(this.frameId)) {
22047 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22048 // this.win = Roo.get(this.frameId).dom.contentWindow;
22050 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22054 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22055 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22060 initEditor : function(){
22061 //console.log("INIT EDITOR");
22062 this.assignDocWin();
22066 this.doc.designMode="on";
22068 this.doc.write(this.getDocMarkup());
22071 var dbody = (this.doc.body || this.doc.documentElement);
22072 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22073 // this copies styles from the containing element into thsi one..
22074 // not sure why we need all of this..
22075 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22077 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22078 //ss['background-attachment'] = 'fixed'; // w3c
22079 dbody.bgProperties = 'fixed'; // ie
22080 //Roo.DomHelper.applyStyles(dbody, ss);
22081 Roo.EventManager.on(this.doc, {
22082 //'mousedown': this.onEditorEvent,
22083 'mouseup': this.onEditorEvent,
22084 'dblclick': this.onEditorEvent,
22085 'click': this.onEditorEvent,
22086 'keyup': this.onEditorEvent,
22091 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22093 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22094 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22096 this.initialized = true;
22098 this.owner.fireEvent('initialize', this);
22103 onDestroy : function(){
22109 //for (var i =0; i < this.toolbars.length;i++) {
22110 // // fixme - ask toolbars for heights?
22111 // this.toolbars[i].onDestroy();
22114 //this.wrap.dom.innerHTML = '';
22115 //this.wrap.remove();
22120 onFirstFocus : function(){
22122 this.assignDocWin();
22125 this.activated = true;
22128 if(Roo.isGecko){ // prevent silly gecko errors
22130 var s = this.win.getSelection();
22131 if(!s.focusNode || s.focusNode.nodeType != 3){
22132 var r = s.getRangeAt(0);
22133 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22138 this.execCmd('useCSS', true);
22139 this.execCmd('styleWithCSS', false);
22142 this.owner.fireEvent('activate', this);
22146 adjustFont: function(btn){
22147 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22148 //if(Roo.isSafari){ // safari
22151 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22152 if(Roo.isSafari){ // safari
22153 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22154 v = (v < 10) ? 10 : v;
22155 v = (v > 48) ? 48 : v;
22156 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22161 v = Math.max(1, v+adjust);
22163 this.execCmd('FontSize', v );
22166 onEditorEvent : function(e)
22168 this.owner.fireEvent('editorevent', this, e);
22169 // this.updateToolbar();
22170 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22173 insertTag : function(tg)
22175 // could be a bit smarter... -> wrap the current selected tRoo..
22176 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22178 range = this.createRange(this.getSelection());
22179 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22180 wrappingNode.appendChild(range.extractContents());
22181 range.insertNode(wrappingNode);
22188 this.execCmd("formatblock", tg);
22192 insertText : function(txt)
22196 var range = this.createRange();
22197 range.deleteContents();
22198 //alert(Sender.getAttribute('label'));
22200 range.insertNode(this.doc.createTextNode(txt));
22206 * Executes a Midas editor command on the editor document and performs necessary focus and
22207 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22208 * @param {String} cmd The Midas command
22209 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22211 relayCmd : function(cmd, value){
22213 this.execCmd(cmd, value);
22214 this.owner.fireEvent('editorevent', this);
22215 //this.updateToolbar();
22216 this.owner.deferFocus();
22220 * Executes a Midas editor command directly on the editor document.
22221 * For visual commands, you should use {@link #relayCmd} instead.
22222 * <b>This should only be called after the editor is initialized.</b>
22223 * @param {String} cmd The Midas command
22224 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22226 execCmd : function(cmd, value){
22227 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22234 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22236 * @param {String} text | dom node..
22238 insertAtCursor : function(text)
22241 if(!this.activated){
22247 var r = this.doc.selection.createRange();
22258 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22262 // from jquery ui (MIT licenced)
22264 var win = this.win;
22266 if (win.getSelection && win.getSelection().getRangeAt) {
22267 range = win.getSelection().getRangeAt(0);
22268 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22269 range.insertNode(node);
22270 } else if (win.document.selection && win.document.selection.createRange) {
22271 // no firefox support
22272 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22273 win.document.selection.createRange().pasteHTML(txt);
22275 // no firefox support
22276 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22277 this.execCmd('InsertHTML', txt);
22286 mozKeyPress : function(e){
22288 var c = e.getCharCode(), cmd;
22291 c = String.fromCharCode(c).toLowerCase();
22305 this.cleanUpPaste.defer(100, this);
22313 e.preventDefault();
22321 fixKeys : function(){ // load time branching for fastest keydown performance
22323 return function(e){
22324 var k = e.getKey(), r;
22327 r = this.doc.selection.createRange();
22330 r.pasteHTML('    ');
22337 r = this.doc.selection.createRange();
22339 var target = r.parentElement();
22340 if(!target || target.tagName.toLowerCase() != 'li'){
22342 r.pasteHTML('<br />');
22348 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22349 this.cleanUpPaste.defer(100, this);
22355 }else if(Roo.isOpera){
22356 return function(e){
22357 var k = e.getKey();
22361 this.execCmd('InsertHTML','    ');
22364 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22365 this.cleanUpPaste.defer(100, this);
22370 }else if(Roo.isSafari){
22371 return function(e){
22372 var k = e.getKey();
22376 this.execCmd('InsertText','\t');
22380 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22381 this.cleanUpPaste.defer(100, this);
22389 getAllAncestors: function()
22391 var p = this.getSelectedNode();
22394 a.push(p); // push blank onto stack..
22395 p = this.getParentElement();
22399 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22403 a.push(this.doc.body);
22407 lastSelNode : false,
22410 getSelection : function()
22412 this.assignDocWin();
22413 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22416 getSelectedNode: function()
22418 // this may only work on Gecko!!!
22420 // should we cache this!!!!
22425 var range = this.createRange(this.getSelection()).cloneRange();
22428 var parent = range.parentElement();
22430 var testRange = range.duplicate();
22431 testRange.moveToElementText(parent);
22432 if (testRange.inRange(range)) {
22435 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22438 parent = parent.parentElement;
22443 // is ancestor a text element.
22444 var ac = range.commonAncestorContainer;
22445 if (ac.nodeType == 3) {
22446 ac = ac.parentNode;
22449 var ar = ac.childNodes;
22452 var other_nodes = [];
22453 var has_other_nodes = false;
22454 for (var i=0;i<ar.length;i++) {
22455 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22458 // fullly contained node.
22460 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22465 // probably selected..
22466 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22467 other_nodes.push(ar[i]);
22471 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22476 has_other_nodes = true;
22478 if (!nodes.length && other_nodes.length) {
22479 nodes= other_nodes;
22481 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22487 createRange: function(sel)
22489 // this has strange effects when using with
22490 // top toolbar - not sure if it's a great idea.
22491 //this.editor.contentWindow.focus();
22492 if (typeof sel != "undefined") {
22494 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22496 return this.doc.createRange();
22499 return this.doc.createRange();
22502 getParentElement: function()
22505 this.assignDocWin();
22506 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22508 var range = this.createRange(sel);
22511 var p = range.commonAncestorContainer;
22512 while (p.nodeType == 3) { // text node
22523 * Range intersection.. the hard stuff...
22527 * [ -- selected range --- ]
22531 * if end is before start or hits it. fail.
22532 * if start is after end or hits it fail.
22534 * if either hits (but other is outside. - then it's not
22540 // @see http://www.thismuchiknow.co.uk/?p=64.
22541 rangeIntersectsNode : function(range, node)
22543 var nodeRange = node.ownerDocument.createRange();
22545 nodeRange.selectNode(node);
22547 nodeRange.selectNodeContents(node);
22550 var rangeStartRange = range.cloneRange();
22551 rangeStartRange.collapse(true);
22553 var rangeEndRange = range.cloneRange();
22554 rangeEndRange.collapse(false);
22556 var nodeStartRange = nodeRange.cloneRange();
22557 nodeStartRange.collapse(true);
22559 var nodeEndRange = nodeRange.cloneRange();
22560 nodeEndRange.collapse(false);
22562 return rangeStartRange.compareBoundaryPoints(
22563 Range.START_TO_START, nodeEndRange) == -1 &&
22564 rangeEndRange.compareBoundaryPoints(
22565 Range.START_TO_START, nodeStartRange) == 1;
22569 rangeCompareNode : function(range, node)
22571 var nodeRange = node.ownerDocument.createRange();
22573 nodeRange.selectNode(node);
22575 nodeRange.selectNodeContents(node);
22579 range.collapse(true);
22581 nodeRange.collapse(true);
22583 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22584 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22586 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22588 var nodeIsBefore = ss == 1;
22589 var nodeIsAfter = ee == -1;
22591 if (nodeIsBefore && nodeIsAfter) {
22594 if (!nodeIsBefore && nodeIsAfter) {
22595 return 1; //right trailed.
22598 if (nodeIsBefore && !nodeIsAfter) {
22599 return 2; // left trailed.
22605 // private? - in a new class?
22606 cleanUpPaste : function()
22608 // cleans up the whole document..
22609 Roo.log('cleanuppaste');
22611 this.cleanUpChildren(this.doc.body);
22612 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22613 if (clean != this.doc.body.innerHTML) {
22614 this.doc.body.innerHTML = clean;
22619 cleanWordChars : function(input) {// change the chars to hex code
22620 var he = Roo.HtmlEditorCore;
22622 var output = input;
22623 Roo.each(he.swapCodes, function(sw) {
22624 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22626 output = output.replace(swapper, sw[1]);
22633 cleanUpChildren : function (n)
22635 if (!n.childNodes.length) {
22638 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22639 this.cleanUpChild(n.childNodes[i]);
22646 cleanUpChild : function (node)
22649 //console.log(node);
22650 if (node.nodeName == "#text") {
22651 // clean up silly Windows -- stuff?
22654 if (node.nodeName == "#comment") {
22655 node.parentNode.removeChild(node);
22656 // clean up silly Windows -- stuff?
22659 var lcname = node.tagName.toLowerCase();
22660 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22661 // whitelist of tags..
22663 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22665 node.parentNode.removeChild(node);
22670 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22672 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22673 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22675 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22676 // remove_keep_children = true;
22679 if (remove_keep_children) {
22680 this.cleanUpChildren(node);
22681 // inserts everything just before this node...
22682 while (node.childNodes.length) {
22683 var cn = node.childNodes[0];
22684 node.removeChild(cn);
22685 node.parentNode.insertBefore(cn, node);
22687 node.parentNode.removeChild(node);
22691 if (!node.attributes || !node.attributes.length) {
22692 this.cleanUpChildren(node);
22696 function cleanAttr(n,v)
22699 if (v.match(/^\./) || v.match(/^\//)) {
22702 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22705 if (v.match(/^#/)) {
22708 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22709 node.removeAttribute(n);
22713 var cwhite = this.cwhite;
22714 var cblack = this.cblack;
22716 function cleanStyle(n,v)
22718 if (v.match(/expression/)) { //XSS?? should we even bother..
22719 node.removeAttribute(n);
22723 var parts = v.split(/;/);
22726 Roo.each(parts, function(p) {
22727 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22731 var l = p.split(':').shift().replace(/\s+/g,'');
22732 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22734 if ( cwhite.length && cblack.indexOf(l) > -1) {
22735 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22736 //node.removeAttribute(n);
22740 // only allow 'c whitelisted system attributes'
22741 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22742 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22743 //node.removeAttribute(n);
22753 if (clean.length) {
22754 node.setAttribute(n, clean.join(';'));
22756 node.removeAttribute(n);
22762 for (var i = node.attributes.length-1; i > -1 ; i--) {
22763 var a = node.attributes[i];
22766 if (a.name.toLowerCase().substr(0,2)=='on') {
22767 node.removeAttribute(a.name);
22770 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22771 node.removeAttribute(a.name);
22774 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22775 cleanAttr(a.name,a.value); // fixme..
22778 if (a.name == 'style') {
22779 cleanStyle(a.name,a.value);
22782 /// clean up MS crap..
22783 // tecnically this should be a list of valid class'es..
22786 if (a.name == 'class') {
22787 if (a.value.match(/^Mso/)) {
22788 node.className = '';
22791 if (a.value.match(/^body$/)) {
22792 node.className = '';
22803 this.cleanUpChildren(node);
22809 * Clean up MS wordisms...
22811 cleanWord : function(node)
22816 this.cleanWord(this.doc.body);
22819 if (node.nodeName == "#text") {
22820 // clean up silly Windows -- stuff?
22823 if (node.nodeName == "#comment") {
22824 node.parentNode.removeChild(node);
22825 // clean up silly Windows -- stuff?
22829 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22830 node.parentNode.removeChild(node);
22834 // remove - but keep children..
22835 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22836 while (node.childNodes.length) {
22837 var cn = node.childNodes[0];
22838 node.removeChild(cn);
22839 node.parentNode.insertBefore(cn, node);
22841 node.parentNode.removeChild(node);
22842 this.iterateChildren(node, this.cleanWord);
22846 if (node.className.length) {
22848 var cn = node.className.split(/\W+/);
22850 Roo.each(cn, function(cls) {
22851 if (cls.match(/Mso[a-zA-Z]+/)) {
22856 node.className = cna.length ? cna.join(' ') : '';
22858 node.removeAttribute("class");
22862 if (node.hasAttribute("lang")) {
22863 node.removeAttribute("lang");
22866 if (node.hasAttribute("style")) {
22868 var styles = node.getAttribute("style").split(";");
22870 Roo.each(styles, function(s) {
22871 if (!s.match(/:/)) {
22874 var kv = s.split(":");
22875 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22878 // what ever is left... we allow.
22881 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22882 if (!nstyle.length) {
22883 node.removeAttribute('style');
22886 this.iterateChildren(node, this.cleanWord);
22892 * iterateChildren of a Node, calling fn each time, using this as the scole..
22893 * @param {DomNode} node node to iterate children of.
22894 * @param {Function} fn method of this class to call on each item.
22896 iterateChildren : function(node, fn)
22898 if (!node.childNodes.length) {
22901 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22902 fn.call(this, node.childNodes[i])
22908 * cleanTableWidths.
22910 * Quite often pasting from word etc.. results in tables with column and widths.
22911 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22914 cleanTableWidths : function(node)
22919 this.cleanTableWidths(this.doc.body);
22924 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22927 Roo.log(node.tagName);
22928 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22929 this.iterateChildren(node, this.cleanTableWidths);
22932 if (node.hasAttribute('width')) {
22933 node.removeAttribute('width');
22937 if (node.hasAttribute("style")) {
22940 var styles = node.getAttribute("style").split(";");
22942 Roo.each(styles, function(s) {
22943 if (!s.match(/:/)) {
22946 var kv = s.split(":");
22947 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22950 // what ever is left... we allow.
22953 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22954 if (!nstyle.length) {
22955 node.removeAttribute('style');
22959 this.iterateChildren(node, this.cleanTableWidths);
22967 domToHTML : function(currentElement, depth, nopadtext) {
22969 depth = depth || 0;
22970 nopadtext = nopadtext || false;
22972 if (!currentElement) {
22973 return this.domToHTML(this.doc.body);
22976 //Roo.log(currentElement);
22978 var allText = false;
22979 var nodeName = currentElement.nodeName;
22980 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22982 if (nodeName == '#text') {
22984 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22989 if (nodeName != 'BODY') {
22992 // Prints the node tagName, such as <A>, <IMG>, etc
22995 for(i = 0; i < currentElement.attributes.length;i++) {
22997 var aname = currentElement.attributes.item(i).name;
22998 if (!currentElement.attributes.item(i).value.length) {
23001 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23004 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23013 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23016 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23021 // Traverse the tree
23023 var currentElementChild = currentElement.childNodes.item(i);
23024 var allText = true;
23025 var innerHTML = '';
23027 while (currentElementChild) {
23028 // Formatting code (indent the tree so it looks nice on the screen)
23029 var nopad = nopadtext;
23030 if (lastnode == 'SPAN') {
23034 if (currentElementChild.nodeName == '#text') {
23035 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23036 toadd = nopadtext ? toadd : toadd.trim();
23037 if (!nopad && toadd.length > 80) {
23038 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23040 innerHTML += toadd;
23043 currentElementChild = currentElement.childNodes.item(i);
23049 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23051 // Recursively traverse the tree structure of the child node
23052 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23053 lastnode = currentElementChild.nodeName;
23055 currentElementChild=currentElement.childNodes.item(i);
23061 // The remaining code is mostly for formatting the tree
23062 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23067 ret+= "</"+tagName+">";
23073 applyBlacklists : function()
23075 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23076 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23080 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23081 if (b.indexOf(tag) > -1) {
23084 this.white.push(tag);
23088 Roo.each(w, function(tag) {
23089 if (b.indexOf(tag) > -1) {
23092 if (this.white.indexOf(tag) > -1) {
23095 this.white.push(tag);
23100 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23101 if (w.indexOf(tag) > -1) {
23104 this.black.push(tag);
23108 Roo.each(b, function(tag) {
23109 if (w.indexOf(tag) > -1) {
23112 if (this.black.indexOf(tag) > -1) {
23115 this.black.push(tag);
23120 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23121 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23125 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23126 if (b.indexOf(tag) > -1) {
23129 this.cwhite.push(tag);
23133 Roo.each(w, function(tag) {
23134 if (b.indexOf(tag) > -1) {
23137 if (this.cwhite.indexOf(tag) > -1) {
23140 this.cwhite.push(tag);
23145 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23146 if (w.indexOf(tag) > -1) {
23149 this.cblack.push(tag);
23153 Roo.each(b, function(tag) {
23154 if (w.indexOf(tag) > -1) {
23157 if (this.cblack.indexOf(tag) > -1) {
23160 this.cblack.push(tag);
23165 setStylesheets : function(stylesheets)
23167 if(typeof(stylesheets) == 'string'){
23168 Roo.get(this.iframe.contentDocument.head).createChild({
23170 rel : 'stylesheet',
23179 Roo.each(stylesheets, function(s) {
23184 Roo.get(_this.iframe.contentDocument.head).createChild({
23186 rel : 'stylesheet',
23195 removeStylesheets : function()
23199 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23204 setStyle : function(style)
23206 Roo.get(this.iframe.contentDocument.head).createChild({
23215 // hide stuff that is not compatible
23229 * @event specialkey
23233 * @cfg {String} fieldClass @hide
23236 * @cfg {String} focusClass @hide
23239 * @cfg {String} autoCreate @hide
23242 * @cfg {String} inputType @hide
23245 * @cfg {String} invalidClass @hide
23248 * @cfg {String} invalidText @hide
23251 * @cfg {String} msgFx @hide
23254 * @cfg {String} validateOnBlur @hide
23258 Roo.HtmlEditorCore.white = [
23259 'area', 'br', 'img', 'input', 'hr', 'wbr',
23261 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23262 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23263 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23264 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23265 'table', 'ul', 'xmp',
23267 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23270 'dir', 'menu', 'ol', 'ul', 'dl',
23276 Roo.HtmlEditorCore.black = [
23277 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23279 'base', 'basefont', 'bgsound', 'blink', 'body',
23280 'frame', 'frameset', 'head', 'html', 'ilayer',
23281 'iframe', 'layer', 'link', 'meta', 'object',
23282 'script', 'style' ,'title', 'xml' // clean later..
23284 Roo.HtmlEditorCore.clean = [
23285 'script', 'style', 'title', 'xml'
23287 Roo.HtmlEditorCore.remove = [
23292 Roo.HtmlEditorCore.ablack = [
23296 Roo.HtmlEditorCore.aclean = [
23297 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23301 Roo.HtmlEditorCore.pwhite= [
23302 'http', 'https', 'mailto'
23305 // white listed style attributes.
23306 Roo.HtmlEditorCore.cwhite= [
23307 // 'text-align', /// default is to allow most things..
23313 // black listed style attributes.
23314 Roo.HtmlEditorCore.cblack= [
23315 // 'font-size' -- this can be set by the project
23319 Roo.HtmlEditorCore.swapCodes =[
23338 * @class Roo.bootstrap.HtmlEditor
23339 * @extends Roo.bootstrap.TextArea
23340 * Bootstrap HtmlEditor class
23343 * Create a new HtmlEditor
23344 * @param {Object} config The config object
23347 Roo.bootstrap.HtmlEditor = function(config){
23348 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23349 if (!this.toolbars) {
23350 this.toolbars = [];
23353 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23356 * @event initialize
23357 * Fires when the editor is fully initialized (including the iframe)
23358 * @param {HtmlEditor} this
23363 * Fires when the editor is first receives the focus. Any insertion must wait
23364 * until after this event.
23365 * @param {HtmlEditor} this
23369 * @event beforesync
23370 * Fires before the textarea is updated with content from the editor iframe. Return false
23371 * to cancel the sync.
23372 * @param {HtmlEditor} this
23373 * @param {String} html
23377 * @event beforepush
23378 * Fires before the iframe editor is updated with content from the textarea. Return false
23379 * to cancel the push.
23380 * @param {HtmlEditor} this
23381 * @param {String} html
23386 * Fires when the textarea is updated with content from the editor iframe.
23387 * @param {HtmlEditor} this
23388 * @param {String} html
23393 * Fires when the iframe editor is updated with content from the textarea.
23394 * @param {HtmlEditor} this
23395 * @param {String} html
23399 * @event editmodechange
23400 * Fires when the editor switches edit modes
23401 * @param {HtmlEditor} this
23402 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23404 editmodechange: true,
23406 * @event editorevent
23407 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23408 * @param {HtmlEditor} this
23412 * @event firstfocus
23413 * Fires when on first focus - needed by toolbars..
23414 * @param {HtmlEditor} this
23419 * Auto save the htmlEditor value as a file into Events
23420 * @param {HtmlEditor} this
23424 * @event savedpreview
23425 * preview the saved version of htmlEditor
23426 * @param {HtmlEditor} this
23433 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23437 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23442 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23447 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23452 * @cfg {Number} height (in pixels)
23456 * @cfg {Number} width (in pixels)
23461 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23464 stylesheets: false,
23469 // private properties
23470 validationEvent : false,
23472 initialized : false,
23475 onFocus : Roo.emptyFn,
23477 hideMode:'offsets',
23479 tbContainer : false,
23483 toolbarContainer :function() {
23484 return this.wrap.select('.x-html-editor-tb',true).first();
23488 * Protected method that will not generally be called directly. It
23489 * is called when the editor creates its toolbar. Override this method if you need to
23490 * add custom toolbar buttons.
23491 * @param {HtmlEditor} editor
23493 createToolbar : function(){
23494 Roo.log('renewing');
23495 Roo.log("create toolbars");
23497 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23498 this.toolbars[0].render(this.toolbarContainer());
23502 // if (!editor.toolbars || !editor.toolbars.length) {
23503 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23506 // for (var i =0 ; i < editor.toolbars.length;i++) {
23507 // editor.toolbars[i] = Roo.factory(
23508 // typeof(editor.toolbars[i]) == 'string' ?
23509 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23510 // Roo.bootstrap.HtmlEditor);
23511 // editor.toolbars[i].init(editor);
23517 onRender : function(ct, position)
23519 // Roo.log("Call onRender: " + this.xtype);
23521 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23523 this.wrap = this.inputEl().wrap({
23524 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23527 this.editorcore.onRender(ct, position);
23529 if (this.resizable) {
23530 this.resizeEl = new Roo.Resizable(this.wrap, {
23534 minHeight : this.height,
23535 height: this.height,
23536 handles : this.resizable,
23539 resize : function(r, w, h) {
23540 _t.onResize(w,h); // -something
23546 this.createToolbar(this);
23549 if(!this.width && this.resizable){
23550 this.setSize(this.wrap.getSize());
23552 if (this.resizeEl) {
23553 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23554 // should trigger onReize..
23560 onResize : function(w, h)
23562 Roo.log('resize: ' +w + ',' + h );
23563 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23567 if(this.inputEl() ){
23568 if(typeof w == 'number'){
23569 var aw = w - this.wrap.getFrameWidth('lr');
23570 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23573 if(typeof h == 'number'){
23574 var tbh = -11; // fixme it needs to tool bar size!
23575 for (var i =0; i < this.toolbars.length;i++) {
23576 // fixme - ask toolbars for heights?
23577 tbh += this.toolbars[i].el.getHeight();
23578 //if (this.toolbars[i].footer) {
23579 // tbh += this.toolbars[i].footer.el.getHeight();
23587 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23588 ah -= 5; // knock a few pixes off for look..
23589 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23593 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23594 this.editorcore.onResize(ew,eh);
23599 * Toggles the editor between standard and source edit mode.
23600 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23602 toggleSourceEdit : function(sourceEditMode)
23604 this.editorcore.toggleSourceEdit(sourceEditMode);
23606 if(this.editorcore.sourceEditMode){
23607 Roo.log('editor - showing textarea');
23610 // Roo.log(this.syncValue());
23612 this.inputEl().removeClass(['hide', 'x-hidden']);
23613 this.inputEl().dom.removeAttribute('tabIndex');
23614 this.inputEl().focus();
23616 Roo.log('editor - hiding textarea');
23618 // Roo.log(this.pushValue());
23621 this.inputEl().addClass(['hide', 'x-hidden']);
23622 this.inputEl().dom.setAttribute('tabIndex', -1);
23623 //this.deferFocus();
23626 if(this.resizable){
23627 this.setSize(this.wrap.getSize());
23630 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23633 // private (for BoxComponent)
23634 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23636 // private (for BoxComponent)
23637 getResizeEl : function(){
23641 // private (for BoxComponent)
23642 getPositionEl : function(){
23647 initEvents : function(){
23648 this.originalValue = this.getValue();
23652 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23655 // markInvalid : Roo.emptyFn,
23657 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23660 // clearInvalid : Roo.emptyFn,
23662 setValue : function(v){
23663 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23664 this.editorcore.pushValue();
23669 deferFocus : function(){
23670 this.focus.defer(10, this);
23674 focus : function(){
23675 this.editorcore.focus();
23681 onDestroy : function(){
23687 for (var i =0; i < this.toolbars.length;i++) {
23688 // fixme - ask toolbars for heights?
23689 this.toolbars[i].onDestroy();
23692 this.wrap.dom.innerHTML = '';
23693 this.wrap.remove();
23698 onFirstFocus : function(){
23699 //Roo.log("onFirstFocus");
23700 this.editorcore.onFirstFocus();
23701 for (var i =0; i < this.toolbars.length;i++) {
23702 this.toolbars[i].onFirstFocus();
23708 syncValue : function()
23710 this.editorcore.syncValue();
23713 pushValue : function()
23715 this.editorcore.pushValue();
23719 // hide stuff that is not compatible
23733 * @event specialkey
23737 * @cfg {String} fieldClass @hide
23740 * @cfg {String} focusClass @hide
23743 * @cfg {String} autoCreate @hide
23746 * @cfg {String} inputType @hide
23749 * @cfg {String} invalidClass @hide
23752 * @cfg {String} invalidText @hide
23755 * @cfg {String} msgFx @hide
23758 * @cfg {String} validateOnBlur @hide
23767 Roo.namespace('Roo.bootstrap.htmleditor');
23769 * @class Roo.bootstrap.HtmlEditorToolbar1
23774 new Roo.bootstrap.HtmlEditor({
23777 new Roo.bootstrap.HtmlEditorToolbar1({
23778 disable : { fonts: 1 , format: 1, ..., ... , ...],
23784 * @cfg {Object} disable List of elements to disable..
23785 * @cfg {Array} btns List of additional buttons.
23789 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23792 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23795 Roo.apply(this, config);
23797 // default disabled, based on 'good practice'..
23798 this.disable = this.disable || {};
23799 Roo.applyIf(this.disable, {
23802 specialElements : true
23804 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23806 this.editor = config.editor;
23807 this.editorcore = config.editor.editorcore;
23809 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23811 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23812 // dont call parent... till later.
23814 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23819 editorcore : false,
23824 "h1","h2","h3","h4","h5","h6",
23826 "abbr", "acronym", "address", "cite", "samp", "var",
23830 onRender : function(ct, position)
23832 // Roo.log("Call onRender: " + this.xtype);
23834 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23836 this.el.dom.style.marginBottom = '0';
23838 var editorcore = this.editorcore;
23839 var editor= this.editor;
23842 var btn = function(id,cmd , toggle, handler, html){
23844 var event = toggle ? 'toggle' : 'click';
23849 xns: Roo.bootstrap,
23852 enableToggle:toggle !== false,
23854 pressed : toggle ? false : null,
23857 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23858 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23864 // var cb_box = function...
23869 xns: Roo.bootstrap,
23870 glyphicon : 'font',
23874 xns: Roo.bootstrap,
23878 Roo.each(this.formats, function(f) {
23879 style.menu.items.push({
23881 xns: Roo.bootstrap,
23882 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23887 editorcore.insertTag(this.tagname);
23894 children.push(style);
23896 btn('bold',false,true);
23897 btn('italic',false,true);
23898 btn('align-left', 'justifyleft',true);
23899 btn('align-center', 'justifycenter',true);
23900 btn('align-right' , 'justifyright',true);
23901 btn('link', false, false, function(btn) {
23902 //Roo.log("create link?");
23903 var url = prompt(this.createLinkText, this.defaultLinkValue);
23904 if(url && url != 'http:/'+'/'){
23905 this.editorcore.relayCmd('createlink', url);
23908 btn('list','insertunorderedlist',true);
23909 btn('pencil', false,true, function(btn){
23911 this.toggleSourceEdit(btn.pressed);
23914 if (this.editor.btns.length > 0) {
23915 for (var i = 0; i<this.editor.btns.length; i++) {
23916 children.push(this.editor.btns[i]);
23924 xns: Roo.bootstrap,
23929 xns: Roo.bootstrap,
23934 cog.menu.items.push({
23936 xns: Roo.bootstrap,
23937 html : Clean styles,
23942 editorcore.insertTag(this.tagname);
23951 this.xtype = 'NavSimplebar';
23953 for(var i=0;i< children.length;i++) {
23955 this.buttons.add(this.addxtypeChild(children[i]));
23959 editor.on('editorevent', this.updateToolbar, this);
23961 onBtnClick : function(id)
23963 this.editorcore.relayCmd(id);
23964 this.editorcore.focus();
23968 * Protected method that will not generally be called directly. It triggers
23969 * a toolbar update by reading the markup state of the current selection in the editor.
23971 updateToolbar: function(){
23973 if(!this.editorcore.activated){
23974 this.editor.onFirstFocus(); // is this neeed?
23978 var btns = this.buttons;
23979 var doc = this.editorcore.doc;
23980 btns.get('bold').setActive(doc.queryCommandState('bold'));
23981 btns.get('italic').setActive(doc.queryCommandState('italic'));
23982 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23984 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23985 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23986 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23988 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23989 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23992 var ans = this.editorcore.getAllAncestors();
23993 if (this.formatCombo) {
23996 var store = this.formatCombo.store;
23997 this.formatCombo.setValue("");
23998 for (var i =0; i < ans.length;i++) {
23999 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24001 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24009 // hides menus... - so this cant be on a menu...
24010 Roo.bootstrap.MenuMgr.hideAll();
24012 Roo.bootstrap.MenuMgr.hideAll();
24013 //this.editorsyncValue();
24015 onFirstFocus: function() {
24016 this.buttons.each(function(item){
24020 toggleSourceEdit : function(sourceEditMode){
24023 if(sourceEditMode){
24024 Roo.log("disabling buttons");
24025 this.buttons.each( function(item){
24026 if(item.cmd != 'pencil'){
24032 Roo.log("enabling buttons");
24033 if(this.editorcore.initialized){
24034 this.buttons.each( function(item){
24040 Roo.log("calling toggole on editor");
24041 // tell the editor that it's been pressed..
24042 this.editor.toggleSourceEdit(sourceEditMode);
24052 * @class Roo.bootstrap.Table.AbstractSelectionModel
24053 * @extends Roo.util.Observable
24054 * Abstract base class for grid SelectionModels. It provides the interface that should be
24055 * implemented by descendant classes. This class should not be directly instantiated.
24058 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24059 this.locked = false;
24060 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24064 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24065 /** @ignore Called by the grid automatically. Do not call directly. */
24066 init : function(grid){
24072 * Locks the selections.
24075 this.locked = true;
24079 * Unlocks the selections.
24081 unlock : function(){
24082 this.locked = false;
24086 * Returns true if the selections are locked.
24087 * @return {Boolean}
24089 isLocked : function(){
24090 return this.locked;
24094 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24095 * @class Roo.bootstrap.Table.RowSelectionModel
24096 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24097 * It supports multiple selections and keyboard selection/navigation.
24099 * @param {Object} config
24102 Roo.bootstrap.Table.RowSelectionModel = function(config){
24103 Roo.apply(this, config);
24104 this.selections = new Roo.util.MixedCollection(false, function(o){
24109 this.lastActive = false;
24113 * @event selectionchange
24114 * Fires when the selection changes
24115 * @param {SelectionModel} this
24117 "selectionchange" : true,
24119 * @event afterselectionchange
24120 * Fires after the selection changes (eg. by key press or clicking)
24121 * @param {SelectionModel} this
24123 "afterselectionchange" : true,
24125 * @event beforerowselect
24126 * Fires when a row is selected being selected, return false to cancel.
24127 * @param {SelectionModel} this
24128 * @param {Number} rowIndex The selected index
24129 * @param {Boolean} keepExisting False if other selections will be cleared
24131 "beforerowselect" : true,
24134 * Fires when a row is selected.
24135 * @param {SelectionModel} this
24136 * @param {Number} rowIndex The selected index
24137 * @param {Roo.data.Record} r The record
24139 "rowselect" : true,
24141 * @event rowdeselect
24142 * Fires when a row is deselected.
24143 * @param {SelectionModel} this
24144 * @param {Number} rowIndex The selected index
24146 "rowdeselect" : true
24148 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24149 this.locked = false;
24152 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24154 * @cfg {Boolean} singleSelect
24155 * True to allow selection of only one row at a time (defaults to false)
24157 singleSelect : false,
24160 initEvents : function()
24163 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24164 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24165 //}else{ // allow click to work like normal
24166 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24168 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24169 this.grid.on("rowclick", this.handleMouseDown, this);
24171 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24172 "up" : function(e){
24174 this.selectPrevious(e.shiftKey);
24175 }else if(this.last !== false && this.lastActive !== false){
24176 var last = this.last;
24177 this.selectRange(this.last, this.lastActive-1);
24178 this.grid.getView().focusRow(this.lastActive);
24179 if(last !== false){
24183 this.selectFirstRow();
24185 this.fireEvent("afterselectionchange", this);
24187 "down" : function(e){
24189 this.selectNext(e.shiftKey);
24190 }else if(this.last !== false && this.lastActive !== false){
24191 var last = this.last;
24192 this.selectRange(this.last, this.lastActive+1);
24193 this.grid.getView().focusRow(this.lastActive);
24194 if(last !== false){
24198 this.selectFirstRow();
24200 this.fireEvent("afterselectionchange", this);
24204 this.grid.store.on('load', function(){
24205 this.selections.clear();
24208 var view = this.grid.view;
24209 view.on("refresh", this.onRefresh, this);
24210 view.on("rowupdated", this.onRowUpdated, this);
24211 view.on("rowremoved", this.onRemove, this);
24216 onRefresh : function()
24218 var ds = this.grid.store, i, v = this.grid.view;
24219 var s = this.selections;
24220 s.each(function(r){
24221 if((i = ds.indexOfId(r.id)) != -1){
24230 onRemove : function(v, index, r){
24231 this.selections.remove(r);
24235 onRowUpdated : function(v, index, r){
24236 if(this.isSelected(r)){
24237 v.onRowSelect(index);
24243 * @param {Array} records The records to select
24244 * @param {Boolean} keepExisting (optional) True to keep existing selections
24246 selectRecords : function(records, keepExisting)
24249 this.clearSelections();
24251 var ds = this.grid.store;
24252 for(var i = 0, len = records.length; i < len; i++){
24253 this.selectRow(ds.indexOf(records[i]), true);
24258 * Gets the number of selected rows.
24261 getCount : function(){
24262 return this.selections.length;
24266 * Selects the first row in the grid.
24268 selectFirstRow : function(){
24273 * Select the last row.
24274 * @param {Boolean} keepExisting (optional) True to keep existing selections
24276 selectLastRow : function(keepExisting){
24277 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24278 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24282 * Selects the row immediately following the last selected row.
24283 * @param {Boolean} keepExisting (optional) True to keep existing selections
24285 selectNext : function(keepExisting)
24287 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24288 this.selectRow(this.last+1, keepExisting);
24289 this.grid.getView().focusRow(this.last);
24294 * Selects the row that precedes the last selected row.
24295 * @param {Boolean} keepExisting (optional) True to keep existing selections
24297 selectPrevious : function(keepExisting){
24299 this.selectRow(this.last-1, keepExisting);
24300 this.grid.getView().focusRow(this.last);
24305 * Returns the selected records
24306 * @return {Array} Array of selected records
24308 getSelections : function(){
24309 return [].concat(this.selections.items);
24313 * Returns the first selected record.
24316 getSelected : function(){
24317 return this.selections.itemAt(0);
24322 * Clears all selections.
24324 clearSelections : function(fast)
24330 var ds = this.grid.store;
24331 var s = this.selections;
24332 s.each(function(r){
24333 this.deselectRow(ds.indexOfId(r.id));
24337 this.selections.clear();
24344 * Selects all rows.
24346 selectAll : function(){
24350 this.selections.clear();
24351 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24352 this.selectRow(i, true);
24357 * Returns True if there is a selection.
24358 * @return {Boolean}
24360 hasSelection : function(){
24361 return this.selections.length > 0;
24365 * Returns True if the specified row is selected.
24366 * @param {Number/Record} record The record or index of the record to check
24367 * @return {Boolean}
24369 isSelected : function(index){
24370 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24371 return (r && this.selections.key(r.id) ? true : false);
24375 * Returns True if the specified record id is selected.
24376 * @param {String} id The id of record to check
24377 * @return {Boolean}
24379 isIdSelected : function(id){
24380 return (this.selections.key(id) ? true : false);
24385 handleMouseDBClick : function(e, t){
24389 handleMouseDown : function(e, t)
24391 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24392 if(this.isLocked() || rowIndex < 0 ){
24395 if(e.shiftKey && this.last !== false){
24396 var last = this.last;
24397 this.selectRange(last, rowIndex, e.ctrlKey);
24398 this.last = last; // reset the last
24402 var isSelected = this.isSelected(rowIndex);
24403 //Roo.log("select row:" + rowIndex);
24405 this.deselectRow(rowIndex);
24407 this.selectRow(rowIndex, true);
24411 if(e.button !== 0 && isSelected){
24412 alert('rowIndex 2: ' + rowIndex);
24413 view.focusRow(rowIndex);
24414 }else if(e.ctrlKey && isSelected){
24415 this.deselectRow(rowIndex);
24416 }else if(!isSelected){
24417 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24418 view.focusRow(rowIndex);
24422 this.fireEvent("afterselectionchange", this);
24425 handleDragableRowClick : function(grid, rowIndex, e)
24427 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24428 this.selectRow(rowIndex, false);
24429 grid.view.focusRow(rowIndex);
24430 this.fireEvent("afterselectionchange", this);
24435 * Selects multiple rows.
24436 * @param {Array} rows Array of the indexes of the row to select
24437 * @param {Boolean} keepExisting (optional) True to keep existing selections
24439 selectRows : function(rows, keepExisting){
24441 this.clearSelections();
24443 for(var i = 0, len = rows.length; i < len; i++){
24444 this.selectRow(rows[i], true);
24449 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24450 * @param {Number} startRow The index of the first row in the range
24451 * @param {Number} endRow The index of the last row in the range
24452 * @param {Boolean} keepExisting (optional) True to retain existing selections
24454 selectRange : function(startRow, endRow, keepExisting){
24459 this.clearSelections();
24461 if(startRow <= endRow){
24462 for(var i = startRow; i <= endRow; i++){
24463 this.selectRow(i, true);
24466 for(var i = startRow; i >= endRow; i--){
24467 this.selectRow(i, true);
24473 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24474 * @param {Number} startRow The index of the first row in the range
24475 * @param {Number} endRow The index of the last row in the range
24477 deselectRange : function(startRow, endRow, preventViewNotify){
24481 for(var i = startRow; i <= endRow; i++){
24482 this.deselectRow(i, preventViewNotify);
24488 * @param {Number} row The index of the row to select
24489 * @param {Boolean} keepExisting (optional) True to keep existing selections
24491 selectRow : function(index, keepExisting, preventViewNotify)
24493 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24496 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24497 if(!keepExisting || this.singleSelect){
24498 this.clearSelections();
24501 var r = this.grid.store.getAt(index);
24502 //console.log('selectRow - record id :' + r.id);
24504 this.selections.add(r);
24505 this.last = this.lastActive = index;
24506 if(!preventViewNotify){
24507 var proxy = new Roo.Element(
24508 this.grid.getRowDom(index)
24510 proxy.addClass('bg-info info');
24512 this.fireEvent("rowselect", this, index, r);
24513 this.fireEvent("selectionchange", this);
24519 * @param {Number} row The index of the row to deselect
24521 deselectRow : function(index, preventViewNotify)
24526 if(this.last == index){
24529 if(this.lastActive == index){
24530 this.lastActive = false;
24533 var r = this.grid.store.getAt(index);
24538 this.selections.remove(r);
24539 //.console.log('deselectRow - record id :' + r.id);
24540 if(!preventViewNotify){
24542 var proxy = new Roo.Element(
24543 this.grid.getRowDom(index)
24545 proxy.removeClass('bg-info info');
24547 this.fireEvent("rowdeselect", this, index);
24548 this.fireEvent("selectionchange", this);
24552 restoreLast : function(){
24554 this.last = this._last;
24559 acceptsNav : function(row, col, cm){
24560 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24564 onEditorKey : function(field, e){
24565 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24570 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24572 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24574 }else if(k == e.ENTER && !e.ctrlKey){
24578 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24580 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24582 }else if(k == e.ESC){
24586 g.startEditing(newCell[0], newCell[1]);
24592 * Ext JS Library 1.1.1
24593 * Copyright(c) 2006-2007, Ext JS, LLC.
24595 * Originally Released Under LGPL - original licence link has changed is not relivant.
24598 * <script type="text/javascript">
24602 * @class Roo.bootstrap.PagingToolbar
24603 * @extends Roo.bootstrap.NavSimplebar
24604 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24606 * Create a new PagingToolbar
24607 * @param {Object} config The config object
24608 * @param {Roo.data.Store} store
24610 Roo.bootstrap.PagingToolbar = function(config)
24612 // old args format still supported... - xtype is prefered..
24613 // created from xtype...
24615 this.ds = config.dataSource;
24617 if (config.store && !this.ds) {
24618 this.store= Roo.factory(config.store, Roo.data);
24619 this.ds = this.store;
24620 this.ds.xmodule = this.xmodule || false;
24623 this.toolbarItems = [];
24624 if (config.items) {
24625 this.toolbarItems = config.items;
24628 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24633 this.bind(this.ds);
24636 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24640 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24642 * @cfg {Roo.data.Store} dataSource
24643 * The underlying data store providing the paged data
24646 * @cfg {String/HTMLElement/Element} container
24647 * container The id or element that will contain the toolbar
24650 * @cfg {Boolean} displayInfo
24651 * True to display the displayMsg (defaults to false)
24654 * @cfg {Number} pageSize
24655 * The number of records to display per page (defaults to 20)
24659 * @cfg {String} displayMsg
24660 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24662 displayMsg : 'Displaying {0} - {1} of {2}',
24664 * @cfg {String} emptyMsg
24665 * The message to display when no records are found (defaults to "No data to display")
24667 emptyMsg : 'No data to display',
24669 * Customizable piece of the default paging text (defaults to "Page")
24672 beforePageText : "Page",
24674 * Customizable piece of the default paging text (defaults to "of %0")
24677 afterPageText : "of {0}",
24679 * Customizable piece of the default paging text (defaults to "First Page")
24682 firstText : "First Page",
24684 * Customizable piece of the default paging text (defaults to "Previous Page")
24687 prevText : "Previous Page",
24689 * Customizable piece of the default paging text (defaults to "Next Page")
24692 nextText : "Next Page",
24694 * Customizable piece of the default paging text (defaults to "Last Page")
24697 lastText : "Last Page",
24699 * Customizable piece of the default paging text (defaults to "Refresh")
24702 refreshText : "Refresh",
24706 onRender : function(ct, position)
24708 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24709 this.navgroup.parentId = this.id;
24710 this.navgroup.onRender(this.el, null);
24711 // add the buttons to the navgroup
24713 if(this.displayInfo){
24714 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24715 this.displayEl = this.el.select('.x-paging-info', true).first();
24716 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24717 // this.displayEl = navel.el.select('span',true).first();
24723 Roo.each(_this.buttons, function(e){ // this might need to use render????
24724 Roo.factory(e).render(_this.el);
24728 Roo.each(_this.toolbarItems, function(e) {
24729 _this.navgroup.addItem(e);
24733 this.first = this.navgroup.addItem({
24734 tooltip: this.firstText,
24736 icon : 'fa fa-step-backward',
24738 preventDefault: true,
24739 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24742 this.prev = this.navgroup.addItem({
24743 tooltip: this.prevText,
24745 icon : 'fa fa-backward',
24747 preventDefault: true,
24748 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24750 //this.addSeparator();
24753 var field = this.navgroup.addItem( {
24755 cls : 'x-paging-position',
24757 html : this.beforePageText +
24758 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24759 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24762 this.field = field.el.select('input', true).first();
24763 this.field.on("keydown", this.onPagingKeydown, this);
24764 this.field.on("focus", function(){this.dom.select();});
24767 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24768 //this.field.setHeight(18);
24769 //this.addSeparator();
24770 this.next = this.navgroup.addItem({
24771 tooltip: this.nextText,
24773 html : ' <i class="fa fa-forward">',
24775 preventDefault: true,
24776 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24778 this.last = this.navgroup.addItem({
24779 tooltip: this.lastText,
24780 icon : 'fa fa-step-forward',
24783 preventDefault: true,
24784 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24786 //this.addSeparator();
24787 this.loading = this.navgroup.addItem({
24788 tooltip: this.refreshText,
24789 icon: 'fa fa-refresh',
24790 preventDefault: true,
24791 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24797 updateInfo : function(){
24798 if(this.displayEl){
24799 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24800 var msg = count == 0 ?
24804 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24806 this.displayEl.update(msg);
24811 onLoad : function(ds, r, o)
24813 this.cursor = o.params.start ? o.params.start : 0;
24815 var d = this.getPageData(),
24820 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24821 this.field.dom.value = ap;
24822 this.first.setDisabled(ap == 1);
24823 this.prev.setDisabled(ap == 1);
24824 this.next.setDisabled(ap == ps);
24825 this.last.setDisabled(ap == ps);
24826 this.loading.enable();
24831 getPageData : function(){
24832 var total = this.ds.getTotalCount();
24835 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24836 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24841 onLoadError : function(){
24842 this.loading.enable();
24846 onPagingKeydown : function(e){
24847 var k = e.getKey();
24848 var d = this.getPageData();
24850 var v = this.field.dom.value, pageNum;
24851 if(!v || isNaN(pageNum = parseInt(v, 10))){
24852 this.field.dom.value = d.activePage;
24855 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24856 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24859 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))
24861 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24862 this.field.dom.value = pageNum;
24863 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24866 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24868 var v = this.field.dom.value, pageNum;
24869 var increment = (e.shiftKey) ? 10 : 1;
24870 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24873 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24874 this.field.dom.value = d.activePage;
24877 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24879 this.field.dom.value = parseInt(v, 10) + increment;
24880 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24881 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24888 beforeLoad : function(){
24890 this.loading.disable();
24895 onClick : function(which){
24904 ds.load({params:{start: 0, limit: this.pageSize}});
24907 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24910 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24913 var total = ds.getTotalCount();
24914 var extra = total % this.pageSize;
24915 var lastStart = extra ? (total - extra) : total-this.pageSize;
24916 ds.load({params:{start: lastStart, limit: this.pageSize}});
24919 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24925 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24926 * @param {Roo.data.Store} store The data store to unbind
24928 unbind : function(ds){
24929 ds.un("beforeload", this.beforeLoad, this);
24930 ds.un("load", this.onLoad, this);
24931 ds.un("loadexception", this.onLoadError, this);
24932 ds.un("remove", this.updateInfo, this);
24933 ds.un("add", this.updateInfo, this);
24934 this.ds = undefined;
24938 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24939 * @param {Roo.data.Store} store The data store to bind
24941 bind : function(ds){
24942 ds.on("beforeload", this.beforeLoad, this);
24943 ds.on("load", this.onLoad, this);
24944 ds.on("loadexception", this.onLoadError, this);
24945 ds.on("remove", this.updateInfo, this);
24946 ds.on("add", this.updateInfo, this);
24957 * @class Roo.bootstrap.MessageBar
24958 * @extends Roo.bootstrap.Component
24959 * Bootstrap MessageBar class
24960 * @cfg {String} html contents of the MessageBar
24961 * @cfg {String} weight (info | success | warning | danger) default info
24962 * @cfg {String} beforeClass insert the bar before the given class
24963 * @cfg {Boolean} closable (true | false) default false
24964 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24967 * Create a new Element
24968 * @param {Object} config The config object
24971 Roo.bootstrap.MessageBar = function(config){
24972 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24975 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24981 beforeClass: 'bootstrap-sticky-wrap',
24983 getAutoCreate : function(){
24987 cls: 'alert alert-dismissable alert-' + this.weight,
24992 html: this.html || ''
24998 cfg.cls += ' alert-messages-fixed';
25012 onRender : function(ct, position)
25014 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25017 var cfg = Roo.apply({}, this.getAutoCreate());
25021 cfg.cls += ' ' + this.cls;
25024 cfg.style = this.style;
25026 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25028 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25031 this.el.select('>button.close').on('click', this.hide, this);
25037 if (!this.rendered) {
25043 this.fireEvent('show', this);
25049 if (!this.rendered) {
25055 this.fireEvent('hide', this);
25058 update : function()
25060 // var e = this.el.dom.firstChild;
25062 // if(this.closable){
25063 // e = e.nextSibling;
25066 // e.data = this.html || '';
25068 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25084 * @class Roo.bootstrap.Graph
25085 * @extends Roo.bootstrap.Component
25086 * Bootstrap Graph class
25090 @cfg {String} graphtype bar | vbar | pie
25091 @cfg {number} g_x coodinator | centre x (pie)
25092 @cfg {number} g_y coodinator | centre y (pie)
25093 @cfg {number} g_r radius (pie)
25094 @cfg {number} g_height height of the chart (respected by all elements in the set)
25095 @cfg {number} g_width width of the chart (respected by all elements in the set)
25096 @cfg {Object} title The title of the chart
25099 -opts (object) options for the chart
25101 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25102 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25104 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.
25105 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25107 o stretch (boolean)
25109 -opts (object) options for the pie
25112 o startAngle (number)
25113 o endAngle (number)
25117 * Create a new Input
25118 * @param {Object} config The config object
25121 Roo.bootstrap.Graph = function(config){
25122 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25128 * The img click event for the img.
25129 * @param {Roo.EventObject} e
25135 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25146 //g_colors: this.colors,
25153 getAutoCreate : function(){
25164 onRender : function(ct,position){
25167 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25169 if (typeof(Raphael) == 'undefined') {
25170 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25174 this.raphael = Raphael(this.el.dom);
25176 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25177 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25178 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25179 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25181 r.text(160, 10, "Single Series Chart").attr(txtattr);
25182 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25183 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25184 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25186 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25187 r.barchart(330, 10, 300, 220, data1);
25188 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25189 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25192 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25193 // r.barchart(30, 30, 560, 250, xdata, {
25194 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25195 // axis : "0 0 1 1",
25196 // axisxlabels : xdata
25197 // //yvalues : cols,
25200 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25202 // this.load(null,xdata,{
25203 // axis : "0 0 1 1",
25204 // axisxlabels : xdata
25209 load : function(graphtype,xdata,opts)
25211 this.raphael.clear();
25213 graphtype = this.graphtype;
25218 var r = this.raphael,
25219 fin = function () {
25220 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25222 fout = function () {
25223 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25225 pfin = function() {
25226 this.sector.stop();
25227 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25230 this.label[0].stop();
25231 this.label[0].attr({ r: 7.5 });
25232 this.label[1].attr({ "font-weight": 800 });
25235 pfout = function() {
25236 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25239 this.label[0].animate({ r: 5 }, 500, "bounce");
25240 this.label[1].attr({ "font-weight": 400 });
25246 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25249 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25252 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25253 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25255 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25262 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25267 setTitle: function(o)
25272 initEvents: function() {
25275 this.el.on('click', this.onClick, this);
25279 onClick : function(e)
25281 Roo.log('img onclick');
25282 this.fireEvent('click', this, e);
25294 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25297 * @class Roo.bootstrap.dash.NumberBox
25298 * @extends Roo.bootstrap.Component
25299 * Bootstrap NumberBox class
25300 * @cfg {String} headline Box headline
25301 * @cfg {String} content Box content
25302 * @cfg {String} icon Box icon
25303 * @cfg {String} footer Footer text
25304 * @cfg {String} fhref Footer href
25307 * Create a new NumberBox
25308 * @param {Object} config The config object
25312 Roo.bootstrap.dash.NumberBox = function(config){
25313 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25317 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25326 getAutoCreate : function(){
25330 cls : 'small-box ',
25338 cls : 'roo-headline',
25339 html : this.headline
25343 cls : 'roo-content',
25344 html : this.content
25358 cls : 'ion ' + this.icon
25367 cls : 'small-box-footer',
25368 href : this.fhref || '#',
25372 cfg.cn.push(footer);
25379 onRender : function(ct,position){
25380 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25387 setHeadline: function (value)
25389 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25392 setFooter: function (value, href)
25394 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25397 this.el.select('a.small-box-footer',true).first().attr('href', href);
25402 setContent: function (value)
25404 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25407 initEvents: function()
25421 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25424 * @class Roo.bootstrap.dash.TabBox
25425 * @extends Roo.bootstrap.Component
25426 * Bootstrap TabBox class
25427 * @cfg {String} title Title of the TabBox
25428 * @cfg {String} icon Icon of the TabBox
25429 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25430 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25433 * Create a new TabBox
25434 * @param {Object} config The config object
25438 Roo.bootstrap.dash.TabBox = function(config){
25439 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25444 * When a pane is added
25445 * @param {Roo.bootstrap.dash.TabPane} pane
25449 * @event activatepane
25450 * When a pane is activated
25451 * @param {Roo.bootstrap.dash.TabPane} pane
25453 "activatepane" : true
25461 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25466 tabScrollable : false,
25468 getChildContainer : function()
25470 return this.el.select('.tab-content', true).first();
25473 getAutoCreate : function(){
25477 cls: 'pull-left header',
25485 cls: 'fa ' + this.icon
25491 cls: 'nav nav-tabs pull-right',
25497 if(this.tabScrollable){
25504 cls: 'nav nav-tabs pull-right',
25515 cls: 'nav-tabs-custom',
25520 cls: 'tab-content no-padding',
25528 initEvents : function()
25530 //Roo.log('add add pane handler');
25531 this.on('addpane', this.onAddPane, this);
25534 * Updates the box title
25535 * @param {String} html to set the title to.
25537 setTitle : function(value)
25539 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25541 onAddPane : function(pane)
25543 this.panes.push(pane);
25544 //Roo.log('addpane');
25546 // tabs are rendere left to right..
25547 if(!this.showtabs){
25551 var ctr = this.el.select('.nav-tabs', true).first();
25554 var existing = ctr.select('.nav-tab',true);
25555 var qty = existing.getCount();;
25558 var tab = ctr.createChild({
25560 cls : 'nav-tab' + (qty ? '' : ' active'),
25568 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25571 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25573 pane.el.addClass('active');
25578 onTabClick : function(ev,un,ob,pane)
25580 //Roo.log('tab - prev default');
25581 ev.preventDefault();
25584 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25585 pane.tab.addClass('active');
25586 //Roo.log(pane.title);
25587 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25588 // technically we should have a deactivate event.. but maybe add later.
25589 // and it should not de-activate the selected tab...
25590 this.fireEvent('activatepane', pane);
25591 pane.el.addClass('active');
25592 pane.fireEvent('activate');
25597 getActivePane : function()
25600 Roo.each(this.panes, function(p) {
25601 if(p.el.hasClass('active')){
25622 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25624 * @class Roo.bootstrap.TabPane
25625 * @extends Roo.bootstrap.Component
25626 * Bootstrap TabPane class
25627 * @cfg {Boolean} active (false | true) Default false
25628 * @cfg {String} title title of panel
25632 * Create a new TabPane
25633 * @param {Object} config The config object
25636 Roo.bootstrap.dash.TabPane = function(config){
25637 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25643 * When a pane is activated
25644 * @param {Roo.bootstrap.dash.TabPane} pane
25651 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25656 // the tabBox that this is attached to.
25659 getAutoCreate : function()
25667 cfg.cls += ' active';
25672 initEvents : function()
25674 //Roo.log('trigger add pane handler');
25675 this.parent().fireEvent('addpane', this)
25679 * Updates the tab title
25680 * @param {String} html to set the title to.
25682 setTitle: function(str)
25688 this.tab.select('a', true).first().dom.innerHTML = str;
25705 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25708 * @class Roo.bootstrap.menu.Menu
25709 * @extends Roo.bootstrap.Component
25710 * Bootstrap Menu class - container for Menu
25711 * @cfg {String} html Text of the menu
25712 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25713 * @cfg {String} icon Font awesome icon
25714 * @cfg {String} pos Menu align to (top | bottom) default bottom
25718 * Create a new Menu
25719 * @param {Object} config The config object
25723 Roo.bootstrap.menu.Menu = function(config){
25724 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25728 * @event beforeshow
25729 * Fires before this menu is displayed
25730 * @param {Roo.bootstrap.menu.Menu} this
25734 * @event beforehide
25735 * Fires before this menu is hidden
25736 * @param {Roo.bootstrap.menu.Menu} this
25741 * Fires after this menu is displayed
25742 * @param {Roo.bootstrap.menu.Menu} this
25747 * Fires after this menu is hidden
25748 * @param {Roo.bootstrap.menu.Menu} this
25753 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25754 * @param {Roo.bootstrap.menu.Menu} this
25755 * @param {Roo.EventObject} e
25762 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25766 weight : 'default',
25771 getChildContainer : function() {
25772 if(this.isSubMenu){
25776 return this.el.select('ul.dropdown-menu', true).first();
25779 getAutoCreate : function()
25784 cls : 'roo-menu-text',
25792 cls : 'fa ' + this.icon
25803 cls : 'dropdown-button btn btn-' + this.weight,
25808 cls : 'dropdown-toggle btn btn-' + this.weight,
25818 cls : 'dropdown-menu'
25824 if(this.pos == 'top'){
25825 cfg.cls += ' dropup';
25828 if(this.isSubMenu){
25831 cls : 'dropdown-menu'
25838 onRender : function(ct, position)
25840 this.isSubMenu = ct.hasClass('dropdown-submenu');
25842 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25845 initEvents : function()
25847 if(this.isSubMenu){
25851 this.hidden = true;
25853 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25854 this.triggerEl.on('click', this.onTriggerPress, this);
25856 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25857 this.buttonEl.on('click', this.onClick, this);
25863 if(this.isSubMenu){
25867 return this.el.select('ul.dropdown-menu', true).first();
25870 onClick : function(e)
25872 this.fireEvent("click", this, e);
25875 onTriggerPress : function(e)
25877 if (this.isVisible()) {
25884 isVisible : function(){
25885 return !this.hidden;
25890 this.fireEvent("beforeshow", this);
25892 this.hidden = false;
25893 this.el.addClass('open');
25895 Roo.get(document).on("mouseup", this.onMouseUp, this);
25897 this.fireEvent("show", this);
25904 this.fireEvent("beforehide", this);
25906 this.hidden = true;
25907 this.el.removeClass('open');
25909 Roo.get(document).un("mouseup", this.onMouseUp);
25911 this.fireEvent("hide", this);
25914 onMouseUp : function()
25928 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25931 * @class Roo.bootstrap.menu.Item
25932 * @extends Roo.bootstrap.Component
25933 * Bootstrap MenuItem class
25934 * @cfg {Boolean} submenu (true | false) default false
25935 * @cfg {String} html text of the item
25936 * @cfg {String} href the link
25937 * @cfg {Boolean} disable (true | false) default false
25938 * @cfg {Boolean} preventDefault (true | false) default true
25939 * @cfg {String} icon Font awesome icon
25940 * @cfg {String} pos Submenu align to (left | right) default right
25944 * Create a new Item
25945 * @param {Object} config The config object
25949 Roo.bootstrap.menu.Item = function(config){
25950 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25954 * Fires when the mouse is hovering over this menu
25955 * @param {Roo.bootstrap.menu.Item} this
25956 * @param {Roo.EventObject} e
25961 * Fires when the mouse exits this menu
25962 * @param {Roo.bootstrap.menu.Item} this
25963 * @param {Roo.EventObject} e
25969 * The raw click event for the entire grid.
25970 * @param {Roo.EventObject} e
25976 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25981 preventDefault: true,
25986 getAutoCreate : function()
25991 cls : 'roo-menu-item-text',
25999 cls : 'fa ' + this.icon
26008 href : this.href || '#',
26015 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26019 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26021 if(this.pos == 'left'){
26022 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26029 initEvents : function()
26031 this.el.on('mouseover', this.onMouseOver, this);
26032 this.el.on('mouseout', this.onMouseOut, this);
26034 this.el.select('a', true).first().on('click', this.onClick, this);
26038 onClick : function(e)
26040 if(this.preventDefault){
26041 e.preventDefault();
26044 this.fireEvent("click", this, e);
26047 onMouseOver : function(e)
26049 if(this.submenu && this.pos == 'left'){
26050 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26053 this.fireEvent("mouseover", this, e);
26056 onMouseOut : function(e)
26058 this.fireEvent("mouseout", this, e);
26070 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26073 * @class Roo.bootstrap.menu.Separator
26074 * @extends Roo.bootstrap.Component
26075 * Bootstrap Separator class
26078 * Create a new Separator
26079 * @param {Object} config The config object
26083 Roo.bootstrap.menu.Separator = function(config){
26084 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26087 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26089 getAutoCreate : function(){
26110 * @class Roo.bootstrap.Tooltip
26111 * Bootstrap Tooltip class
26112 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26113 * to determine which dom element triggers the tooltip.
26115 * It needs to add support for additional attributes like tooltip-position
26118 * Create a new Toolti
26119 * @param {Object} config The config object
26122 Roo.bootstrap.Tooltip = function(config){
26123 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26125 this.alignment = Roo.bootstrap.Tooltip.alignment;
26127 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26128 this.alignment = config.alignment;
26133 Roo.apply(Roo.bootstrap.Tooltip, {
26135 * @function init initialize tooltip monitoring.
26139 currentTip : false,
26140 currentRegion : false,
26146 Roo.get(document).on('mouseover', this.enter ,this);
26147 Roo.get(document).on('mouseout', this.leave, this);
26150 this.currentTip = new Roo.bootstrap.Tooltip();
26153 enter : function(ev)
26155 var dom = ev.getTarget();
26157 //Roo.log(['enter',dom]);
26158 var el = Roo.fly(dom);
26159 if (this.currentEl) {
26161 //Roo.log(this.currentEl);
26162 //Roo.log(this.currentEl.contains(dom));
26163 if (this.currentEl == el) {
26166 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26172 if (this.currentTip.el) {
26173 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26177 if(!el || el.dom == document){
26183 // you can not look for children, as if el is the body.. then everythign is the child..
26184 if (!el.attr('tooltip')) { //
26185 if (!el.select("[tooltip]").elements.length) {
26188 // is the mouse over this child...?
26189 bindEl = el.select("[tooltip]").first();
26190 var xy = ev.getXY();
26191 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26192 //Roo.log("not in region.");
26195 //Roo.log("child element over..");
26198 this.currentEl = bindEl;
26199 this.currentTip.bind(bindEl);
26200 this.currentRegion = Roo.lib.Region.getRegion(dom);
26201 this.currentTip.enter();
26204 leave : function(ev)
26206 var dom = ev.getTarget();
26207 //Roo.log(['leave',dom]);
26208 if (!this.currentEl) {
26213 if (dom != this.currentEl.dom) {
26216 var xy = ev.getXY();
26217 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26220 // only activate leave if mouse cursor is outside... bounding box..
26225 if (this.currentTip) {
26226 this.currentTip.leave();
26228 //Roo.log('clear currentEl');
26229 this.currentEl = false;
26234 'left' : ['r-l', [-2,0], 'right'],
26235 'right' : ['l-r', [2,0], 'left'],
26236 'bottom' : ['t-b', [0,2], 'top'],
26237 'top' : [ 'b-t', [0,-2], 'bottom']
26243 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26248 delay : null, // can be { show : 300 , hide: 500}
26252 hoverState : null, //???
26254 placement : 'bottom',
26258 getAutoCreate : function(){
26265 cls : 'tooltip-arrow'
26268 cls : 'tooltip-inner'
26275 bind : function(el)
26281 enter : function () {
26283 if (this.timeout != null) {
26284 clearTimeout(this.timeout);
26287 this.hoverState = 'in';
26288 //Roo.log("enter - show");
26289 if (!this.delay || !this.delay.show) {
26294 this.timeout = setTimeout(function () {
26295 if (_t.hoverState == 'in') {
26298 }, this.delay.show);
26302 clearTimeout(this.timeout);
26304 this.hoverState = 'out';
26305 if (!this.delay || !this.delay.hide) {
26311 this.timeout = setTimeout(function () {
26312 //Roo.log("leave - timeout");
26314 if (_t.hoverState == 'out') {
26316 Roo.bootstrap.Tooltip.currentEl = false;
26321 show : function (msg)
26324 this.render(document.body);
26327 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26329 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26331 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26333 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26335 var placement = typeof this.placement == 'function' ?
26336 this.placement.call(this, this.el, on_el) :
26339 var autoToken = /\s?auto?\s?/i;
26340 var autoPlace = autoToken.test(placement);
26342 placement = placement.replace(autoToken, '') || 'top';
26346 //this.el.setXY([0,0]);
26348 //this.el.dom.style.display='block';
26350 //this.el.appendTo(on_el);
26352 var p = this.getPosition();
26353 var box = this.el.getBox();
26359 var align = this.alignment[placement];
26361 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26363 if(placement == 'top' || placement == 'bottom'){
26365 placement = 'right';
26368 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26369 placement = 'left';
26372 var scroll = Roo.select('body', true).first().getScroll();
26374 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26378 align = this.alignment[placement];
26381 this.el.alignTo(this.bindEl, align[0],align[1]);
26382 //var arrow = this.el.select('.arrow',true).first();
26383 //arrow.set(align[2],
26385 this.el.addClass(placement);
26387 this.el.addClass('in fade');
26389 this.hoverState = null;
26391 if (this.el.hasClass('fade')) {
26402 //this.el.setXY([0,0]);
26403 this.el.removeClass('in');
26419 * @class Roo.bootstrap.LocationPicker
26420 * @extends Roo.bootstrap.Component
26421 * Bootstrap LocationPicker class
26422 * @cfg {Number} latitude Position when init default 0
26423 * @cfg {Number} longitude Position when init default 0
26424 * @cfg {Number} zoom default 15
26425 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26426 * @cfg {Boolean} mapTypeControl default false
26427 * @cfg {Boolean} disableDoubleClickZoom default false
26428 * @cfg {Boolean} scrollwheel default true
26429 * @cfg {Boolean} streetViewControl default false
26430 * @cfg {Number} radius default 0
26431 * @cfg {String} locationName
26432 * @cfg {Boolean} draggable default true
26433 * @cfg {Boolean} enableAutocomplete default false
26434 * @cfg {Boolean} enableReverseGeocode default true
26435 * @cfg {String} markerTitle
26438 * Create a new LocationPicker
26439 * @param {Object} config The config object
26443 Roo.bootstrap.LocationPicker = function(config){
26445 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26450 * Fires when the picker initialized.
26451 * @param {Roo.bootstrap.LocationPicker} this
26452 * @param {Google Location} location
26456 * @event positionchanged
26457 * Fires when the picker position changed.
26458 * @param {Roo.bootstrap.LocationPicker} this
26459 * @param {Google Location} location
26461 positionchanged : true,
26464 * Fires when the map resize.
26465 * @param {Roo.bootstrap.LocationPicker} this
26470 * Fires when the map show.
26471 * @param {Roo.bootstrap.LocationPicker} this
26476 * Fires when the map hide.
26477 * @param {Roo.bootstrap.LocationPicker} this
26482 * Fires when click the map.
26483 * @param {Roo.bootstrap.LocationPicker} this
26484 * @param {Map event} e
26488 * @event mapRightClick
26489 * Fires when right click the map.
26490 * @param {Roo.bootstrap.LocationPicker} this
26491 * @param {Map event} e
26493 mapRightClick : true,
26495 * @event markerClick
26496 * Fires when click the marker.
26497 * @param {Roo.bootstrap.LocationPicker} this
26498 * @param {Map event} e
26500 markerClick : true,
26502 * @event markerRightClick
26503 * Fires when right click the marker.
26504 * @param {Roo.bootstrap.LocationPicker} this
26505 * @param {Map event} e
26507 markerRightClick : true,
26509 * @event OverlayViewDraw
26510 * Fires when OverlayView Draw
26511 * @param {Roo.bootstrap.LocationPicker} this
26513 OverlayViewDraw : true,
26515 * @event OverlayViewOnAdd
26516 * Fires when OverlayView Draw
26517 * @param {Roo.bootstrap.LocationPicker} this
26519 OverlayViewOnAdd : true,
26521 * @event OverlayViewOnRemove
26522 * Fires when OverlayView Draw
26523 * @param {Roo.bootstrap.LocationPicker} this
26525 OverlayViewOnRemove : true,
26527 * @event OverlayViewShow
26528 * Fires when OverlayView Draw
26529 * @param {Roo.bootstrap.LocationPicker} this
26530 * @param {Pixel} cpx
26532 OverlayViewShow : true,
26534 * @event OverlayViewHide
26535 * Fires when OverlayView Draw
26536 * @param {Roo.bootstrap.LocationPicker} this
26538 OverlayViewHide : true,
26540 * @event loadexception
26541 * Fires when load google lib failed.
26542 * @param {Roo.bootstrap.LocationPicker} this
26544 loadexception : true
26549 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26551 gMapContext: false,
26557 mapTypeControl: false,
26558 disableDoubleClickZoom: false,
26560 streetViewControl: false,
26564 enableAutocomplete: false,
26565 enableReverseGeocode: true,
26568 getAutoCreate: function()
26573 cls: 'roo-location-picker'
26579 initEvents: function(ct, position)
26581 if(!this.el.getWidth() || this.isApplied()){
26585 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26590 initial: function()
26592 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26593 this.fireEvent('loadexception', this);
26597 if(!this.mapTypeId){
26598 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26601 this.gMapContext = this.GMapContext();
26603 this.initOverlayView();
26605 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26609 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26610 _this.setPosition(_this.gMapContext.marker.position);
26613 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26614 _this.fireEvent('mapClick', this, event);
26618 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26619 _this.fireEvent('mapRightClick', this, event);
26623 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26624 _this.fireEvent('markerClick', this, event);
26628 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26629 _this.fireEvent('markerRightClick', this, event);
26633 this.setPosition(this.gMapContext.location);
26635 this.fireEvent('initial', this, this.gMapContext.location);
26638 initOverlayView: function()
26642 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26646 _this.fireEvent('OverlayViewDraw', _this);
26651 _this.fireEvent('OverlayViewOnAdd', _this);
26654 onRemove: function()
26656 _this.fireEvent('OverlayViewOnRemove', _this);
26659 show: function(cpx)
26661 _this.fireEvent('OverlayViewShow', _this, cpx);
26666 _this.fireEvent('OverlayViewHide', _this);
26672 fromLatLngToContainerPixel: function(event)
26674 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26677 isApplied: function()
26679 return this.getGmapContext() == false ? false : true;
26682 getGmapContext: function()
26684 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26687 GMapContext: function()
26689 var position = new google.maps.LatLng(this.latitude, this.longitude);
26691 var _map = new google.maps.Map(this.el.dom, {
26694 mapTypeId: this.mapTypeId,
26695 mapTypeControl: this.mapTypeControl,
26696 disableDoubleClickZoom: this.disableDoubleClickZoom,
26697 scrollwheel: this.scrollwheel,
26698 streetViewControl: this.streetViewControl,
26699 locationName: this.locationName,
26700 draggable: this.draggable,
26701 enableAutocomplete: this.enableAutocomplete,
26702 enableReverseGeocode: this.enableReverseGeocode
26705 var _marker = new google.maps.Marker({
26706 position: position,
26708 title: this.markerTitle,
26709 draggable: this.draggable
26716 location: position,
26717 radius: this.radius,
26718 locationName: this.locationName,
26719 addressComponents: {
26720 formatted_address: null,
26721 addressLine1: null,
26722 addressLine2: null,
26724 streetNumber: null,
26728 stateOrProvince: null
26731 domContainer: this.el.dom,
26732 geodecoder: new google.maps.Geocoder()
26736 drawCircle: function(center, radius, options)
26738 if (this.gMapContext.circle != null) {
26739 this.gMapContext.circle.setMap(null);
26743 options = Roo.apply({}, options, {
26744 strokeColor: "#0000FF",
26745 strokeOpacity: .35,
26747 fillColor: "#0000FF",
26751 options.map = this.gMapContext.map;
26752 options.radius = radius;
26753 options.center = center;
26754 this.gMapContext.circle = new google.maps.Circle(options);
26755 return this.gMapContext.circle;
26761 setPosition: function(location)
26763 this.gMapContext.location = location;
26764 this.gMapContext.marker.setPosition(location);
26765 this.gMapContext.map.panTo(location);
26766 this.drawCircle(location, this.gMapContext.radius, {});
26770 if (this.gMapContext.settings.enableReverseGeocode) {
26771 this.gMapContext.geodecoder.geocode({
26772 latLng: this.gMapContext.location
26773 }, function(results, status) {
26775 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26776 _this.gMapContext.locationName = results[0].formatted_address;
26777 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26779 _this.fireEvent('positionchanged', this, location);
26786 this.fireEvent('positionchanged', this, location);
26791 google.maps.event.trigger(this.gMapContext.map, "resize");
26793 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26795 this.fireEvent('resize', this);
26798 setPositionByLatLng: function(latitude, longitude)
26800 this.setPosition(new google.maps.LatLng(latitude, longitude));
26803 getCurrentPosition: function()
26806 latitude: this.gMapContext.location.lat(),
26807 longitude: this.gMapContext.location.lng()
26811 getAddressName: function()
26813 return this.gMapContext.locationName;
26816 getAddressComponents: function()
26818 return this.gMapContext.addressComponents;
26821 address_component_from_google_geocode: function(address_components)
26825 for (var i = 0; i < address_components.length; i++) {
26826 var component = address_components[i];
26827 if (component.types.indexOf("postal_code") >= 0) {
26828 result.postalCode = component.short_name;
26829 } else if (component.types.indexOf("street_number") >= 0) {
26830 result.streetNumber = component.short_name;
26831 } else if (component.types.indexOf("route") >= 0) {
26832 result.streetName = component.short_name;
26833 } else if (component.types.indexOf("neighborhood") >= 0) {
26834 result.city = component.short_name;
26835 } else if (component.types.indexOf("locality") >= 0) {
26836 result.city = component.short_name;
26837 } else if (component.types.indexOf("sublocality") >= 0) {
26838 result.district = component.short_name;
26839 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26840 result.stateOrProvince = component.short_name;
26841 } else if (component.types.indexOf("country") >= 0) {
26842 result.country = component.short_name;
26846 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26847 result.addressLine2 = "";
26851 setZoomLevel: function(zoom)
26853 this.gMapContext.map.setZoom(zoom);
26866 this.fireEvent('show', this);
26877 this.fireEvent('hide', this);
26882 Roo.apply(Roo.bootstrap.LocationPicker, {
26884 OverlayView : function(map, options)
26886 options = options || {};
26900 * @class Roo.bootstrap.Alert
26901 * @extends Roo.bootstrap.Component
26902 * Bootstrap Alert class
26903 * @cfg {String} title The title of alert
26904 * @cfg {String} html The content of alert
26905 * @cfg {String} weight ( success | info | warning | danger )
26906 * @cfg {String} faicon font-awesomeicon
26909 * Create a new alert
26910 * @param {Object} config The config object
26914 Roo.bootstrap.Alert = function(config){
26915 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26919 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26926 getAutoCreate : function()
26935 cls : 'roo-alert-icon'
26940 cls : 'roo-alert-title',
26945 cls : 'roo-alert-text',
26952 cfg.cn[0].cls += ' fa ' + this.faicon;
26956 cfg.cls += ' alert-' + this.weight;
26962 initEvents: function()
26964 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26967 setTitle : function(str)
26969 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26972 setText : function(str)
26974 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26977 setWeight : function(weight)
26980 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26983 this.weight = weight;
26985 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26988 setIcon : function(icon)
26991 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26994 this.faicon = icon;
26996 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27017 * @class Roo.bootstrap.UploadCropbox
27018 * @extends Roo.bootstrap.Component
27019 * Bootstrap UploadCropbox class
27020 * @cfg {String} emptyText show when image has been loaded
27021 * @cfg {String} rotateNotify show when image too small to rotate
27022 * @cfg {Number} errorTimeout default 3000
27023 * @cfg {Number} minWidth default 300
27024 * @cfg {Number} minHeight default 300
27025 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27026 * @cfg {Boolean} isDocument (true|false) default false
27027 * @cfg {String} url action url
27028 * @cfg {String} paramName default 'imageUpload'
27029 * @cfg {String} method default POST
27030 * @cfg {Boolean} loadMask (true|false) default true
27031 * @cfg {Boolean} loadingText default 'Loading...'
27034 * Create a new UploadCropbox
27035 * @param {Object} config The config object
27038 Roo.bootstrap.UploadCropbox = function(config){
27039 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27043 * @event beforeselectfile
27044 * Fire before select file
27045 * @param {Roo.bootstrap.UploadCropbox} this
27047 "beforeselectfile" : true,
27050 * Fire after initEvent
27051 * @param {Roo.bootstrap.UploadCropbox} this
27056 * Fire after initEvent
27057 * @param {Roo.bootstrap.UploadCropbox} this
27058 * @param {String} data
27063 * Fire when preparing the file data
27064 * @param {Roo.bootstrap.UploadCropbox} this
27065 * @param {Object} file
27070 * Fire when get exception
27071 * @param {Roo.bootstrap.UploadCropbox} this
27072 * @param {XMLHttpRequest} xhr
27074 "exception" : true,
27076 * @event beforeloadcanvas
27077 * Fire before load the canvas
27078 * @param {Roo.bootstrap.UploadCropbox} this
27079 * @param {String} src
27081 "beforeloadcanvas" : true,
27084 * Fire when trash image
27085 * @param {Roo.bootstrap.UploadCropbox} this
27090 * Fire when download the image
27091 * @param {Roo.bootstrap.UploadCropbox} this
27095 * @event footerbuttonclick
27096 * Fire when footerbuttonclick
27097 * @param {Roo.bootstrap.UploadCropbox} this
27098 * @param {String} type
27100 "footerbuttonclick" : true,
27104 * @param {Roo.bootstrap.UploadCropbox} this
27109 * Fire when rotate the image
27110 * @param {Roo.bootstrap.UploadCropbox} this
27111 * @param {String} pos
27116 * Fire when inspect the file
27117 * @param {Roo.bootstrap.UploadCropbox} this
27118 * @param {Object} file
27123 * Fire when xhr upload the file
27124 * @param {Roo.bootstrap.UploadCropbox} this
27125 * @param {Object} data
27130 * Fire when arrange the file data
27131 * @param {Roo.bootstrap.UploadCropbox} this
27132 * @param {Object} formData
27137 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27140 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27142 emptyText : 'Click to upload image',
27143 rotateNotify : 'Image is too small to rotate',
27144 errorTimeout : 3000,
27158 cropType : 'image/jpeg',
27160 canvasLoaded : false,
27161 isDocument : false,
27163 paramName : 'imageUpload',
27165 loadingText : 'Loading...',
27168 getAutoCreate : function()
27172 cls : 'roo-upload-cropbox',
27176 cls : 'roo-upload-cropbox-selector',
27181 cls : 'roo-upload-cropbox-body',
27182 style : 'cursor:pointer',
27186 cls : 'roo-upload-cropbox-preview'
27190 cls : 'roo-upload-cropbox-thumb'
27194 cls : 'roo-upload-cropbox-empty-notify',
27195 html : this.emptyText
27199 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27200 html : this.rotateNotify
27206 cls : 'roo-upload-cropbox-footer',
27209 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27219 onRender : function(ct, position)
27221 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27223 if (this.buttons.length) {
27225 Roo.each(this.buttons, function(bb) {
27227 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27229 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27235 this.maskEl = this.el;
27239 initEvents : function()
27241 this.urlAPI = (window.createObjectURL && window) ||
27242 (window.URL && URL.revokeObjectURL && URL) ||
27243 (window.webkitURL && webkitURL);
27245 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27246 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27248 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27249 this.selectorEl.hide();
27251 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27252 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27254 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27255 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27256 this.thumbEl.hide();
27258 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27259 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27261 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27262 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27263 this.errorEl.hide();
27265 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27266 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27267 this.footerEl.hide();
27269 this.setThumbBoxSize();
27275 this.fireEvent('initial', this);
27282 window.addEventListener("resize", function() { _this.resize(); } );
27284 this.bodyEl.on('click', this.beforeSelectFile, this);
27287 this.bodyEl.on('touchstart', this.onTouchStart, this);
27288 this.bodyEl.on('touchmove', this.onTouchMove, this);
27289 this.bodyEl.on('touchend', this.onTouchEnd, this);
27293 this.bodyEl.on('mousedown', this.onMouseDown, this);
27294 this.bodyEl.on('mousemove', this.onMouseMove, this);
27295 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27296 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27297 Roo.get(document).on('mouseup', this.onMouseUp, this);
27300 this.selectorEl.on('change', this.onFileSelected, this);
27306 this.baseScale = 1;
27308 this.baseRotate = 1;
27309 this.dragable = false;
27310 this.pinching = false;
27313 this.cropData = false;
27314 this.notifyEl.dom.innerHTML = this.emptyText;
27316 this.selectorEl.dom.value = '';
27320 resize : function()
27322 if(this.fireEvent('resize', this) != false){
27323 this.setThumbBoxPosition();
27324 this.setCanvasPosition();
27328 onFooterButtonClick : function(e, el, o, type)
27331 case 'rotate-left' :
27332 this.onRotateLeft(e);
27334 case 'rotate-right' :
27335 this.onRotateRight(e);
27338 this.beforeSelectFile(e);
27353 this.fireEvent('footerbuttonclick', this, type);
27356 beforeSelectFile : function(e)
27358 e.preventDefault();
27360 if(this.fireEvent('beforeselectfile', this) != false){
27361 this.selectorEl.dom.click();
27365 onFileSelected : function(e)
27367 e.preventDefault();
27369 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27373 var file = this.selectorEl.dom.files[0];
27375 if(this.fireEvent('inspect', this, file) != false){
27376 this.prepare(file);
27381 trash : function(e)
27383 this.fireEvent('trash', this);
27386 download : function(e)
27388 this.fireEvent('download', this);
27391 loadCanvas : function(src)
27393 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27397 this.imageEl = document.createElement('img');
27401 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27403 this.imageEl.src = src;
27407 onLoadCanvas : function()
27409 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27410 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27412 this.bodyEl.un('click', this.beforeSelectFile, this);
27414 this.notifyEl.hide();
27415 this.thumbEl.show();
27416 this.footerEl.show();
27418 this.baseRotateLevel();
27420 if(this.isDocument){
27421 this.setThumbBoxSize();
27424 this.setThumbBoxPosition();
27426 this.baseScaleLevel();
27432 this.canvasLoaded = true;
27435 this.maskEl.unmask();
27440 setCanvasPosition : function()
27442 if(!this.canvasEl){
27446 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27447 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27449 this.previewEl.setLeft(pw);
27450 this.previewEl.setTop(ph);
27454 onMouseDown : function(e)
27458 this.dragable = true;
27459 this.pinching = false;
27461 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27462 this.dragable = false;
27466 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27467 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27471 onMouseMove : function(e)
27475 if(!this.canvasLoaded){
27479 if (!this.dragable){
27483 var minX = Math.ceil(this.thumbEl.getLeft(true));
27484 var minY = Math.ceil(this.thumbEl.getTop(true));
27486 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27487 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27489 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27490 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27492 x = x - this.mouseX;
27493 y = y - this.mouseY;
27495 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27496 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27498 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27499 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27501 this.previewEl.setLeft(bgX);
27502 this.previewEl.setTop(bgY);
27504 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27505 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27508 onMouseUp : function(e)
27512 this.dragable = false;
27515 onMouseWheel : function(e)
27519 this.startScale = this.scale;
27521 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27523 if(!this.zoomable()){
27524 this.scale = this.startScale;
27533 zoomable : function()
27535 var minScale = this.thumbEl.getWidth() / this.minWidth;
27537 if(this.minWidth < this.minHeight){
27538 minScale = this.thumbEl.getHeight() / this.minHeight;
27541 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27542 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27546 (this.rotate == 0 || this.rotate == 180) &&
27548 width > this.imageEl.OriginWidth ||
27549 height > this.imageEl.OriginHeight ||
27550 (width < this.minWidth && height < this.minHeight)
27558 (this.rotate == 90 || this.rotate == 270) &&
27560 width > this.imageEl.OriginWidth ||
27561 height > this.imageEl.OriginHeight ||
27562 (width < this.minHeight && height < this.minWidth)
27569 !this.isDocument &&
27570 (this.rotate == 0 || this.rotate == 180) &&
27572 width < this.minWidth ||
27573 width > this.imageEl.OriginWidth ||
27574 height < this.minHeight ||
27575 height > this.imageEl.OriginHeight
27582 !this.isDocument &&
27583 (this.rotate == 90 || this.rotate == 270) &&
27585 width < this.minHeight ||
27586 width > this.imageEl.OriginWidth ||
27587 height < this.minWidth ||
27588 height > this.imageEl.OriginHeight
27598 onRotateLeft : function(e)
27600 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27602 var minScale = this.thumbEl.getWidth() / this.minWidth;
27604 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27605 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27607 this.startScale = this.scale;
27609 while (this.getScaleLevel() < minScale){
27611 this.scale = this.scale + 1;
27613 if(!this.zoomable()){
27618 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27619 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27624 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27631 this.scale = this.startScale;
27633 this.onRotateFail();
27638 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27640 if(this.isDocument){
27641 this.setThumbBoxSize();
27642 this.setThumbBoxPosition();
27643 this.setCanvasPosition();
27648 this.fireEvent('rotate', this, 'left');
27652 onRotateRight : function(e)
27654 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27656 var minScale = this.thumbEl.getWidth() / this.minWidth;
27658 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27659 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27661 this.startScale = this.scale;
27663 while (this.getScaleLevel() < minScale){
27665 this.scale = this.scale + 1;
27667 if(!this.zoomable()){
27672 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27673 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27678 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27685 this.scale = this.startScale;
27687 this.onRotateFail();
27692 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27694 if(this.isDocument){
27695 this.setThumbBoxSize();
27696 this.setThumbBoxPosition();
27697 this.setCanvasPosition();
27702 this.fireEvent('rotate', this, 'right');
27705 onRotateFail : function()
27707 this.errorEl.show(true);
27711 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27716 this.previewEl.dom.innerHTML = '';
27718 var canvasEl = document.createElement("canvas");
27720 var contextEl = canvasEl.getContext("2d");
27722 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27723 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27724 var center = this.imageEl.OriginWidth / 2;
27726 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27727 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27728 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27729 center = this.imageEl.OriginHeight / 2;
27732 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27734 contextEl.translate(center, center);
27735 contextEl.rotate(this.rotate * Math.PI / 180);
27737 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27739 this.canvasEl = document.createElement("canvas");
27741 this.contextEl = this.canvasEl.getContext("2d");
27743 switch (this.rotate) {
27746 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27747 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27749 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27754 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27755 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27757 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27758 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);
27762 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27767 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27768 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27770 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27771 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);
27775 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);
27780 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27781 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27783 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27784 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27788 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);
27795 this.previewEl.appendChild(this.canvasEl);
27797 this.setCanvasPosition();
27802 if(!this.canvasLoaded){
27806 var imageCanvas = document.createElement("canvas");
27808 var imageContext = imageCanvas.getContext("2d");
27810 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27811 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27813 var center = imageCanvas.width / 2;
27815 imageContext.translate(center, center);
27817 imageContext.rotate(this.rotate * Math.PI / 180);
27819 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27821 var canvas = document.createElement("canvas");
27823 var context = canvas.getContext("2d");
27825 canvas.width = this.minWidth;
27826 canvas.height = this.minHeight;
27828 switch (this.rotate) {
27831 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27832 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27834 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27835 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27837 var targetWidth = this.minWidth - 2 * x;
27838 var targetHeight = this.minHeight - 2 * y;
27842 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27843 scale = targetWidth / width;
27846 if(x > 0 && y == 0){
27847 scale = targetHeight / height;
27850 if(x > 0 && y > 0){
27851 scale = targetWidth / width;
27853 if(width < height){
27854 scale = targetHeight / height;
27858 context.scale(scale, scale);
27860 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27861 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27863 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27864 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27866 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27871 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27872 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27874 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27875 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27877 var targetWidth = this.minWidth - 2 * x;
27878 var targetHeight = this.minHeight - 2 * y;
27882 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27883 scale = targetWidth / width;
27886 if(x > 0 && y == 0){
27887 scale = targetHeight / height;
27890 if(x > 0 && y > 0){
27891 scale = targetWidth / width;
27893 if(width < height){
27894 scale = targetHeight / height;
27898 context.scale(scale, scale);
27900 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27901 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27903 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27904 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27906 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27908 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27913 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27914 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27916 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27917 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27919 var targetWidth = this.minWidth - 2 * x;
27920 var targetHeight = this.minHeight - 2 * y;
27924 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27925 scale = targetWidth / width;
27928 if(x > 0 && y == 0){
27929 scale = targetHeight / height;
27932 if(x > 0 && y > 0){
27933 scale = targetWidth / width;
27935 if(width < height){
27936 scale = targetHeight / height;
27940 context.scale(scale, scale);
27942 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27943 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27945 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27946 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27948 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27949 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27951 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27956 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27957 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27959 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27960 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27962 var targetWidth = this.minWidth - 2 * x;
27963 var targetHeight = this.minHeight - 2 * y;
27967 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27968 scale = targetWidth / width;
27971 if(x > 0 && y == 0){
27972 scale = targetHeight / height;
27975 if(x > 0 && y > 0){
27976 scale = targetWidth / width;
27978 if(width < height){
27979 scale = targetHeight / height;
27983 context.scale(scale, scale);
27985 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27986 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27988 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27989 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27991 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27993 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28000 this.cropData = canvas.toDataURL(this.cropType);
28002 if(this.fireEvent('crop', this, this.cropData) !== false){
28003 this.process(this.file, this.cropData);
28010 setThumbBoxSize : function()
28014 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28015 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28016 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28018 this.minWidth = width;
28019 this.minHeight = height;
28021 if(this.rotate == 90 || this.rotate == 270){
28022 this.minWidth = height;
28023 this.minHeight = width;
28028 width = Math.ceil(this.minWidth * height / this.minHeight);
28030 if(this.minWidth > this.minHeight){
28032 height = Math.ceil(this.minHeight * width / this.minWidth);
28035 this.thumbEl.setStyle({
28036 width : width + 'px',
28037 height : height + 'px'
28044 setThumbBoxPosition : function()
28046 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28047 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28049 this.thumbEl.setLeft(x);
28050 this.thumbEl.setTop(y);
28054 baseRotateLevel : function()
28056 this.baseRotate = 1;
28059 typeof(this.exif) != 'undefined' &&
28060 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28061 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28063 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28066 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28070 baseScaleLevel : function()
28074 if(this.isDocument){
28076 if(this.baseRotate == 6 || this.baseRotate == 8){
28078 height = this.thumbEl.getHeight();
28079 this.baseScale = height / this.imageEl.OriginWidth;
28081 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28082 width = this.thumbEl.getWidth();
28083 this.baseScale = width / this.imageEl.OriginHeight;
28089 height = this.thumbEl.getHeight();
28090 this.baseScale = height / this.imageEl.OriginHeight;
28092 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28093 width = this.thumbEl.getWidth();
28094 this.baseScale = width / this.imageEl.OriginWidth;
28100 if(this.baseRotate == 6 || this.baseRotate == 8){
28102 width = this.thumbEl.getHeight();
28103 this.baseScale = width / this.imageEl.OriginHeight;
28105 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28106 height = this.thumbEl.getWidth();
28107 this.baseScale = height / this.imageEl.OriginHeight;
28110 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28111 height = this.thumbEl.getWidth();
28112 this.baseScale = height / this.imageEl.OriginHeight;
28114 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28115 width = this.thumbEl.getHeight();
28116 this.baseScale = width / this.imageEl.OriginWidth;
28123 width = this.thumbEl.getWidth();
28124 this.baseScale = width / this.imageEl.OriginWidth;
28126 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28127 height = this.thumbEl.getHeight();
28128 this.baseScale = height / this.imageEl.OriginHeight;
28131 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28133 height = this.thumbEl.getHeight();
28134 this.baseScale = height / this.imageEl.OriginHeight;
28136 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28137 width = this.thumbEl.getWidth();
28138 this.baseScale = width / this.imageEl.OriginWidth;
28146 getScaleLevel : function()
28148 return this.baseScale * Math.pow(1.1, this.scale);
28151 onTouchStart : function(e)
28153 if(!this.canvasLoaded){
28154 this.beforeSelectFile(e);
28158 var touches = e.browserEvent.touches;
28164 if(touches.length == 1){
28165 this.onMouseDown(e);
28169 if(touches.length != 2){
28175 for(var i = 0, finger; finger = touches[i]; i++){
28176 coords.push(finger.pageX, finger.pageY);
28179 var x = Math.pow(coords[0] - coords[2], 2);
28180 var y = Math.pow(coords[1] - coords[3], 2);
28182 this.startDistance = Math.sqrt(x + y);
28184 this.startScale = this.scale;
28186 this.pinching = true;
28187 this.dragable = false;
28191 onTouchMove : function(e)
28193 if(!this.pinching && !this.dragable){
28197 var touches = e.browserEvent.touches;
28204 this.onMouseMove(e);
28210 for(var i = 0, finger; finger = touches[i]; i++){
28211 coords.push(finger.pageX, finger.pageY);
28214 var x = Math.pow(coords[0] - coords[2], 2);
28215 var y = Math.pow(coords[1] - coords[3], 2);
28217 this.endDistance = Math.sqrt(x + y);
28219 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28221 if(!this.zoomable()){
28222 this.scale = this.startScale;
28230 onTouchEnd : function(e)
28232 this.pinching = false;
28233 this.dragable = false;
28237 process : function(file, crop)
28240 this.maskEl.mask(this.loadingText);
28243 this.xhr = new XMLHttpRequest();
28245 file.xhr = this.xhr;
28247 this.xhr.open(this.method, this.url, true);
28250 "Accept": "application/json",
28251 "Cache-Control": "no-cache",
28252 "X-Requested-With": "XMLHttpRequest"
28255 for (var headerName in headers) {
28256 var headerValue = headers[headerName];
28258 this.xhr.setRequestHeader(headerName, headerValue);
28264 this.xhr.onload = function()
28266 _this.xhrOnLoad(_this.xhr);
28269 this.xhr.onerror = function()
28271 _this.xhrOnError(_this.xhr);
28274 var formData = new FormData();
28276 formData.append('returnHTML', 'NO');
28279 formData.append('crop', crop);
28282 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28283 formData.append(this.paramName, file, file.name);
28286 if(typeof(file.filename) != 'undefined'){
28287 formData.append('filename', file.filename);
28290 if(typeof(file.mimetype) != 'undefined'){
28291 formData.append('mimetype', file.mimetype);
28294 if(this.fireEvent('arrange', this, formData) != false){
28295 this.xhr.send(formData);
28299 xhrOnLoad : function(xhr)
28302 this.maskEl.unmask();
28305 if (xhr.readyState !== 4) {
28306 this.fireEvent('exception', this, xhr);
28310 var response = Roo.decode(xhr.responseText);
28312 if(!response.success){
28313 this.fireEvent('exception', this, xhr);
28317 var response = Roo.decode(xhr.responseText);
28319 this.fireEvent('upload', this, response);
28323 xhrOnError : function()
28326 this.maskEl.unmask();
28329 Roo.log('xhr on error');
28331 var response = Roo.decode(xhr.responseText);
28337 prepare : function(file)
28340 this.maskEl.mask(this.loadingText);
28346 if(typeof(file) === 'string'){
28347 this.loadCanvas(file);
28351 if(!file || !this.urlAPI){
28356 this.cropType = file.type;
28360 if(this.fireEvent('prepare', this, this.file) != false){
28362 var reader = new FileReader();
28364 reader.onload = function (e) {
28365 if (e.target.error) {
28366 Roo.log(e.target.error);
28370 var buffer = e.target.result,
28371 dataView = new DataView(buffer),
28373 maxOffset = dataView.byteLength - 4,
28377 if (dataView.getUint16(0) === 0xffd8) {
28378 while (offset < maxOffset) {
28379 markerBytes = dataView.getUint16(offset);
28381 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28382 markerLength = dataView.getUint16(offset + 2) + 2;
28383 if (offset + markerLength > dataView.byteLength) {
28384 Roo.log('Invalid meta data: Invalid segment size.');
28388 if(markerBytes == 0xffe1){
28389 _this.parseExifData(
28396 offset += markerLength;
28406 var url = _this.urlAPI.createObjectURL(_this.file);
28408 _this.loadCanvas(url);
28413 reader.readAsArrayBuffer(this.file);
28419 parseExifData : function(dataView, offset, length)
28421 var tiffOffset = offset + 10,
28425 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28426 // No Exif data, might be XMP data instead
28430 // Check for the ASCII code for "Exif" (0x45786966):
28431 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28432 // No Exif data, might be XMP data instead
28435 if (tiffOffset + 8 > dataView.byteLength) {
28436 Roo.log('Invalid Exif data: Invalid segment size.');
28439 // Check for the two null bytes:
28440 if (dataView.getUint16(offset + 8) !== 0x0000) {
28441 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28444 // Check the byte alignment:
28445 switch (dataView.getUint16(tiffOffset)) {
28447 littleEndian = true;
28450 littleEndian = false;
28453 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28456 // Check for the TIFF tag marker (0x002A):
28457 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28458 Roo.log('Invalid Exif data: Missing TIFF marker.');
28461 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28462 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28464 this.parseExifTags(
28467 tiffOffset + dirOffset,
28472 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28477 if (dirOffset + 6 > dataView.byteLength) {
28478 Roo.log('Invalid Exif data: Invalid directory offset.');
28481 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28482 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28483 if (dirEndOffset + 4 > dataView.byteLength) {
28484 Roo.log('Invalid Exif data: Invalid directory size.');
28487 for (i = 0; i < tagsNumber; i += 1) {
28491 dirOffset + 2 + 12 * i, // tag offset
28495 // Return the offset to the next directory:
28496 return dataView.getUint32(dirEndOffset, littleEndian);
28499 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28501 var tag = dataView.getUint16(offset, littleEndian);
28503 this.exif[tag] = this.getExifValue(
28507 dataView.getUint16(offset + 2, littleEndian), // tag type
28508 dataView.getUint32(offset + 4, littleEndian), // tag length
28513 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28515 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28524 Roo.log('Invalid Exif data: Invalid tag type.');
28528 tagSize = tagType.size * length;
28529 // Determine if the value is contained in the dataOffset bytes,
28530 // or if the value at the dataOffset is a pointer to the actual data:
28531 dataOffset = tagSize > 4 ?
28532 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28533 if (dataOffset + tagSize > dataView.byteLength) {
28534 Roo.log('Invalid Exif data: Invalid data offset.');
28537 if (length === 1) {
28538 return tagType.getValue(dataView, dataOffset, littleEndian);
28541 for (i = 0; i < length; i += 1) {
28542 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28545 if (tagType.ascii) {
28547 // Concatenate the chars:
28548 for (i = 0; i < values.length; i += 1) {
28550 // Ignore the terminating NULL byte(s):
28551 if (c === '\u0000') {
28563 Roo.apply(Roo.bootstrap.UploadCropbox, {
28565 'Orientation': 0x0112
28569 1: 0, //'top-left',
28571 3: 180, //'bottom-right',
28572 // 4: 'bottom-left',
28574 6: 90, //'right-top',
28575 // 7: 'right-bottom',
28576 8: 270 //'left-bottom'
28580 // byte, 8-bit unsigned int:
28582 getValue: function (dataView, dataOffset) {
28583 return dataView.getUint8(dataOffset);
28587 // ascii, 8-bit byte:
28589 getValue: function (dataView, dataOffset) {
28590 return String.fromCharCode(dataView.getUint8(dataOffset));
28595 // short, 16 bit int:
28597 getValue: function (dataView, dataOffset, littleEndian) {
28598 return dataView.getUint16(dataOffset, littleEndian);
28602 // long, 32 bit int:
28604 getValue: function (dataView, dataOffset, littleEndian) {
28605 return dataView.getUint32(dataOffset, littleEndian);
28609 // rational = two long values, first is numerator, second is denominator:
28611 getValue: function (dataView, dataOffset, littleEndian) {
28612 return dataView.getUint32(dataOffset, littleEndian) /
28613 dataView.getUint32(dataOffset + 4, littleEndian);
28617 // slong, 32 bit signed int:
28619 getValue: function (dataView, dataOffset, littleEndian) {
28620 return dataView.getInt32(dataOffset, littleEndian);
28624 // srational, two slongs, first is numerator, second is denominator:
28626 getValue: function (dataView, dataOffset, littleEndian) {
28627 return dataView.getInt32(dataOffset, littleEndian) /
28628 dataView.getInt32(dataOffset + 4, littleEndian);
28638 cls : 'btn-group roo-upload-cropbox-rotate-left',
28639 action : 'rotate-left',
28643 cls : 'btn btn-default',
28644 html : '<i class="fa fa-undo"></i>'
28650 cls : 'btn-group roo-upload-cropbox-picture',
28651 action : 'picture',
28655 cls : 'btn btn-default',
28656 html : '<i class="fa fa-picture-o"></i>'
28662 cls : 'btn-group roo-upload-cropbox-rotate-right',
28663 action : 'rotate-right',
28667 cls : 'btn btn-default',
28668 html : '<i class="fa fa-repeat"></i>'
28676 cls : 'btn-group roo-upload-cropbox-rotate-left',
28677 action : 'rotate-left',
28681 cls : 'btn btn-default',
28682 html : '<i class="fa fa-undo"></i>'
28688 cls : 'btn-group roo-upload-cropbox-download',
28689 action : 'download',
28693 cls : 'btn btn-default',
28694 html : '<i class="fa fa-download"></i>'
28700 cls : 'btn-group roo-upload-cropbox-crop',
28705 cls : 'btn btn-default',
28706 html : '<i class="fa fa-crop"></i>'
28712 cls : 'btn-group roo-upload-cropbox-trash',
28717 cls : 'btn btn-default',
28718 html : '<i class="fa fa-trash"></i>'
28724 cls : 'btn-group roo-upload-cropbox-rotate-right',
28725 action : 'rotate-right',
28729 cls : 'btn btn-default',
28730 html : '<i class="fa fa-repeat"></i>'
28738 cls : 'btn-group roo-upload-cropbox-rotate-left',
28739 action : 'rotate-left',
28743 cls : 'btn btn-default',
28744 html : '<i class="fa fa-undo"></i>'
28750 cls : 'btn-group roo-upload-cropbox-rotate-right',
28751 action : 'rotate-right',
28755 cls : 'btn btn-default',
28756 html : '<i class="fa fa-repeat"></i>'
28769 * @class Roo.bootstrap.DocumentManager
28770 * @extends Roo.bootstrap.Component
28771 * Bootstrap DocumentManager class
28772 * @cfg {String} paramName default 'imageUpload'
28773 * @cfg {String} toolTipName default 'filename'
28774 * @cfg {String} method default POST
28775 * @cfg {String} url action url
28776 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28777 * @cfg {Boolean} multiple multiple upload default true
28778 * @cfg {Number} thumbSize default 300
28779 * @cfg {String} fieldLabel
28780 * @cfg {Number} labelWidth default 4
28781 * @cfg {String} labelAlign (left|top) default left
28782 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28783 * @cfg {Number} labellg set the width of label (1-12)
28784 * @cfg {Number} labelmd set the width of label (1-12)
28785 * @cfg {Number} labelsm set the width of label (1-12)
28786 * @cfg {Number} labelxs set the width of label (1-12)
28789 * Create a new DocumentManager
28790 * @param {Object} config The config object
28793 Roo.bootstrap.DocumentManager = function(config){
28794 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28797 this.delegates = [];
28802 * Fire when initial the DocumentManager
28803 * @param {Roo.bootstrap.DocumentManager} this
28808 * inspect selected file
28809 * @param {Roo.bootstrap.DocumentManager} this
28810 * @param {File} file
28815 * Fire when xhr load exception
28816 * @param {Roo.bootstrap.DocumentManager} this
28817 * @param {XMLHttpRequest} xhr
28819 "exception" : true,
28821 * @event afterupload
28822 * Fire when xhr load exception
28823 * @param {Roo.bootstrap.DocumentManager} this
28824 * @param {XMLHttpRequest} xhr
28826 "afterupload" : true,
28829 * prepare the form data
28830 * @param {Roo.bootstrap.DocumentManager} this
28831 * @param {Object} formData
28836 * Fire when remove the file
28837 * @param {Roo.bootstrap.DocumentManager} this
28838 * @param {Object} file
28843 * Fire after refresh the file
28844 * @param {Roo.bootstrap.DocumentManager} this
28849 * Fire after click the image
28850 * @param {Roo.bootstrap.DocumentManager} this
28851 * @param {Object} file
28856 * Fire when upload a image and editable set to true
28857 * @param {Roo.bootstrap.DocumentManager} this
28858 * @param {Object} file
28862 * @event beforeselectfile
28863 * Fire before select file
28864 * @param {Roo.bootstrap.DocumentManager} this
28866 "beforeselectfile" : true,
28869 * Fire before process file
28870 * @param {Roo.bootstrap.DocumentManager} this
28871 * @param {Object} file
28875 * @event previewrendered
28876 * Fire when preview rendered
28877 * @param {Roo.bootstrap.DocumentManager} this
28878 * @param {Object} file
28880 "previewrendered" : true,
28883 "previewResize" : true
28888 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28897 paramName : 'imageUpload',
28898 toolTipName : 'filename',
28901 labelAlign : 'left',
28911 getAutoCreate : function()
28913 var managerWidget = {
28915 cls : 'roo-document-manager',
28919 cls : 'roo-document-manager-selector',
28924 cls : 'roo-document-manager-uploader',
28928 cls : 'roo-document-manager-upload-btn',
28929 html : '<i class="fa fa-plus"></i>'
28940 cls : 'column col-md-12',
28945 if(this.fieldLabel.length){
28950 cls : 'column col-md-12',
28951 html : this.fieldLabel
28955 cls : 'column col-md-12',
28960 if(this.labelAlign == 'left'){
28965 html : this.fieldLabel
28974 if(this.labelWidth > 12){
28975 content[0].style = "width: " + this.labelWidth + 'px';
28978 if(this.labelWidth < 13 && this.labelmd == 0){
28979 this.labelmd = this.labelWidth;
28982 if(this.labellg > 0){
28983 content[0].cls += ' col-lg-' + this.labellg;
28984 content[1].cls += ' col-lg-' + (12 - this.labellg);
28987 if(this.labelmd > 0){
28988 content[0].cls += ' col-md-' + this.labelmd;
28989 content[1].cls += ' col-md-' + (12 - this.labelmd);
28992 if(this.labelsm > 0){
28993 content[0].cls += ' col-sm-' + this.labelsm;
28994 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28997 if(this.labelxs > 0){
28998 content[0].cls += ' col-xs-' + this.labelxs;
28999 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29007 cls : 'row clearfix',
29015 initEvents : function()
29017 this.managerEl = this.el.select('.roo-document-manager', true).first();
29018 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29020 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29021 this.selectorEl.hide();
29024 this.selectorEl.attr('multiple', 'multiple');
29027 this.selectorEl.on('change', this.onFileSelected, this);
29029 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29030 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29032 this.uploader.on('click', this.onUploaderClick, this);
29034 this.renderProgressDialog();
29038 window.addEventListener("resize", function() { _this.refresh(); } );
29040 this.fireEvent('initial', this);
29043 renderProgressDialog : function()
29047 this.progressDialog = new Roo.bootstrap.Modal({
29048 cls : 'roo-document-manager-progress-dialog',
29049 allow_close : false,
29059 btnclick : function() {
29060 _this.uploadCancel();
29066 this.progressDialog.render(Roo.get(document.body));
29068 this.progress = new Roo.bootstrap.Progress({
29069 cls : 'roo-document-manager-progress',
29074 this.progress.render(this.progressDialog.getChildContainer());
29076 this.progressBar = new Roo.bootstrap.ProgressBar({
29077 cls : 'roo-document-manager-progress-bar',
29080 aria_valuemax : 12,
29084 this.progressBar.render(this.progress.getChildContainer());
29087 onUploaderClick : function(e)
29089 e.preventDefault();
29091 if(this.fireEvent('beforeselectfile', this) != false){
29092 this.selectorEl.dom.click();
29097 onFileSelected : function(e)
29099 e.preventDefault();
29101 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29105 Roo.each(this.selectorEl.dom.files, function(file){
29106 if(this.fireEvent('inspect', this, file) != false){
29107 this.files.push(file);
29117 this.selectorEl.dom.value = '';
29119 if(!this.files || !this.files.length){
29123 if(this.boxes > 0 && this.files.length > this.boxes){
29124 this.files = this.files.slice(0, this.boxes);
29127 this.uploader.show();
29129 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29130 this.uploader.hide();
29139 Roo.each(this.files, function(file){
29141 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29142 var f = this.renderPreview(file);
29147 if(file.type.indexOf('image') != -1){
29148 this.delegates.push(
29150 _this.process(file);
29151 }).createDelegate(this)
29159 _this.process(file);
29160 }).createDelegate(this)
29165 this.files = files;
29167 this.delegates = this.delegates.concat(docs);
29169 if(!this.delegates.length){
29174 this.progressBar.aria_valuemax = this.delegates.length;
29181 arrange : function()
29183 if(!this.delegates.length){
29184 this.progressDialog.hide();
29189 var delegate = this.delegates.shift();
29191 this.progressDialog.show();
29193 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29195 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29200 refresh : function()
29202 this.uploader.show();
29204 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29205 this.uploader.hide();
29208 Roo.isTouch ? this.closable(false) : this.closable(true);
29210 this.fireEvent('refresh', this);
29213 onRemove : function(e, el, o)
29215 e.preventDefault();
29217 this.fireEvent('remove', this, o);
29221 remove : function(o)
29225 Roo.each(this.files, function(file){
29226 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29235 this.files = files;
29242 Roo.each(this.files, function(file){
29247 file.target.remove();
29256 onClick : function(e, el, o)
29258 e.preventDefault();
29260 this.fireEvent('click', this, o);
29264 closable : function(closable)
29266 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29268 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29280 xhrOnLoad : function(xhr)
29282 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29286 if (xhr.readyState !== 4) {
29288 this.fireEvent('exception', this, xhr);
29292 var response = Roo.decode(xhr.responseText);
29294 if(!response.success){
29296 this.fireEvent('exception', this, xhr);
29300 var file = this.renderPreview(response.data);
29302 this.files.push(file);
29306 this.fireEvent('afterupload', this, xhr);
29310 xhrOnError : function(xhr)
29312 Roo.log('xhr on error');
29314 var response = Roo.decode(xhr.responseText);
29321 process : function(file)
29323 if(this.fireEvent('process', this, file) !== false){
29324 if(this.editable && file.type.indexOf('image') != -1){
29325 this.fireEvent('edit', this, file);
29329 this.uploadStart(file, false);
29336 uploadStart : function(file, crop)
29338 this.xhr = new XMLHttpRequest();
29340 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29345 file.xhr = this.xhr;
29347 this.managerEl.createChild({
29349 cls : 'roo-document-manager-loading',
29353 tooltip : file.name,
29354 cls : 'roo-document-manager-thumb',
29355 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29361 this.xhr.open(this.method, this.url, true);
29364 "Accept": "application/json",
29365 "Cache-Control": "no-cache",
29366 "X-Requested-With": "XMLHttpRequest"
29369 for (var headerName in headers) {
29370 var headerValue = headers[headerName];
29372 this.xhr.setRequestHeader(headerName, headerValue);
29378 this.xhr.onload = function()
29380 _this.xhrOnLoad(_this.xhr);
29383 this.xhr.onerror = function()
29385 _this.xhrOnError(_this.xhr);
29388 var formData = new FormData();
29390 formData.append('returnHTML', 'NO');
29393 formData.append('crop', crop);
29396 formData.append(this.paramName, file, file.name);
29403 if(this.fireEvent('prepare', this, formData, options) != false){
29405 if(options.manually){
29409 this.xhr.send(formData);
29413 this.uploadCancel();
29416 uploadCancel : function()
29422 this.delegates = [];
29424 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29431 renderPreview : function(file)
29433 if(typeof(file.target) != 'undefined' && file.target){
29437 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29439 var previewEl = this.managerEl.createChild({
29441 cls : 'roo-document-manager-preview',
29445 tooltip : file[this.toolTipName],
29446 cls : 'roo-document-manager-thumb',
29447 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29452 html : '<i class="fa fa-times-circle"></i>'
29457 var close = previewEl.select('button.close', true).first();
29459 close.on('click', this.onRemove, this, file);
29461 file.target = previewEl;
29463 var image = previewEl.select('img', true).first();
29467 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29469 image.on('click', this.onClick, this, file);
29471 this.fireEvent('previewrendered', this, file);
29477 onPreviewLoad : function(file, image)
29479 if(typeof(file.target) == 'undefined' || !file.target){
29483 var width = image.dom.naturalWidth || image.dom.width;
29484 var height = image.dom.naturalHeight || image.dom.height;
29486 if(!this.previewResize) {
29490 if(width > height){
29491 file.target.addClass('wide');
29495 file.target.addClass('tall');
29500 uploadFromSource : function(file, crop)
29502 this.xhr = new XMLHttpRequest();
29504 this.managerEl.createChild({
29506 cls : 'roo-document-manager-loading',
29510 tooltip : file.name,
29511 cls : 'roo-document-manager-thumb',
29512 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29518 this.xhr.open(this.method, this.url, true);
29521 "Accept": "application/json",
29522 "Cache-Control": "no-cache",
29523 "X-Requested-With": "XMLHttpRequest"
29526 for (var headerName in headers) {
29527 var headerValue = headers[headerName];
29529 this.xhr.setRequestHeader(headerName, headerValue);
29535 this.xhr.onload = function()
29537 _this.xhrOnLoad(_this.xhr);
29540 this.xhr.onerror = function()
29542 _this.xhrOnError(_this.xhr);
29545 var formData = new FormData();
29547 formData.append('returnHTML', 'NO');
29549 formData.append('crop', crop);
29551 if(typeof(file.filename) != 'undefined'){
29552 formData.append('filename', file.filename);
29555 if(typeof(file.mimetype) != 'undefined'){
29556 formData.append('mimetype', file.mimetype);
29561 if(this.fireEvent('prepare', this, formData) != false){
29562 this.xhr.send(formData);
29572 * @class Roo.bootstrap.DocumentViewer
29573 * @extends Roo.bootstrap.Component
29574 * Bootstrap DocumentViewer class
29575 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29576 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29579 * Create a new DocumentViewer
29580 * @param {Object} config The config object
29583 Roo.bootstrap.DocumentViewer = function(config){
29584 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29589 * Fire after initEvent
29590 * @param {Roo.bootstrap.DocumentViewer} this
29596 * @param {Roo.bootstrap.DocumentViewer} this
29601 * Fire after download button
29602 * @param {Roo.bootstrap.DocumentViewer} this
29607 * Fire after trash button
29608 * @param {Roo.bootstrap.DocumentViewer} this
29615 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29617 showDownload : true,
29621 getAutoCreate : function()
29625 cls : 'roo-document-viewer',
29629 cls : 'roo-document-viewer-body',
29633 cls : 'roo-document-viewer-thumb',
29637 cls : 'roo-document-viewer-image'
29645 cls : 'roo-document-viewer-footer',
29648 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29652 cls : 'btn-group roo-document-viewer-download',
29656 cls : 'btn btn-default',
29657 html : '<i class="fa fa-download"></i>'
29663 cls : 'btn-group roo-document-viewer-trash',
29667 cls : 'btn btn-default',
29668 html : '<i class="fa fa-trash"></i>'
29681 initEvents : function()
29683 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29684 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29686 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29687 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29689 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29690 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29692 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29693 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29695 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29696 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29698 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29699 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29701 this.bodyEl.on('click', this.onClick, this);
29702 this.downloadBtn.on('click', this.onDownload, this);
29703 this.trashBtn.on('click', this.onTrash, this);
29705 this.downloadBtn.hide();
29706 this.trashBtn.hide();
29708 if(this.showDownload){
29709 this.downloadBtn.show();
29712 if(this.showTrash){
29713 this.trashBtn.show();
29716 if(!this.showDownload && !this.showTrash) {
29717 this.footerEl.hide();
29722 initial : function()
29724 this.fireEvent('initial', this);
29728 onClick : function(e)
29730 e.preventDefault();
29732 this.fireEvent('click', this);
29735 onDownload : function(e)
29737 e.preventDefault();
29739 this.fireEvent('download', this);
29742 onTrash : function(e)
29744 e.preventDefault();
29746 this.fireEvent('trash', this);
29758 * @class Roo.bootstrap.NavProgressBar
29759 * @extends Roo.bootstrap.Component
29760 * Bootstrap NavProgressBar class
29763 * Create a new nav progress bar
29764 * @param {Object} config The config object
29767 Roo.bootstrap.NavProgressBar = function(config){
29768 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29770 this.bullets = this.bullets || [];
29772 // Roo.bootstrap.NavProgressBar.register(this);
29776 * Fires when the active item changes
29777 * @param {Roo.bootstrap.NavProgressBar} this
29778 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29779 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29786 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29791 getAutoCreate : function()
29793 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29797 cls : 'roo-navigation-bar-group',
29801 cls : 'roo-navigation-top-bar'
29805 cls : 'roo-navigation-bullets-bar',
29809 cls : 'roo-navigation-bar'
29816 cls : 'roo-navigation-bottom-bar'
29826 initEvents: function()
29831 onRender : function(ct, position)
29833 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29835 if(this.bullets.length){
29836 Roo.each(this.bullets, function(b){
29845 addItem : function(cfg)
29847 var item = new Roo.bootstrap.NavProgressItem(cfg);
29849 item.parentId = this.id;
29850 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29853 var top = new Roo.bootstrap.Element({
29855 cls : 'roo-navigation-bar-text'
29858 var bottom = new Roo.bootstrap.Element({
29860 cls : 'roo-navigation-bar-text'
29863 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29864 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29866 var topText = new Roo.bootstrap.Element({
29868 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29871 var bottomText = new Roo.bootstrap.Element({
29873 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29876 topText.onRender(top.el, null);
29877 bottomText.onRender(bottom.el, null);
29880 item.bottomEl = bottom;
29883 this.barItems.push(item);
29888 getActive : function()
29890 var active = false;
29892 Roo.each(this.barItems, function(v){
29894 if (!v.isActive()) {
29906 setActiveItem : function(item)
29910 Roo.each(this.barItems, function(v){
29911 if (v.rid == item.rid) {
29915 if (v.isActive()) {
29916 v.setActive(false);
29921 item.setActive(true);
29923 this.fireEvent('changed', this, item, prev);
29926 getBarItem: function(rid)
29930 Roo.each(this.barItems, function(e) {
29931 if (e.rid != rid) {
29942 indexOfItem : function(item)
29946 Roo.each(this.barItems, function(v, i){
29948 if (v.rid != item.rid) {
29959 setActiveNext : function()
29961 var i = this.indexOfItem(this.getActive());
29963 if (i > this.barItems.length) {
29967 this.setActiveItem(this.barItems[i+1]);
29970 setActivePrev : function()
29972 var i = this.indexOfItem(this.getActive());
29978 this.setActiveItem(this.barItems[i-1]);
29981 format : function()
29983 if(!this.barItems.length){
29987 var width = 100 / this.barItems.length;
29989 Roo.each(this.barItems, function(i){
29990 i.el.setStyle('width', width + '%');
29991 i.topEl.el.setStyle('width', width + '%');
29992 i.bottomEl.el.setStyle('width', width + '%');
30001 * Nav Progress Item
30006 * @class Roo.bootstrap.NavProgressItem
30007 * @extends Roo.bootstrap.Component
30008 * Bootstrap NavProgressItem class
30009 * @cfg {String} rid the reference id
30010 * @cfg {Boolean} active (true|false) Is item active default false
30011 * @cfg {Boolean} disabled (true|false) Is item active default false
30012 * @cfg {String} html
30013 * @cfg {String} position (top|bottom) text position default bottom
30014 * @cfg {String} icon show icon instead of number
30017 * Create a new NavProgressItem
30018 * @param {Object} config The config object
30020 Roo.bootstrap.NavProgressItem = function(config){
30021 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30026 * The raw click event for the entire grid.
30027 * @param {Roo.bootstrap.NavProgressItem} this
30028 * @param {Roo.EventObject} e
30035 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30041 position : 'bottom',
30044 getAutoCreate : function()
30046 var iconCls = 'roo-navigation-bar-item-icon';
30048 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30052 cls: 'roo-navigation-bar-item',
30062 cfg.cls += ' active';
30065 cfg.cls += ' disabled';
30071 disable : function()
30073 this.setDisabled(true);
30076 enable : function()
30078 this.setDisabled(false);
30081 initEvents: function()
30083 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30085 this.iconEl.on('click', this.onClick, this);
30088 onClick : function(e)
30090 e.preventDefault();
30096 if(this.fireEvent('click', this, e) === false){
30100 this.parent().setActiveItem(this);
30103 isActive: function ()
30105 return this.active;
30108 setActive : function(state)
30110 if(this.active == state){
30114 this.active = state;
30117 this.el.addClass('active');
30121 this.el.removeClass('active');
30126 setDisabled : function(state)
30128 if(this.disabled == state){
30132 this.disabled = state;
30135 this.el.addClass('disabled');
30139 this.el.removeClass('disabled');
30142 tooltipEl : function()
30144 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30157 * @class Roo.bootstrap.FieldLabel
30158 * @extends Roo.bootstrap.Component
30159 * Bootstrap FieldLabel class
30160 * @cfg {String} html contents of the element
30161 * @cfg {String} tag tag of the element default label
30162 * @cfg {String} cls class of the element
30163 * @cfg {String} target label target
30164 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30165 * @cfg {String} invalidClass default "text-warning"
30166 * @cfg {String} validClass default "text-success"
30167 * @cfg {String} iconTooltip default "This field is required"
30168 * @cfg {String} indicatorpos (left|right) default left
30171 * Create a new FieldLabel
30172 * @param {Object} config The config object
30175 Roo.bootstrap.FieldLabel = function(config){
30176 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30181 * Fires after the field has been marked as invalid.
30182 * @param {Roo.form.FieldLabel} this
30183 * @param {String} msg The validation message
30188 * Fires after the field has been validated with no errors.
30189 * @param {Roo.form.FieldLabel} this
30195 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30202 invalidClass : 'has-warning',
30203 validClass : 'has-success',
30204 iconTooltip : 'This field is required',
30205 indicatorpos : 'left',
30207 getAutoCreate : function(){
30210 if (!this.allowBlank) {
30216 cls : 'roo-bootstrap-field-label ' + this.cls,
30221 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30222 tooltip : this.iconTooltip
30231 if(this.indicatorpos == 'right'){
30234 cls : 'roo-bootstrap-field-label ' + this.cls,
30243 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30244 tooltip : this.iconTooltip
30253 initEvents: function()
30255 Roo.bootstrap.Element.superclass.initEvents.call(this);
30257 this.indicator = this.indicatorEl();
30259 if(this.indicator){
30260 this.indicator.removeClass('visible');
30261 this.indicator.addClass('invisible');
30264 Roo.bootstrap.FieldLabel.register(this);
30267 indicatorEl : function()
30269 var indicator = this.el.select('i.roo-required-indicator',true).first();
30280 * Mark this field as valid
30282 markValid : function()
30284 if(this.indicator){
30285 this.indicator.removeClass('visible');
30286 this.indicator.addClass('invisible');
30289 this.el.removeClass(this.invalidClass);
30291 this.el.addClass(this.validClass);
30293 this.fireEvent('valid', this);
30297 * Mark this field as invalid
30298 * @param {String} msg The validation message
30300 markInvalid : function(msg)
30302 if(this.indicator){
30303 this.indicator.removeClass('invisible');
30304 this.indicator.addClass('visible');
30307 this.el.removeClass(this.validClass);
30309 this.el.addClass(this.invalidClass);
30311 this.fireEvent('invalid', this, msg);
30317 Roo.apply(Roo.bootstrap.FieldLabel, {
30322 * register a FieldLabel Group
30323 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30325 register : function(label)
30327 if(this.groups.hasOwnProperty(label.target)){
30331 this.groups[label.target] = label;
30335 * fetch a FieldLabel Group based on the target
30336 * @param {string} target
30337 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30339 get: function(target) {
30340 if (typeof(this.groups[target]) == 'undefined') {
30344 return this.groups[target] ;
30353 * page DateSplitField.
30359 * @class Roo.bootstrap.DateSplitField
30360 * @extends Roo.bootstrap.Component
30361 * Bootstrap DateSplitField class
30362 * @cfg {string} fieldLabel - the label associated
30363 * @cfg {Number} labelWidth set the width of label (0-12)
30364 * @cfg {String} labelAlign (top|left)
30365 * @cfg {Boolean} dayAllowBlank (true|false) default false
30366 * @cfg {Boolean} monthAllowBlank (true|false) default false
30367 * @cfg {Boolean} yearAllowBlank (true|false) default false
30368 * @cfg {string} dayPlaceholder
30369 * @cfg {string} monthPlaceholder
30370 * @cfg {string} yearPlaceholder
30371 * @cfg {string} dayFormat default 'd'
30372 * @cfg {string} monthFormat default 'm'
30373 * @cfg {string} yearFormat default 'Y'
30374 * @cfg {Number} labellg set the width of label (1-12)
30375 * @cfg {Number} labelmd set the width of label (1-12)
30376 * @cfg {Number} labelsm set the width of label (1-12)
30377 * @cfg {Number} labelxs set the width of label (1-12)
30381 * Create a new DateSplitField
30382 * @param {Object} config The config object
30385 Roo.bootstrap.DateSplitField = function(config){
30386 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30392 * getting the data of years
30393 * @param {Roo.bootstrap.DateSplitField} this
30394 * @param {Object} years
30399 * getting the data of days
30400 * @param {Roo.bootstrap.DateSplitField} this
30401 * @param {Object} days
30406 * Fires after the field has been marked as invalid.
30407 * @param {Roo.form.Field} this
30408 * @param {String} msg The validation message
30413 * Fires after the field has been validated with no errors.
30414 * @param {Roo.form.Field} this
30420 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30423 labelAlign : 'top',
30425 dayAllowBlank : false,
30426 monthAllowBlank : false,
30427 yearAllowBlank : false,
30428 dayPlaceholder : '',
30429 monthPlaceholder : '',
30430 yearPlaceholder : '',
30434 isFormField : true,
30440 getAutoCreate : function()
30444 cls : 'row roo-date-split-field-group',
30449 cls : 'form-hidden-field roo-date-split-field-group-value',
30455 var labelCls = 'col-md-12';
30456 var contentCls = 'col-md-4';
30458 if(this.fieldLabel){
30462 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30466 html : this.fieldLabel
30471 if(this.labelAlign == 'left'){
30473 if(this.labelWidth > 12){
30474 label.style = "width: " + this.labelWidth + 'px';
30477 if(this.labelWidth < 13 && this.labelmd == 0){
30478 this.labelmd = this.labelWidth;
30481 if(this.labellg > 0){
30482 labelCls = ' col-lg-' + this.labellg;
30483 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30486 if(this.labelmd > 0){
30487 labelCls = ' col-md-' + this.labelmd;
30488 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30491 if(this.labelsm > 0){
30492 labelCls = ' col-sm-' + this.labelsm;
30493 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30496 if(this.labelxs > 0){
30497 labelCls = ' col-xs-' + this.labelxs;
30498 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30502 label.cls += ' ' + labelCls;
30504 cfg.cn.push(label);
30507 Roo.each(['day', 'month', 'year'], function(t){
30510 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30517 inputEl: function ()
30519 return this.el.select('.roo-date-split-field-group-value', true).first();
30522 onRender : function(ct, position)
30526 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30528 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30530 this.dayField = new Roo.bootstrap.ComboBox({
30531 allowBlank : this.dayAllowBlank,
30532 alwaysQuery : true,
30533 displayField : 'value',
30536 forceSelection : true,
30538 placeholder : this.dayPlaceholder,
30539 selectOnFocus : true,
30540 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30541 triggerAction : 'all',
30543 valueField : 'value',
30544 store : new Roo.data.SimpleStore({
30545 data : (function() {
30547 _this.fireEvent('days', _this, days);
30550 fields : [ 'value' ]
30553 select : function (_self, record, index)
30555 _this.setValue(_this.getValue());
30560 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30562 this.monthField = new Roo.bootstrap.MonthField({
30563 after : '<i class=\"fa fa-calendar\"></i>',
30564 allowBlank : this.monthAllowBlank,
30565 placeholder : this.monthPlaceholder,
30568 render : function (_self)
30570 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30571 e.preventDefault();
30575 select : function (_self, oldvalue, newvalue)
30577 _this.setValue(_this.getValue());
30582 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30584 this.yearField = new Roo.bootstrap.ComboBox({
30585 allowBlank : this.yearAllowBlank,
30586 alwaysQuery : true,
30587 displayField : 'value',
30590 forceSelection : true,
30592 placeholder : this.yearPlaceholder,
30593 selectOnFocus : true,
30594 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30595 triggerAction : 'all',
30597 valueField : 'value',
30598 store : new Roo.data.SimpleStore({
30599 data : (function() {
30601 _this.fireEvent('years', _this, years);
30604 fields : [ 'value' ]
30607 select : function (_self, record, index)
30609 _this.setValue(_this.getValue());
30614 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30617 setValue : function(v, format)
30619 this.inputEl.dom.value = v;
30621 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30623 var d = Date.parseDate(v, f);
30630 this.setDay(d.format(this.dayFormat));
30631 this.setMonth(d.format(this.monthFormat));
30632 this.setYear(d.format(this.yearFormat));
30639 setDay : function(v)
30641 this.dayField.setValue(v);
30642 this.inputEl.dom.value = this.getValue();
30647 setMonth : function(v)
30649 this.monthField.setValue(v, true);
30650 this.inputEl.dom.value = this.getValue();
30655 setYear : function(v)
30657 this.yearField.setValue(v);
30658 this.inputEl.dom.value = this.getValue();
30663 getDay : function()
30665 return this.dayField.getValue();
30668 getMonth : function()
30670 return this.monthField.getValue();
30673 getYear : function()
30675 return this.yearField.getValue();
30678 getValue : function()
30680 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30682 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30692 this.inputEl.dom.value = '';
30697 validate : function()
30699 var d = this.dayField.validate();
30700 var m = this.monthField.validate();
30701 var y = this.yearField.validate();
30706 (!this.dayAllowBlank && !d) ||
30707 (!this.monthAllowBlank && !m) ||
30708 (!this.yearAllowBlank && !y)
30713 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30722 this.markInvalid();
30727 markValid : function()
30730 var label = this.el.select('label', true).first();
30731 var icon = this.el.select('i.fa-star', true).first();
30737 this.fireEvent('valid', this);
30741 * Mark this field as invalid
30742 * @param {String} msg The validation message
30744 markInvalid : function(msg)
30747 var label = this.el.select('label', true).first();
30748 var icon = this.el.select('i.fa-star', true).first();
30750 if(label && !icon){
30751 this.el.select('.roo-date-split-field-label', true).createChild({
30753 cls : 'text-danger fa fa-lg fa-star',
30754 tooltip : 'This field is required',
30755 style : 'margin-right:5px;'
30759 this.fireEvent('invalid', this, msg);
30762 clearInvalid : function()
30764 var label = this.el.select('label', true).first();
30765 var icon = this.el.select('i.fa-star', true).first();
30771 this.fireEvent('valid', this);
30774 getName: function()
30784 * http://masonry.desandro.com
30786 * The idea is to render all the bricks based on vertical width...
30788 * The original code extends 'outlayer' - we might need to use that....
30794 * @class Roo.bootstrap.LayoutMasonry
30795 * @extends Roo.bootstrap.Component
30796 * Bootstrap Layout Masonry class
30799 * Create a new Element
30800 * @param {Object} config The config object
30803 Roo.bootstrap.LayoutMasonry = function(config){
30805 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30809 Roo.bootstrap.LayoutMasonry.register(this);
30815 * Fire after layout the items
30816 * @param {Roo.bootstrap.LayoutMasonry} this
30817 * @param {Roo.EventObject} e
30824 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30827 * @cfg {Boolean} isLayoutInstant = no animation?
30829 isLayoutInstant : false, // needed?
30832 * @cfg {Number} boxWidth width of the columns
30837 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30842 * @cfg {Number} padWidth padding below box..
30847 * @cfg {Number} gutter gutter width..
30852 * @cfg {Number} maxCols maximum number of columns
30858 * @cfg {Boolean} isAutoInitial defalut true
30860 isAutoInitial : true,
30865 * @cfg {Boolean} isHorizontal defalut false
30867 isHorizontal : false,
30869 currentSize : null,
30875 bricks: null, //CompositeElement
30879 _isLayoutInited : false,
30881 // isAlternative : false, // only use for vertical layout...
30884 * @cfg {Number} alternativePadWidth padding below box..
30886 alternativePadWidth : 50,
30888 selectedBrick : [],
30890 getAutoCreate : function(){
30892 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30896 cls: 'blog-masonary-wrapper ' + this.cls,
30898 cls : 'mas-boxes masonary'
30905 getChildContainer: function( )
30907 if (this.boxesEl) {
30908 return this.boxesEl;
30911 this.boxesEl = this.el.select('.mas-boxes').first();
30913 return this.boxesEl;
30917 initEvents : function()
30921 if(this.isAutoInitial){
30922 Roo.log('hook children rendered');
30923 this.on('childrenrendered', function() {
30924 Roo.log('children rendered');
30930 initial : function()
30932 this.selectedBrick = [];
30934 this.currentSize = this.el.getBox(true);
30936 Roo.EventManager.onWindowResize(this.resize, this);
30938 if(!this.isAutoInitial){
30946 //this.layout.defer(500,this);
30950 resize : function()
30952 var cs = this.el.getBox(true);
30955 this.currentSize.width == cs.width &&
30956 this.currentSize.x == cs.x &&
30957 this.currentSize.height == cs.height &&
30958 this.currentSize.y == cs.y
30960 Roo.log("no change in with or X or Y");
30964 this.currentSize = cs;
30970 layout : function()
30972 this._resetLayout();
30974 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30976 this.layoutItems( isInstant );
30978 this._isLayoutInited = true;
30980 this.fireEvent('layout', this);
30984 _resetLayout : function()
30986 if(this.isHorizontal){
30987 this.horizontalMeasureColumns();
30991 this.verticalMeasureColumns();
30995 verticalMeasureColumns : function()
30997 this.getContainerWidth();
30999 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31000 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31004 var boxWidth = this.boxWidth + this.padWidth;
31006 if(this.containerWidth < this.boxWidth){
31007 boxWidth = this.containerWidth
31010 var containerWidth = this.containerWidth;
31012 var cols = Math.floor(containerWidth / boxWidth);
31014 this.cols = Math.max( cols, 1 );
31016 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31018 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31020 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31022 this.colWidth = boxWidth + avail - this.padWidth;
31024 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31025 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31028 horizontalMeasureColumns : function()
31030 this.getContainerWidth();
31032 var boxWidth = this.boxWidth;
31034 if(this.containerWidth < boxWidth){
31035 boxWidth = this.containerWidth;
31038 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31040 this.el.setHeight(boxWidth);
31044 getContainerWidth : function()
31046 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31049 layoutItems : function( isInstant )
31051 Roo.log(this.bricks);
31053 var items = Roo.apply([], this.bricks);
31055 if(this.isHorizontal){
31056 this._horizontalLayoutItems( items , isInstant );
31060 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31061 // this._verticalAlternativeLayoutItems( items , isInstant );
31065 this._verticalLayoutItems( items , isInstant );
31069 _verticalLayoutItems : function ( items , isInstant)
31071 if ( !items || !items.length ) {
31076 ['xs', 'xs', 'xs', 'tall'],
31077 ['xs', 'xs', 'tall'],
31078 ['xs', 'xs', 'sm'],
31079 ['xs', 'xs', 'xs'],
31085 ['sm', 'xs', 'xs'],
31089 ['tall', 'xs', 'xs', 'xs'],
31090 ['tall', 'xs', 'xs'],
31102 Roo.each(items, function(item, k){
31104 switch (item.size) {
31105 // these layouts take up a full box,
31116 boxes.push([item]);
31139 var filterPattern = function(box, length)
31147 var pattern = box.slice(0, length);
31151 Roo.each(pattern, function(i){
31152 format.push(i.size);
31155 Roo.each(standard, function(s){
31157 if(String(s) != String(format)){
31166 if(!match && length == 1){
31171 filterPattern(box, length - 1);
31175 queue.push(pattern);
31177 box = box.slice(length, box.length);
31179 filterPattern(box, 4);
31185 Roo.each(boxes, function(box, k){
31191 if(box.length == 1){
31196 filterPattern(box, 4);
31200 this._processVerticalLayoutQueue( queue, isInstant );
31204 // _verticalAlternativeLayoutItems : function( items , isInstant )
31206 // if ( !items || !items.length ) {
31210 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31214 _horizontalLayoutItems : function ( items , isInstant)
31216 if ( !items || !items.length || items.length < 3) {
31222 var eItems = items.slice(0, 3);
31224 items = items.slice(3, items.length);
31227 ['xs', 'xs', 'xs', 'wide'],
31228 ['xs', 'xs', 'wide'],
31229 ['xs', 'xs', 'sm'],
31230 ['xs', 'xs', 'xs'],
31236 ['sm', 'xs', 'xs'],
31240 ['wide', 'xs', 'xs', 'xs'],
31241 ['wide', 'xs', 'xs'],
31254 Roo.each(items, function(item, k){
31256 switch (item.size) {
31267 boxes.push([item]);
31291 var filterPattern = function(box, length)
31299 var pattern = box.slice(0, length);
31303 Roo.each(pattern, function(i){
31304 format.push(i.size);
31307 Roo.each(standard, function(s){
31309 if(String(s) != String(format)){
31318 if(!match && length == 1){
31323 filterPattern(box, length - 1);
31327 queue.push(pattern);
31329 box = box.slice(length, box.length);
31331 filterPattern(box, 4);
31337 Roo.each(boxes, function(box, k){
31343 if(box.length == 1){
31348 filterPattern(box, 4);
31355 var pos = this.el.getBox(true);
31359 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31361 var hit_end = false;
31363 Roo.each(queue, function(box){
31367 Roo.each(box, function(b){
31369 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31379 Roo.each(box, function(b){
31381 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31384 mx = Math.max(mx, b.x);
31388 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31392 Roo.each(box, function(b){
31394 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31408 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31411 /** Sets position of item in DOM
31412 * @param {Element} item
31413 * @param {Number} x - horizontal position
31414 * @param {Number} y - vertical position
31415 * @param {Boolean} isInstant - disables transitions
31417 _processVerticalLayoutQueue : function( queue, isInstant )
31419 var pos = this.el.getBox(true);
31424 for (var i = 0; i < this.cols; i++){
31428 Roo.each(queue, function(box, k){
31430 var col = k % this.cols;
31432 Roo.each(box, function(b,kk){
31434 b.el.position('absolute');
31436 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31437 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31439 if(b.size == 'md-left' || b.size == 'md-right'){
31440 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31441 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31444 b.el.setWidth(width);
31445 b.el.setHeight(height);
31447 b.el.select('iframe',true).setSize(width,height);
31451 for (var i = 0; i < this.cols; i++){
31453 if(maxY[i] < maxY[col]){
31458 col = Math.min(col, i);
31462 x = pos.x + col * (this.colWidth + this.padWidth);
31466 var positions = [];
31468 switch (box.length){
31470 positions = this.getVerticalOneBoxColPositions(x, y, box);
31473 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31476 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31479 positions = this.getVerticalFourBoxColPositions(x, y, box);
31485 Roo.each(box, function(b,kk){
31487 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31489 var sz = b.el.getSize();
31491 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31499 for (var i = 0; i < this.cols; i++){
31500 mY = Math.max(mY, maxY[i]);
31503 this.el.setHeight(mY - pos.y);
31507 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31509 // var pos = this.el.getBox(true);
31512 // var maxX = pos.right;
31514 // var maxHeight = 0;
31516 // Roo.each(items, function(item, k){
31520 // item.el.position('absolute');
31522 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31524 // item.el.setWidth(width);
31526 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31528 // item.el.setHeight(height);
31531 // item.el.setXY([x, y], isInstant ? false : true);
31533 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31536 // y = y + height + this.alternativePadWidth;
31538 // maxHeight = maxHeight + height + this.alternativePadWidth;
31542 // this.el.setHeight(maxHeight);
31546 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31548 var pos = this.el.getBox(true);
31553 var maxX = pos.right;
31555 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31557 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31559 Roo.each(queue, function(box, k){
31561 Roo.each(box, function(b, kk){
31563 b.el.position('absolute');
31565 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31566 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31568 if(b.size == 'md-left' || b.size == 'md-right'){
31569 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31570 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31573 b.el.setWidth(width);
31574 b.el.setHeight(height);
31582 var positions = [];
31584 switch (box.length){
31586 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31589 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31592 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31595 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31601 Roo.each(box, function(b,kk){
31603 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31605 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31613 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31615 Roo.each(eItems, function(b,k){
31617 b.size = (k == 0) ? 'sm' : 'xs';
31618 b.x = (k == 0) ? 2 : 1;
31619 b.y = (k == 0) ? 2 : 1;
31621 b.el.position('absolute');
31623 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31625 b.el.setWidth(width);
31627 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31629 b.el.setHeight(height);
31633 var positions = [];
31636 x : maxX - this.unitWidth * 2 - this.gutter,
31641 x : maxX - this.unitWidth,
31642 y : minY + (this.unitWidth + this.gutter) * 2
31646 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31650 Roo.each(eItems, function(b,k){
31652 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31658 getVerticalOneBoxColPositions : function(x, y, box)
31662 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31664 if(box[0].size == 'md-left'){
31668 if(box[0].size == 'md-right'){
31673 x : x + (this.unitWidth + this.gutter) * rand,
31680 getVerticalTwoBoxColPositions : function(x, y, box)
31684 if(box[0].size == 'xs'){
31688 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31692 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31706 x : x + (this.unitWidth + this.gutter) * 2,
31707 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31714 getVerticalThreeBoxColPositions : function(x, y, box)
31718 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31726 x : x + (this.unitWidth + this.gutter) * 1,
31731 x : x + (this.unitWidth + this.gutter) * 2,
31739 if(box[0].size == 'xs' && box[1].size == 'xs'){
31748 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31752 x : x + (this.unitWidth + this.gutter) * 1,
31766 x : x + (this.unitWidth + this.gutter) * 2,
31771 x : x + (this.unitWidth + this.gutter) * 2,
31772 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31779 getVerticalFourBoxColPositions : function(x, y, box)
31783 if(box[0].size == 'xs'){
31792 y : y + (this.unitHeight + this.gutter) * 1
31797 y : y + (this.unitHeight + this.gutter) * 2
31801 x : x + (this.unitWidth + this.gutter) * 1,
31815 x : x + (this.unitWidth + this.gutter) * 2,
31820 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31821 y : y + (this.unitHeight + this.gutter) * 1
31825 x : x + (this.unitWidth + this.gutter) * 2,
31826 y : y + (this.unitWidth + this.gutter) * 2
31833 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31837 if(box[0].size == 'md-left'){
31839 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31846 if(box[0].size == 'md-right'){
31848 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31849 y : minY + (this.unitWidth + this.gutter) * 1
31855 var rand = Math.floor(Math.random() * (4 - box[0].y));
31858 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31859 y : minY + (this.unitWidth + this.gutter) * rand
31866 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31870 if(box[0].size == 'xs'){
31873 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31878 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31879 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31887 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31892 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31893 y : minY + (this.unitWidth + this.gutter) * 2
31900 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31904 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31907 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31912 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31913 y : minY + (this.unitWidth + this.gutter) * 1
31917 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31918 y : minY + (this.unitWidth + this.gutter) * 2
31925 if(box[0].size == 'xs' && box[1].size == 'xs'){
31928 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31933 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31938 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31939 y : minY + (this.unitWidth + this.gutter) * 1
31947 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31952 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31953 y : minY + (this.unitWidth + this.gutter) * 2
31957 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31958 y : minY + (this.unitWidth + this.gutter) * 2
31965 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31969 if(box[0].size == 'xs'){
31972 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31977 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31982 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),
31987 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31988 y : minY + (this.unitWidth + this.gutter) * 1
31996 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32001 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32002 y : minY + (this.unitWidth + this.gutter) * 2
32006 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32007 y : minY + (this.unitWidth + this.gutter) * 2
32011 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),
32012 y : minY + (this.unitWidth + this.gutter) * 2
32020 * remove a Masonry Brick
32021 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32023 removeBrick : function(brick_id)
32029 for (var i = 0; i<this.bricks.length; i++) {
32030 if (this.bricks[i].id == brick_id) {
32031 this.bricks.splice(i,1);
32032 this.el.dom.removeChild(Roo.get(brick_id).dom);
32039 * adds a Masonry Brick
32040 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32042 addBrick : function(cfg)
32044 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32045 //this.register(cn);
32046 cn.parentId = this.id;
32047 cn.render(this.el);
32052 * register a Masonry Brick
32053 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32056 register : function(brick)
32058 this.bricks.push(brick);
32059 brick.masonryId = this.id;
32063 * clear all the Masonry Brick
32065 clearAll : function()
32068 //this.getChildContainer().dom.innerHTML = "";
32069 this.el.dom.innerHTML = '';
32072 getSelected : function()
32074 if (!this.selectedBrick) {
32078 return this.selectedBrick;
32082 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32086 * register a Masonry Layout
32087 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32090 register : function(layout)
32092 this.groups[layout.id] = layout;
32095 * fetch a Masonry Layout based on the masonry layout ID
32096 * @param {string} the masonry layout to add
32097 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32100 get: function(layout_id) {
32101 if (typeof(this.groups[layout_id]) == 'undefined') {
32104 return this.groups[layout_id] ;
32116 * http://masonry.desandro.com
32118 * The idea is to render all the bricks based on vertical width...
32120 * The original code extends 'outlayer' - we might need to use that....
32126 * @class Roo.bootstrap.LayoutMasonryAuto
32127 * @extends Roo.bootstrap.Component
32128 * Bootstrap Layout Masonry class
32131 * Create a new Element
32132 * @param {Object} config The config object
32135 Roo.bootstrap.LayoutMasonryAuto = function(config){
32136 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32139 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32142 * @cfg {Boolean} isFitWidth - resize the width..
32144 isFitWidth : false, // options..
32146 * @cfg {Boolean} isOriginLeft = left align?
32148 isOriginLeft : true,
32150 * @cfg {Boolean} isOriginTop = top align?
32152 isOriginTop : false,
32154 * @cfg {Boolean} isLayoutInstant = no animation?
32156 isLayoutInstant : false, // needed?
32158 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32160 isResizingContainer : true,
32162 * @cfg {Number} columnWidth width of the columns
32168 * @cfg {Number} maxCols maximum number of columns
32173 * @cfg {Number} padHeight padding below box..
32179 * @cfg {Boolean} isAutoInitial defalut true
32182 isAutoInitial : true,
32188 initialColumnWidth : 0,
32189 currentSize : null,
32191 colYs : null, // array.
32198 bricks: null, //CompositeElement
32199 cols : 0, // array?
32200 // element : null, // wrapped now this.el
32201 _isLayoutInited : null,
32204 getAutoCreate : function(){
32208 cls: 'blog-masonary-wrapper ' + this.cls,
32210 cls : 'mas-boxes masonary'
32217 getChildContainer: function( )
32219 if (this.boxesEl) {
32220 return this.boxesEl;
32223 this.boxesEl = this.el.select('.mas-boxes').first();
32225 return this.boxesEl;
32229 initEvents : function()
32233 if(this.isAutoInitial){
32234 Roo.log('hook children rendered');
32235 this.on('childrenrendered', function() {
32236 Roo.log('children rendered');
32243 initial : function()
32245 this.reloadItems();
32247 this.currentSize = this.el.getBox(true);
32249 /// was window resize... - let's see if this works..
32250 Roo.EventManager.onWindowResize(this.resize, this);
32252 if(!this.isAutoInitial){
32257 this.layout.defer(500,this);
32260 reloadItems: function()
32262 this.bricks = this.el.select('.masonry-brick', true);
32264 this.bricks.each(function(b) {
32265 //Roo.log(b.getSize());
32266 if (!b.attr('originalwidth')) {
32267 b.attr('originalwidth', b.getSize().width);
32272 Roo.log(this.bricks.elements.length);
32275 resize : function()
32278 var cs = this.el.getBox(true);
32280 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32281 Roo.log("no change in with or X");
32284 this.currentSize = cs;
32288 layout : function()
32291 this._resetLayout();
32292 //this._manageStamps();
32294 // don't animate first layout
32295 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32296 this.layoutItems( isInstant );
32298 // flag for initalized
32299 this._isLayoutInited = true;
32302 layoutItems : function( isInstant )
32304 //var items = this._getItemsForLayout( this.items );
32305 // original code supports filtering layout items.. we just ignore it..
32307 this._layoutItems( this.bricks , isInstant );
32309 this._postLayout();
32311 _layoutItems : function ( items , isInstant)
32313 //this.fireEvent( 'layout', this, items );
32316 if ( !items || !items.elements.length ) {
32317 // no items, emit event with empty array
32322 items.each(function(item) {
32323 Roo.log("layout item");
32325 // get x/y object from method
32326 var position = this._getItemLayoutPosition( item );
32328 position.item = item;
32329 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32330 queue.push( position );
32333 this._processLayoutQueue( queue );
32335 /** Sets position of item in DOM
32336 * @param {Element} item
32337 * @param {Number} x - horizontal position
32338 * @param {Number} y - vertical position
32339 * @param {Boolean} isInstant - disables transitions
32341 _processLayoutQueue : function( queue )
32343 for ( var i=0, len = queue.length; i < len; i++ ) {
32344 var obj = queue[i];
32345 obj.item.position('absolute');
32346 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32352 * Any logic you want to do after each layout,
32353 * i.e. size the container
32355 _postLayout : function()
32357 this.resizeContainer();
32360 resizeContainer : function()
32362 if ( !this.isResizingContainer ) {
32365 var size = this._getContainerSize();
32367 this.el.setSize(size.width,size.height);
32368 this.boxesEl.setSize(size.width,size.height);
32374 _resetLayout : function()
32376 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32377 this.colWidth = this.el.getWidth();
32378 //this.gutter = this.el.getWidth();
32380 this.measureColumns();
32386 this.colYs.push( 0 );
32392 measureColumns : function()
32394 this.getContainerWidth();
32395 // if columnWidth is 0, default to outerWidth of first item
32396 if ( !this.columnWidth ) {
32397 var firstItem = this.bricks.first();
32398 Roo.log(firstItem);
32399 this.columnWidth = this.containerWidth;
32400 if (firstItem && firstItem.attr('originalwidth') ) {
32401 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32403 // columnWidth fall back to item of first element
32404 Roo.log("set column width?");
32405 this.initialColumnWidth = this.columnWidth ;
32407 // if first elem has no width, default to size of container
32412 if (this.initialColumnWidth) {
32413 this.columnWidth = this.initialColumnWidth;
32418 // column width is fixed at the top - however if container width get's smaller we should
32421 // this bit calcs how man columns..
32423 var columnWidth = this.columnWidth += this.gutter;
32425 // calculate columns
32426 var containerWidth = this.containerWidth + this.gutter;
32428 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32429 // fix rounding errors, typically with gutters
32430 var excess = columnWidth - containerWidth % columnWidth;
32433 // if overshoot is less than a pixel, round up, otherwise floor it
32434 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32435 cols = Math[ mathMethod ]( cols );
32436 this.cols = Math.max( cols, 1 );
32437 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32439 // padding positioning..
32440 var totalColWidth = this.cols * this.columnWidth;
32441 var padavail = this.containerWidth - totalColWidth;
32442 // so for 2 columns - we need 3 'pads'
32444 var padNeeded = (1+this.cols) * this.padWidth;
32446 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32448 this.columnWidth += padExtra
32449 //this.padWidth = Math.floor(padavail / ( this.cols));
32451 // adjust colum width so that padding is fixed??
32453 // we have 3 columns ... total = width * 3
32454 // we have X left over... that should be used by
32456 //if (this.expandC) {
32464 getContainerWidth : function()
32466 /* // container is parent if fit width
32467 var container = this.isFitWidth ? this.element.parentNode : this.element;
32468 // check that this.size and size are there
32469 // IE8 triggers resize on body size change, so they might not be
32471 var size = getSize( container ); //FIXME
32472 this.containerWidth = size && size.innerWidth; //FIXME
32475 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32479 _getItemLayoutPosition : function( item ) // what is item?
32481 // we resize the item to our columnWidth..
32483 item.setWidth(this.columnWidth);
32484 item.autoBoxAdjust = false;
32486 var sz = item.getSize();
32488 // how many columns does this brick span
32489 var remainder = this.containerWidth % this.columnWidth;
32491 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32492 // round if off by 1 pixel, otherwise use ceil
32493 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32494 colSpan = Math.min( colSpan, this.cols );
32496 // normally this should be '1' as we dont' currently allow multi width columns..
32498 var colGroup = this._getColGroup( colSpan );
32499 // get the minimum Y value from the columns
32500 var minimumY = Math.min.apply( Math, colGroup );
32501 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32503 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32505 // position the brick
32507 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32508 y: this.currentSize.y + minimumY + this.padHeight
32512 // apply setHeight to necessary columns
32513 var setHeight = minimumY + sz.height + this.padHeight;
32514 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32516 var setSpan = this.cols + 1 - colGroup.length;
32517 for ( var i = 0; i < setSpan; i++ ) {
32518 this.colYs[ shortColIndex + i ] = setHeight ;
32525 * @param {Number} colSpan - number of columns the element spans
32526 * @returns {Array} colGroup
32528 _getColGroup : function( colSpan )
32530 if ( colSpan < 2 ) {
32531 // if brick spans only one column, use all the column Ys
32536 // how many different places could this brick fit horizontally
32537 var groupCount = this.cols + 1 - colSpan;
32538 // for each group potential horizontal position
32539 for ( var i = 0; i < groupCount; i++ ) {
32540 // make an array of colY values for that one group
32541 var groupColYs = this.colYs.slice( i, i + colSpan );
32542 // and get the max value of the array
32543 colGroup[i] = Math.max.apply( Math, groupColYs );
32548 _manageStamp : function( stamp )
32550 var stampSize = stamp.getSize();
32551 var offset = stamp.getBox();
32552 // get the columns that this stamp affects
32553 var firstX = this.isOriginLeft ? offset.x : offset.right;
32554 var lastX = firstX + stampSize.width;
32555 var firstCol = Math.floor( firstX / this.columnWidth );
32556 firstCol = Math.max( 0, firstCol );
32558 var lastCol = Math.floor( lastX / this.columnWidth );
32559 // lastCol should not go over if multiple of columnWidth #425
32560 lastCol -= lastX % this.columnWidth ? 0 : 1;
32561 lastCol = Math.min( this.cols - 1, lastCol );
32563 // set colYs to bottom of the stamp
32564 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32567 for ( var i = firstCol; i <= lastCol; i++ ) {
32568 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32573 _getContainerSize : function()
32575 this.maxY = Math.max.apply( Math, this.colYs );
32580 if ( this.isFitWidth ) {
32581 size.width = this._getContainerFitWidth();
32587 _getContainerFitWidth : function()
32589 var unusedCols = 0;
32590 // count unused columns
32593 if ( this.colYs[i] !== 0 ) {
32598 // fit container to columns that have been used
32599 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32602 needsResizeLayout : function()
32604 var previousWidth = this.containerWidth;
32605 this.getContainerWidth();
32606 return previousWidth !== this.containerWidth;
32621 * @class Roo.bootstrap.MasonryBrick
32622 * @extends Roo.bootstrap.Component
32623 * Bootstrap MasonryBrick class
32626 * Create a new MasonryBrick
32627 * @param {Object} config The config object
32630 Roo.bootstrap.MasonryBrick = function(config){
32632 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32634 Roo.bootstrap.MasonryBrick.register(this);
32640 * When a MasonryBrick is clcik
32641 * @param {Roo.bootstrap.MasonryBrick} this
32642 * @param {Roo.EventObject} e
32648 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32651 * @cfg {String} title
32655 * @cfg {String} html
32659 * @cfg {String} bgimage
32663 * @cfg {String} videourl
32667 * @cfg {String} cls
32671 * @cfg {String} href
32675 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32680 * @cfg {String} placetitle (center|bottom)
32685 * @cfg {Boolean} isFitContainer defalut true
32687 isFitContainer : true,
32690 * @cfg {Boolean} preventDefault defalut false
32692 preventDefault : false,
32695 * @cfg {Boolean} inverse defalut false
32697 maskInverse : false,
32699 getAutoCreate : function()
32701 if(!this.isFitContainer){
32702 return this.getSplitAutoCreate();
32705 var cls = 'masonry-brick masonry-brick-full';
32707 if(this.href.length){
32708 cls += ' masonry-brick-link';
32711 if(this.bgimage.length){
32712 cls += ' masonry-brick-image';
32715 if(this.maskInverse){
32716 cls += ' mask-inverse';
32719 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32720 cls += ' enable-mask';
32724 cls += ' masonry-' + this.size + '-brick';
32727 if(this.placetitle.length){
32729 switch (this.placetitle) {
32731 cls += ' masonry-center-title';
32734 cls += ' masonry-bottom-title';
32741 if(!this.html.length && !this.bgimage.length){
32742 cls += ' masonry-center-title';
32745 if(!this.html.length && this.bgimage.length){
32746 cls += ' masonry-bottom-title';
32751 cls += ' ' + this.cls;
32755 tag: (this.href.length) ? 'a' : 'div',
32760 cls: 'masonry-brick-mask'
32764 cls: 'masonry-brick-paragraph',
32770 if(this.href.length){
32771 cfg.href = this.href;
32774 var cn = cfg.cn[1].cn;
32776 if(this.title.length){
32779 cls: 'masonry-brick-title',
32784 if(this.html.length){
32787 cls: 'masonry-brick-text',
32792 if (!this.title.length && !this.html.length) {
32793 cfg.cn[1].cls += ' hide';
32796 if(this.bgimage.length){
32799 cls: 'masonry-brick-image-view',
32804 if(this.videourl.length){
32805 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32806 // youtube support only?
32809 cls: 'masonry-brick-image-view',
32812 allowfullscreen : true
32820 getSplitAutoCreate : function()
32822 var cls = 'masonry-brick masonry-brick-split';
32824 if(this.href.length){
32825 cls += ' masonry-brick-link';
32828 if(this.bgimage.length){
32829 cls += ' masonry-brick-image';
32833 cls += ' masonry-' + this.size + '-brick';
32836 switch (this.placetitle) {
32838 cls += ' masonry-center-title';
32841 cls += ' masonry-bottom-title';
32844 if(!this.bgimage.length){
32845 cls += ' masonry-center-title';
32848 if(this.bgimage.length){
32849 cls += ' masonry-bottom-title';
32855 cls += ' ' + this.cls;
32859 tag: (this.href.length) ? 'a' : 'div',
32864 cls: 'masonry-brick-split-head',
32868 cls: 'masonry-brick-paragraph',
32875 cls: 'masonry-brick-split-body',
32881 if(this.href.length){
32882 cfg.href = this.href;
32885 if(this.title.length){
32886 cfg.cn[0].cn[0].cn.push({
32888 cls: 'masonry-brick-title',
32893 if(this.html.length){
32894 cfg.cn[1].cn.push({
32896 cls: 'masonry-brick-text',
32901 if(this.bgimage.length){
32902 cfg.cn[0].cn.push({
32904 cls: 'masonry-brick-image-view',
32909 if(this.videourl.length){
32910 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32911 // youtube support only?
32912 cfg.cn[0].cn.cn.push({
32914 cls: 'masonry-brick-image-view',
32917 allowfullscreen : true
32924 initEvents: function()
32926 switch (this.size) {
32959 this.el.on('touchstart', this.onTouchStart, this);
32960 this.el.on('touchmove', this.onTouchMove, this);
32961 this.el.on('touchend', this.onTouchEnd, this);
32962 this.el.on('contextmenu', this.onContextMenu, this);
32964 this.el.on('mouseenter' ,this.enter, this);
32965 this.el.on('mouseleave', this.leave, this);
32966 this.el.on('click', this.onClick, this);
32969 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32970 this.parent().bricks.push(this);
32975 onClick: function(e, el)
32977 var time = this.endTimer - this.startTimer;
32978 // Roo.log(e.preventDefault());
32981 e.preventDefault();
32986 if(!this.preventDefault){
32990 e.preventDefault();
32992 if (this.activeClass != '') {
32993 this.selectBrick();
32996 this.fireEvent('click', this, e);
32999 enter: function(e, el)
33001 e.preventDefault();
33003 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33007 if(this.bgimage.length && this.html.length){
33008 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33012 leave: function(e, el)
33014 e.preventDefault();
33016 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33020 if(this.bgimage.length && this.html.length){
33021 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33025 onTouchStart: function(e, el)
33027 // e.preventDefault();
33029 this.touchmoved = false;
33031 if(!this.isFitContainer){
33035 if(!this.bgimage.length || !this.html.length){
33039 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33041 this.timer = new Date().getTime();
33045 onTouchMove: function(e, el)
33047 this.touchmoved = true;
33050 onContextMenu : function(e,el)
33052 e.preventDefault();
33053 e.stopPropagation();
33057 onTouchEnd: function(e, el)
33059 // e.preventDefault();
33061 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33068 if(!this.bgimage.length || !this.html.length){
33070 if(this.href.length){
33071 window.location.href = this.href;
33077 if(!this.isFitContainer){
33081 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33083 window.location.href = this.href;
33086 //selection on single brick only
33087 selectBrick : function() {
33089 if (!this.parentId) {
33093 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33094 var index = m.selectedBrick.indexOf(this.id);
33097 m.selectedBrick.splice(index,1);
33098 this.el.removeClass(this.activeClass);
33102 for(var i = 0; i < m.selectedBrick.length; i++) {
33103 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33104 b.el.removeClass(b.activeClass);
33107 m.selectedBrick = [];
33109 m.selectedBrick.push(this.id);
33110 this.el.addClass(this.activeClass);
33114 isSelected : function(){
33115 return this.el.hasClass(this.activeClass);
33120 Roo.apply(Roo.bootstrap.MasonryBrick, {
33123 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33125 * register a Masonry Brick
33126 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33129 register : function(brick)
33131 //this.groups[brick.id] = brick;
33132 this.groups.add(brick.id, brick);
33135 * fetch a masonry brick based on the masonry brick ID
33136 * @param {string} the masonry brick to add
33137 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33140 get: function(brick_id)
33142 // if (typeof(this.groups[brick_id]) == 'undefined') {
33145 // return this.groups[brick_id] ;
33147 if(this.groups.key(brick_id)) {
33148 return this.groups.key(brick_id);
33166 * @class Roo.bootstrap.Brick
33167 * @extends Roo.bootstrap.Component
33168 * Bootstrap Brick class
33171 * Create a new Brick
33172 * @param {Object} config The config object
33175 Roo.bootstrap.Brick = function(config){
33176 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33182 * When a Brick is click
33183 * @param {Roo.bootstrap.Brick} this
33184 * @param {Roo.EventObject} e
33190 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33193 * @cfg {String} title
33197 * @cfg {String} html
33201 * @cfg {String} bgimage
33205 * @cfg {String} cls
33209 * @cfg {String} href
33213 * @cfg {String} video
33217 * @cfg {Boolean} square
33221 getAutoCreate : function()
33223 var cls = 'roo-brick';
33225 if(this.href.length){
33226 cls += ' roo-brick-link';
33229 if(this.bgimage.length){
33230 cls += ' roo-brick-image';
33233 if(!this.html.length && !this.bgimage.length){
33234 cls += ' roo-brick-center-title';
33237 if(!this.html.length && this.bgimage.length){
33238 cls += ' roo-brick-bottom-title';
33242 cls += ' ' + this.cls;
33246 tag: (this.href.length) ? 'a' : 'div',
33251 cls: 'roo-brick-paragraph',
33257 if(this.href.length){
33258 cfg.href = this.href;
33261 var cn = cfg.cn[0].cn;
33263 if(this.title.length){
33266 cls: 'roo-brick-title',
33271 if(this.html.length){
33274 cls: 'roo-brick-text',
33281 if(this.bgimage.length){
33284 cls: 'roo-brick-image-view',
33292 initEvents: function()
33294 if(this.title.length || this.html.length){
33295 this.el.on('mouseenter' ,this.enter, this);
33296 this.el.on('mouseleave', this.leave, this);
33299 Roo.EventManager.onWindowResize(this.resize, this);
33301 if(this.bgimage.length){
33302 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33303 this.imageEl.on('load', this.onImageLoad, this);
33310 onImageLoad : function()
33315 resize : function()
33317 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33319 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33321 if(this.bgimage.length){
33322 var image = this.el.select('.roo-brick-image-view', true).first();
33324 image.setWidth(paragraph.getWidth());
33327 image.setHeight(paragraph.getWidth());
33330 this.el.setHeight(image.getHeight());
33331 paragraph.setHeight(image.getHeight());
33337 enter: function(e, el)
33339 e.preventDefault();
33341 if(this.bgimage.length){
33342 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33343 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33347 leave: function(e, el)
33349 e.preventDefault();
33351 if(this.bgimage.length){
33352 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33353 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33368 * @class Roo.bootstrap.NumberField
33369 * @extends Roo.bootstrap.Input
33370 * Bootstrap NumberField class
33376 * Create a new NumberField
33377 * @param {Object} config The config object
33380 Roo.bootstrap.NumberField = function(config){
33381 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33384 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33387 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33389 allowDecimals : true,
33391 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33393 decimalSeparator : ".",
33395 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33397 decimalPrecision : 2,
33399 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33401 allowNegative : true,
33404 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33408 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33410 minValue : Number.NEGATIVE_INFINITY,
33412 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33414 maxValue : Number.MAX_VALUE,
33416 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33418 minText : "The minimum value for this field is {0}",
33420 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33422 maxText : "The maximum value for this field is {0}",
33424 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33425 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33427 nanText : "{0} is not a valid number",
33429 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33431 thousandsDelimiter : false,
33433 * @cfg {String} valueAlign alignment of value
33435 valueAlign : "left",
33437 getAutoCreate : function()
33439 var hiddenInput = {
33443 cls: 'hidden-number-input'
33447 hiddenInput.name = this.name;
33452 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33454 this.name = hiddenInput.name;
33456 if(cfg.cn.length > 0) {
33457 cfg.cn.push(hiddenInput);
33464 initEvents : function()
33466 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33468 var allowed = "0123456789";
33470 if(this.allowDecimals){
33471 allowed += this.decimalSeparator;
33474 if(this.allowNegative){
33478 if(this.thousandsDelimiter) {
33482 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33484 var keyPress = function(e){
33486 var k = e.getKey();
33488 var c = e.getCharCode();
33491 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33492 allowed.indexOf(String.fromCharCode(c)) === -1
33498 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33502 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33507 this.el.on("keypress", keyPress, this);
33510 validateValue : function(value)
33513 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33517 var num = this.parseValue(value);
33520 this.markInvalid(String.format(this.nanText, value));
33524 if(num < this.minValue){
33525 this.markInvalid(String.format(this.minText, this.minValue));
33529 if(num > this.maxValue){
33530 this.markInvalid(String.format(this.maxText, this.maxValue));
33537 getValue : function()
33539 var v = this.hiddenEl().getValue();
33541 return this.fixPrecision(this.parseValue(v));
33544 parseValue : function(value)
33546 if(this.thousandsDelimiter) {
33548 r = new RegExp(",", "g");
33549 value = value.replace(r, "");
33552 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33553 return isNaN(value) ? '' : value;
33556 fixPrecision : function(value)
33558 if(this.thousandsDelimiter) {
33560 r = new RegExp(",", "g");
33561 value = value.replace(r, "");
33564 var nan = isNaN(value);
33566 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33567 return nan ? '' : value;
33569 return parseFloat(value).toFixed(this.decimalPrecision);
33572 setValue : function(v)
33574 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33580 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33582 this.inputEl().dom.value = (v == '') ? '' :
33583 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33585 if(!this.allowZero && v === '0') {
33586 this.hiddenEl().dom.value = '';
33587 this.inputEl().dom.value = '';
33594 decimalPrecisionFcn : function(v)
33596 return Math.floor(v);
33599 beforeBlur : function()
33601 var v = this.parseValue(this.getRawValue());
33603 if(v || v === 0 || v === ''){
33608 hiddenEl : function()
33610 return this.el.select('input.hidden-number-input',true).first();
33622 * @class Roo.bootstrap.DocumentSlider
33623 * @extends Roo.bootstrap.Component
33624 * Bootstrap DocumentSlider class
33627 * Create a new DocumentViewer
33628 * @param {Object} config The config object
33631 Roo.bootstrap.DocumentSlider = function(config){
33632 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33639 * Fire after initEvent
33640 * @param {Roo.bootstrap.DocumentSlider} this
33645 * Fire after update
33646 * @param {Roo.bootstrap.DocumentSlider} this
33652 * @param {Roo.bootstrap.DocumentSlider} this
33658 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33664 getAutoCreate : function()
33668 cls : 'roo-document-slider',
33672 cls : 'roo-document-slider-header',
33676 cls : 'roo-document-slider-header-title'
33682 cls : 'roo-document-slider-body',
33686 cls : 'roo-document-slider-prev',
33690 cls : 'fa fa-chevron-left'
33696 cls : 'roo-document-slider-thumb',
33700 cls : 'roo-document-slider-image'
33706 cls : 'roo-document-slider-next',
33710 cls : 'fa fa-chevron-right'
33722 initEvents : function()
33724 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33725 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33727 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33728 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33730 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33731 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33733 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33734 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33736 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33737 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33739 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33740 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33742 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33743 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33745 this.thumbEl.on('click', this.onClick, this);
33747 this.prevIndicator.on('click', this.prev, this);
33749 this.nextIndicator.on('click', this.next, this);
33753 initial : function()
33755 if(this.files.length){
33756 this.indicator = 1;
33760 this.fireEvent('initial', this);
33763 update : function()
33765 this.imageEl.attr('src', this.files[this.indicator - 1]);
33767 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33769 this.prevIndicator.show();
33771 if(this.indicator == 1){
33772 this.prevIndicator.hide();
33775 this.nextIndicator.show();
33777 if(this.indicator == this.files.length){
33778 this.nextIndicator.hide();
33781 this.thumbEl.scrollTo('top');
33783 this.fireEvent('update', this);
33786 onClick : function(e)
33788 e.preventDefault();
33790 this.fireEvent('click', this);
33795 e.preventDefault();
33797 this.indicator = Math.max(1, this.indicator - 1);
33804 e.preventDefault();
33806 this.indicator = Math.min(this.files.length, this.indicator + 1);
33820 * @class Roo.bootstrap.RadioSet
33821 * @extends Roo.bootstrap.Input
33822 * Bootstrap RadioSet class
33823 * @cfg {String} indicatorpos (left|right) default left
33824 * @cfg {Boolean} inline (true|false) inline the element (default true)
33825 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33827 * Create a new RadioSet
33828 * @param {Object} config The config object
33831 Roo.bootstrap.RadioSet = function(config){
33833 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33837 Roo.bootstrap.RadioSet.register(this);
33842 * Fires when the element is checked or unchecked.
33843 * @param {Roo.bootstrap.RadioSet} this This radio
33844 * @param {Roo.bootstrap.Radio} item The checked item
33849 * Fires when the element is click.
33850 * @param {Roo.bootstrap.RadioSet} this This radio set
33851 * @param {Roo.bootstrap.Radio} item The checked item
33852 * @param {Roo.EventObject} e The event object
33859 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33867 indicatorpos : 'left',
33869 getAutoCreate : function()
33873 cls : 'roo-radio-set-label',
33877 html : this.fieldLabel
33882 if(this.indicatorpos == 'left'){
33885 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33886 tooltip : 'This field is required'
33891 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33892 tooltip : 'This field is required'
33898 cls : 'roo-radio-set-items'
33901 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33903 if (align === 'left' && this.fieldLabel.length) {
33906 cls : "roo-radio-set-right",
33912 if(this.labelWidth > 12){
33913 label.style = "width: " + this.labelWidth + 'px';
33916 if(this.labelWidth < 13 && this.labelmd == 0){
33917 this.labelmd = this.labelWidth;
33920 if(this.labellg > 0){
33921 label.cls += ' col-lg-' + this.labellg;
33922 items.cls += ' col-lg-' + (12 - this.labellg);
33925 if(this.labelmd > 0){
33926 label.cls += ' col-md-' + this.labelmd;
33927 items.cls += ' col-md-' + (12 - this.labelmd);
33930 if(this.labelsm > 0){
33931 label.cls += ' col-sm-' + this.labelsm;
33932 items.cls += ' col-sm-' + (12 - this.labelsm);
33935 if(this.labelxs > 0){
33936 label.cls += ' col-xs-' + this.labelxs;
33937 items.cls += ' col-xs-' + (12 - this.labelxs);
33943 cls : 'roo-radio-set',
33947 cls : 'roo-radio-set-input',
33950 value : this.value ? this.value : ''
33957 if(this.weight.length){
33958 cfg.cls += ' roo-radio-' + this.weight;
33962 cfg.cls += ' roo-radio-set-inline';
33966 ['xs','sm','md','lg'].map(function(size){
33967 if (settings[size]) {
33968 cfg.cls += ' col-' + size + '-' + settings[size];
33976 initEvents : function()
33978 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33979 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33981 if(!this.fieldLabel.length){
33982 this.labelEl.hide();
33985 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33986 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33988 this.indicator = this.indicatorEl();
33990 if(this.indicator){
33991 this.indicator.addClass('invisible');
33994 this.originalValue = this.getValue();
33998 inputEl: function ()
34000 return this.el.select('.roo-radio-set-input', true).first();
34003 getChildContainer : function()
34005 return this.itemsEl;
34008 register : function(item)
34010 this.radioes.push(item);
34014 validate : function()
34016 if(this.getVisibilityEl().hasClass('hidden')){
34022 Roo.each(this.radioes, function(i){
34031 if(this.allowBlank) {
34035 if(this.disabled || valid){
34040 this.markInvalid();
34045 markValid : function()
34047 if(this.labelEl.isVisible(true)){
34048 this.indicatorEl().removeClass('visible');
34049 this.indicatorEl().addClass('invisible');
34052 this.el.removeClass([this.invalidClass, this.validClass]);
34053 this.el.addClass(this.validClass);
34055 this.fireEvent('valid', this);
34058 markInvalid : function(msg)
34060 if(this.allowBlank || this.disabled){
34064 if(this.labelEl.isVisible(true)){
34065 this.indicatorEl().removeClass('invisible');
34066 this.indicatorEl().addClass('visible');
34069 this.el.removeClass([this.invalidClass, this.validClass]);
34070 this.el.addClass(this.invalidClass);
34072 this.fireEvent('invalid', this, msg);
34076 setValue : function(v, suppressEvent)
34078 if(this.value === v){
34085 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34088 Roo.each(this.radioes, function(i){
34090 i.el.removeClass('checked');
34093 Roo.each(this.radioes, function(i){
34095 if(i.value === v || i.value.toString() === v.toString()){
34097 i.el.addClass('checked');
34099 if(suppressEvent !== true){
34100 this.fireEvent('check', this, i);
34111 clearInvalid : function(){
34113 if(!this.el || this.preventMark){
34117 this.el.removeClass([this.invalidClass]);
34119 this.fireEvent('valid', this);
34124 Roo.apply(Roo.bootstrap.RadioSet, {
34128 register : function(set)
34130 this.groups[set.name] = set;
34133 get: function(name)
34135 if (typeof(this.groups[name]) == 'undefined') {
34139 return this.groups[name] ;
34145 * Ext JS Library 1.1.1
34146 * Copyright(c) 2006-2007, Ext JS, LLC.
34148 * Originally Released Under LGPL - original licence link has changed is not relivant.
34151 * <script type="text/javascript">
34156 * @class Roo.bootstrap.SplitBar
34157 * @extends Roo.util.Observable
34158 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34162 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34163 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34164 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34165 split.minSize = 100;
34166 split.maxSize = 600;
34167 split.animate = true;
34168 split.on('moved', splitterMoved);
34171 * Create a new SplitBar
34172 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34173 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34174 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34175 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34176 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34177 position of the SplitBar).
34179 Roo.bootstrap.SplitBar = function(cfg){
34184 // dragElement : elm
34185 // resizingElement: el,
34187 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34188 // placement : Roo.bootstrap.SplitBar.LEFT ,
34189 // existingProxy ???
34192 this.el = Roo.get(cfg.dragElement, true);
34193 this.el.dom.unselectable = "on";
34195 this.resizingEl = Roo.get(cfg.resizingElement, true);
34199 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34200 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34203 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34206 * The minimum size of the resizing element. (Defaults to 0)
34212 * The maximum size of the resizing element. (Defaults to 2000)
34215 this.maxSize = 2000;
34218 * Whether to animate the transition to the new size
34221 this.animate = false;
34224 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34227 this.useShim = false;
34232 if(!cfg.existingProxy){
34234 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34236 this.proxy = Roo.get(cfg.existingProxy).dom;
34239 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34242 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34245 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34248 this.dragSpecs = {};
34251 * @private The adapter to use to positon and resize elements
34253 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34254 this.adapter.init(this);
34256 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34258 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34259 this.el.addClass("roo-splitbar-h");
34262 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34263 this.el.addClass("roo-splitbar-v");
34269 * Fires when the splitter is moved (alias for {@link #event-moved})
34270 * @param {Roo.bootstrap.SplitBar} this
34271 * @param {Number} newSize the new width or height
34276 * Fires when the splitter is moved
34277 * @param {Roo.bootstrap.SplitBar} this
34278 * @param {Number} newSize the new width or height
34282 * @event beforeresize
34283 * Fires before the splitter is dragged
34284 * @param {Roo.bootstrap.SplitBar} this
34286 "beforeresize" : true,
34288 "beforeapply" : true
34291 Roo.util.Observable.call(this);
34294 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34295 onStartProxyDrag : function(x, y){
34296 this.fireEvent("beforeresize", this);
34298 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34300 o.enableDisplayMode("block");
34301 // all splitbars share the same overlay
34302 Roo.bootstrap.SplitBar.prototype.overlay = o;
34304 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34305 this.overlay.show();
34306 Roo.get(this.proxy).setDisplayed("block");
34307 var size = this.adapter.getElementSize(this);
34308 this.activeMinSize = this.getMinimumSize();;
34309 this.activeMaxSize = this.getMaximumSize();;
34310 var c1 = size - this.activeMinSize;
34311 var c2 = Math.max(this.activeMaxSize - size, 0);
34312 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34313 this.dd.resetConstraints();
34314 this.dd.setXConstraint(
34315 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34316 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34318 this.dd.setYConstraint(0, 0);
34320 this.dd.resetConstraints();
34321 this.dd.setXConstraint(0, 0);
34322 this.dd.setYConstraint(
34323 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34324 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34327 this.dragSpecs.startSize = size;
34328 this.dragSpecs.startPoint = [x, y];
34329 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34333 * @private Called after the drag operation by the DDProxy
34335 onEndProxyDrag : function(e){
34336 Roo.get(this.proxy).setDisplayed(false);
34337 var endPoint = Roo.lib.Event.getXY(e);
34339 this.overlay.hide();
34342 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34343 newSize = this.dragSpecs.startSize +
34344 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34345 endPoint[0] - this.dragSpecs.startPoint[0] :
34346 this.dragSpecs.startPoint[0] - endPoint[0]
34349 newSize = this.dragSpecs.startSize +
34350 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34351 endPoint[1] - this.dragSpecs.startPoint[1] :
34352 this.dragSpecs.startPoint[1] - endPoint[1]
34355 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34356 if(newSize != this.dragSpecs.startSize){
34357 if(this.fireEvent('beforeapply', this, newSize) !== false){
34358 this.adapter.setElementSize(this, newSize);
34359 this.fireEvent("moved", this, newSize);
34360 this.fireEvent("resize", this, newSize);
34366 * Get the adapter this SplitBar uses
34367 * @return The adapter object
34369 getAdapter : function(){
34370 return this.adapter;
34374 * Set the adapter this SplitBar uses
34375 * @param {Object} adapter A SplitBar adapter object
34377 setAdapter : function(adapter){
34378 this.adapter = adapter;
34379 this.adapter.init(this);
34383 * Gets the minimum size for the resizing element
34384 * @return {Number} The minimum size
34386 getMinimumSize : function(){
34387 return this.minSize;
34391 * Sets the minimum size for the resizing element
34392 * @param {Number} minSize The minimum size
34394 setMinimumSize : function(minSize){
34395 this.minSize = minSize;
34399 * Gets the maximum size for the resizing element
34400 * @return {Number} The maximum size
34402 getMaximumSize : function(){
34403 return this.maxSize;
34407 * Sets the maximum size for the resizing element
34408 * @param {Number} maxSize The maximum size
34410 setMaximumSize : function(maxSize){
34411 this.maxSize = maxSize;
34415 * Sets the initialize size for the resizing element
34416 * @param {Number} size The initial size
34418 setCurrentSize : function(size){
34419 var oldAnimate = this.animate;
34420 this.animate = false;
34421 this.adapter.setElementSize(this, size);
34422 this.animate = oldAnimate;
34426 * Destroy this splitbar.
34427 * @param {Boolean} removeEl True to remove the element
34429 destroy : function(removeEl){
34431 this.shim.remove();
34434 this.proxy.parentNode.removeChild(this.proxy);
34442 * @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.
34444 Roo.bootstrap.SplitBar.createProxy = function(dir){
34445 var proxy = new Roo.Element(document.createElement("div"));
34446 proxy.unselectable();
34447 var cls = 'roo-splitbar-proxy';
34448 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34449 document.body.appendChild(proxy.dom);
34454 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34455 * Default Adapter. It assumes the splitter and resizing element are not positioned
34456 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34458 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34461 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34462 // do nothing for now
34463 init : function(s){
34467 * Called before drag operations to get the current size of the resizing element.
34468 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34470 getElementSize : function(s){
34471 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34472 return s.resizingEl.getWidth();
34474 return s.resizingEl.getHeight();
34479 * Called after drag operations to set the size of the resizing element.
34480 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34481 * @param {Number} newSize The new size to set
34482 * @param {Function} onComplete A function to be invoked when resizing is complete
34484 setElementSize : function(s, newSize, onComplete){
34485 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34487 s.resizingEl.setWidth(newSize);
34489 onComplete(s, newSize);
34492 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34497 s.resizingEl.setHeight(newSize);
34499 onComplete(s, newSize);
34502 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34509 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34510 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34511 * Adapter that moves the splitter element to align with the resized sizing element.
34512 * Used with an absolute positioned SplitBar.
34513 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34514 * document.body, make sure you assign an id to the body element.
34516 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34517 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34518 this.container = Roo.get(container);
34521 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34522 init : function(s){
34523 this.basic.init(s);
34526 getElementSize : function(s){
34527 return this.basic.getElementSize(s);
34530 setElementSize : function(s, newSize, onComplete){
34531 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34534 moveSplitter : function(s){
34535 var yes = Roo.bootstrap.SplitBar;
34536 switch(s.placement){
34538 s.el.setX(s.resizingEl.getRight());
34541 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34544 s.el.setY(s.resizingEl.getBottom());
34547 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34554 * Orientation constant - Create a vertical SplitBar
34558 Roo.bootstrap.SplitBar.VERTICAL = 1;
34561 * Orientation constant - Create a horizontal SplitBar
34565 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34568 * Placement constant - The resizing element is to the left of the splitter element
34572 Roo.bootstrap.SplitBar.LEFT = 1;
34575 * Placement constant - The resizing element is to the right of the splitter element
34579 Roo.bootstrap.SplitBar.RIGHT = 2;
34582 * Placement constant - The resizing element is positioned above the splitter element
34586 Roo.bootstrap.SplitBar.TOP = 3;
34589 * Placement constant - The resizing element is positioned under splitter element
34593 Roo.bootstrap.SplitBar.BOTTOM = 4;
34594 Roo.namespace("Roo.bootstrap.layout");/*
34596 * Ext JS Library 1.1.1
34597 * Copyright(c) 2006-2007, Ext JS, LLC.
34599 * Originally Released Under LGPL - original licence link has changed is not relivant.
34602 * <script type="text/javascript">
34606 * @class Roo.bootstrap.layout.Manager
34607 * @extends Roo.bootstrap.Component
34608 * Base class for layout managers.
34610 Roo.bootstrap.layout.Manager = function(config)
34612 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34618 /** false to disable window resize monitoring @type Boolean */
34619 this.monitorWindowResize = true;
34624 * Fires when a layout is performed.
34625 * @param {Roo.LayoutManager} this
34629 * @event regionresized
34630 * Fires when the user resizes a region.
34631 * @param {Roo.LayoutRegion} region The resized region
34632 * @param {Number} newSize The new size (width for east/west, height for north/south)
34634 "regionresized" : true,
34636 * @event regioncollapsed
34637 * Fires when a region is collapsed.
34638 * @param {Roo.LayoutRegion} region The collapsed region
34640 "regioncollapsed" : true,
34642 * @event regionexpanded
34643 * Fires when a region is expanded.
34644 * @param {Roo.LayoutRegion} region The expanded region
34646 "regionexpanded" : true
34648 this.updating = false;
34651 this.el = Roo.get(config.el);
34657 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34662 monitorWindowResize : true,
34668 onRender : function(ct, position)
34671 this.el = Roo.get(ct);
34674 //this.fireEvent('render',this);
34678 initEvents: function()
34682 // ie scrollbar fix
34683 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34684 document.body.scroll = "no";
34685 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34686 this.el.position('relative');
34688 this.id = this.el.id;
34689 this.el.addClass("roo-layout-container");
34690 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34691 if(this.el.dom != document.body ) {
34692 this.el.on('resize', this.layout,this);
34693 this.el.on('show', this.layout,this);
34699 * Returns true if this layout is currently being updated
34700 * @return {Boolean}
34702 isUpdating : function(){
34703 return this.updating;
34707 * Suspend the LayoutManager from doing auto-layouts while
34708 * making multiple add or remove calls
34710 beginUpdate : function(){
34711 this.updating = true;
34715 * Restore auto-layouts and optionally disable the manager from performing a layout
34716 * @param {Boolean} noLayout true to disable a layout update
34718 endUpdate : function(noLayout){
34719 this.updating = false;
34725 layout: function(){
34729 onRegionResized : function(region, newSize){
34730 this.fireEvent("regionresized", region, newSize);
34734 onRegionCollapsed : function(region){
34735 this.fireEvent("regioncollapsed", region);
34738 onRegionExpanded : function(region){
34739 this.fireEvent("regionexpanded", region);
34743 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34744 * performs box-model adjustments.
34745 * @return {Object} The size as an object {width: (the width), height: (the height)}
34747 getViewSize : function()
34750 if(this.el.dom != document.body){
34751 size = this.el.getSize();
34753 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34755 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34756 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34761 * Returns the Element this layout is bound to.
34762 * @return {Roo.Element}
34764 getEl : function(){
34769 * Returns the specified region.
34770 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34771 * @return {Roo.LayoutRegion}
34773 getRegion : function(target){
34774 return this.regions[target.toLowerCase()];
34777 onWindowResize : function(){
34778 if(this.monitorWindowResize){
34785 * Ext JS Library 1.1.1
34786 * Copyright(c) 2006-2007, Ext JS, LLC.
34788 * Originally Released Under LGPL - original licence link has changed is not relivant.
34791 * <script type="text/javascript">
34794 * @class Roo.bootstrap.layout.Border
34795 * @extends Roo.bootstrap.layout.Manager
34796 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34797 * please see: examples/bootstrap/nested.html<br><br>
34799 <b>The container the layout is rendered into can be either the body element or any other element.
34800 If it is not the body element, the container needs to either be an absolute positioned element,
34801 or you will need to add "position:relative" to the css of the container. You will also need to specify
34802 the container size if it is not the body element.</b>
34805 * Create a new Border
34806 * @param {Object} config Configuration options
34808 Roo.bootstrap.layout.Border = function(config){
34809 config = config || {};
34810 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34814 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34815 if(config[region]){
34816 config[region].region = region;
34817 this.addRegion(config[region]);
34823 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34825 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34827 * Creates and adds a new region if it doesn't already exist.
34828 * @param {String} target The target region key (north, south, east, west or center).
34829 * @param {Object} config The regions config object
34830 * @return {BorderLayoutRegion} The new region
34832 addRegion : function(config)
34834 if(!this.regions[config.region]){
34835 var r = this.factory(config);
34836 this.bindRegion(r);
34838 return this.regions[config.region];
34842 bindRegion : function(r){
34843 this.regions[r.config.region] = r;
34845 r.on("visibilitychange", this.layout, this);
34846 r.on("paneladded", this.layout, this);
34847 r.on("panelremoved", this.layout, this);
34848 r.on("invalidated", this.layout, this);
34849 r.on("resized", this.onRegionResized, this);
34850 r.on("collapsed", this.onRegionCollapsed, this);
34851 r.on("expanded", this.onRegionExpanded, this);
34855 * Performs a layout update.
34857 layout : function()
34859 if(this.updating) {
34863 // render all the rebions if they have not been done alreayd?
34864 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34865 if(this.regions[region] && !this.regions[region].bodyEl){
34866 this.regions[region].onRender(this.el)
34870 var size = this.getViewSize();
34871 var w = size.width;
34872 var h = size.height;
34877 //var x = 0, y = 0;
34879 var rs = this.regions;
34880 var north = rs["north"];
34881 var south = rs["south"];
34882 var west = rs["west"];
34883 var east = rs["east"];
34884 var center = rs["center"];
34885 //if(this.hideOnLayout){ // not supported anymore
34886 //c.el.setStyle("display", "none");
34888 if(north && north.isVisible()){
34889 var b = north.getBox();
34890 var m = north.getMargins();
34891 b.width = w - (m.left+m.right);
34894 centerY = b.height + b.y + m.bottom;
34895 centerH -= centerY;
34896 north.updateBox(this.safeBox(b));
34898 if(south && south.isVisible()){
34899 var b = south.getBox();
34900 var m = south.getMargins();
34901 b.width = w - (m.left+m.right);
34903 var totalHeight = (b.height + m.top + m.bottom);
34904 b.y = h - totalHeight + m.top;
34905 centerH -= totalHeight;
34906 south.updateBox(this.safeBox(b));
34908 if(west && west.isVisible()){
34909 var b = west.getBox();
34910 var m = west.getMargins();
34911 b.height = centerH - (m.top+m.bottom);
34913 b.y = centerY + m.top;
34914 var totalWidth = (b.width + m.left + m.right);
34915 centerX += totalWidth;
34916 centerW -= totalWidth;
34917 west.updateBox(this.safeBox(b));
34919 if(east && east.isVisible()){
34920 var b = east.getBox();
34921 var m = east.getMargins();
34922 b.height = centerH - (m.top+m.bottom);
34923 var totalWidth = (b.width + m.left + m.right);
34924 b.x = w - totalWidth + m.left;
34925 b.y = centerY + m.top;
34926 centerW -= totalWidth;
34927 east.updateBox(this.safeBox(b));
34930 var m = center.getMargins();
34932 x: centerX + m.left,
34933 y: centerY + m.top,
34934 width: centerW - (m.left+m.right),
34935 height: centerH - (m.top+m.bottom)
34937 //if(this.hideOnLayout){
34938 //center.el.setStyle("display", "block");
34940 center.updateBox(this.safeBox(centerBox));
34943 this.fireEvent("layout", this);
34947 safeBox : function(box){
34948 box.width = Math.max(0, box.width);
34949 box.height = Math.max(0, box.height);
34954 * Adds a ContentPanel (or subclass) to this layout.
34955 * @param {String} target The target region key (north, south, east, west or center).
34956 * @param {Roo.ContentPanel} panel The panel to add
34957 * @return {Roo.ContentPanel} The added panel
34959 add : function(target, panel){
34961 target = target.toLowerCase();
34962 return this.regions[target].add(panel);
34966 * Remove a ContentPanel (or subclass) to this layout.
34967 * @param {String} target The target region key (north, south, east, west or center).
34968 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34969 * @return {Roo.ContentPanel} The removed panel
34971 remove : function(target, panel){
34972 target = target.toLowerCase();
34973 return this.regions[target].remove(panel);
34977 * Searches all regions for a panel with the specified id
34978 * @param {String} panelId
34979 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34981 findPanel : function(panelId){
34982 var rs = this.regions;
34983 for(var target in rs){
34984 if(typeof rs[target] != "function"){
34985 var p = rs[target].getPanel(panelId);
34995 * Searches all regions for a panel with the specified id and activates (shows) it.
34996 * @param {String/ContentPanel} panelId The panels id or the panel itself
34997 * @return {Roo.ContentPanel} The shown panel or null
34999 showPanel : function(panelId) {
35000 var rs = this.regions;
35001 for(var target in rs){
35002 var r = rs[target];
35003 if(typeof r != "function"){
35004 if(r.hasPanel(panelId)){
35005 return r.showPanel(panelId);
35013 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35014 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35017 restoreState : function(provider){
35019 provider = Roo.state.Manager;
35021 var sm = new Roo.LayoutStateManager();
35022 sm.init(this, provider);
35028 * Adds a xtype elements to the layout.
35032 xtype : 'ContentPanel',
35039 xtype : 'NestedLayoutPanel',
35045 items : [ ... list of content panels or nested layout panels.. ]
35049 * @param {Object} cfg Xtype definition of item to add.
35051 addxtype : function(cfg)
35053 // basically accepts a pannel...
35054 // can accept a layout region..!?!?
35055 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35058 // theory? children can only be panels??
35060 //if (!cfg.xtype.match(/Panel$/)) {
35065 if (typeof(cfg.region) == 'undefined') {
35066 Roo.log("Failed to add Panel, region was not set");
35070 var region = cfg.region;
35076 xitems = cfg.items;
35083 case 'Content': // ContentPanel (el, cfg)
35084 case 'Scroll': // ContentPanel (el, cfg)
35086 cfg.autoCreate = true;
35087 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35089 // var el = this.el.createChild();
35090 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35093 this.add(region, ret);
35097 case 'TreePanel': // our new panel!
35098 cfg.el = this.el.createChild();
35099 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35100 this.add(region, ret);
35105 // create a new Layout (which is a Border Layout...
35107 var clayout = cfg.layout;
35108 clayout.el = this.el.createChild();
35109 clayout.items = clayout.items || [];
35113 // replace this exitems with the clayout ones..
35114 xitems = clayout.items;
35116 // force background off if it's in center...
35117 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35118 cfg.background = false;
35120 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35123 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35124 //console.log('adding nested layout panel ' + cfg.toSource());
35125 this.add(region, ret);
35126 nb = {}; /// find first...
35131 // needs grid and region
35133 //var el = this.getRegion(region).el.createChild();
35135 *var el = this.el.createChild();
35136 // create the grid first...
35137 cfg.grid.container = el;
35138 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35141 if (region == 'center' && this.active ) {
35142 cfg.background = false;
35145 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35147 this.add(region, ret);
35149 if (cfg.background) {
35150 // render grid on panel activation (if panel background)
35151 ret.on('activate', function(gp) {
35152 if (!gp.grid.rendered) {
35153 // gp.grid.render(el);
35157 // cfg.grid.render(el);
35163 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35164 // it was the old xcomponent building that caused this before.
35165 // espeically if border is the top element in the tree.
35175 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35177 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35178 this.add(region, ret);
35182 throw "Can not add '" + cfg.xtype + "' to Border";
35188 this.beginUpdate();
35192 Roo.each(xitems, function(i) {
35193 region = nb && i.region ? i.region : false;
35195 var add = ret.addxtype(i);
35198 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35199 if (!i.background) {
35200 abn[region] = nb[region] ;
35207 // make the last non-background panel active..
35208 //if (nb) { Roo.log(abn); }
35211 for(var r in abn) {
35212 region = this.getRegion(r);
35214 // tried using nb[r], but it does not work..
35216 region.showPanel(abn[r]);
35227 factory : function(cfg)
35230 var validRegions = Roo.bootstrap.layout.Border.regions;
35232 var target = cfg.region;
35235 var r = Roo.bootstrap.layout;
35239 return new r.North(cfg);
35241 return new r.South(cfg);
35243 return new r.East(cfg);
35245 return new r.West(cfg);
35247 return new r.Center(cfg);
35249 throw 'Layout region "'+target+'" not supported.';
35256 * Ext JS Library 1.1.1
35257 * Copyright(c) 2006-2007, Ext JS, LLC.
35259 * Originally Released Under LGPL - original licence link has changed is not relivant.
35262 * <script type="text/javascript">
35266 * @class Roo.bootstrap.layout.Basic
35267 * @extends Roo.util.Observable
35268 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35269 * and does not have a titlebar, tabs or any other features. All it does is size and position
35270 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35271 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35272 * @cfg {string} region the region that it inhabits..
35273 * @cfg {bool} skipConfig skip config?
35277 Roo.bootstrap.layout.Basic = function(config){
35279 this.mgr = config.mgr;
35281 this.position = config.region;
35283 var skipConfig = config.skipConfig;
35287 * @scope Roo.BasicLayoutRegion
35291 * @event beforeremove
35292 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35293 * @param {Roo.LayoutRegion} this
35294 * @param {Roo.ContentPanel} panel The panel
35295 * @param {Object} e The cancel event object
35297 "beforeremove" : true,
35299 * @event invalidated
35300 * Fires when the layout for this region is changed.
35301 * @param {Roo.LayoutRegion} this
35303 "invalidated" : true,
35305 * @event visibilitychange
35306 * Fires when this region is shown or hidden
35307 * @param {Roo.LayoutRegion} this
35308 * @param {Boolean} visibility true or false
35310 "visibilitychange" : true,
35312 * @event paneladded
35313 * Fires when a panel is added.
35314 * @param {Roo.LayoutRegion} this
35315 * @param {Roo.ContentPanel} panel The panel
35317 "paneladded" : true,
35319 * @event panelremoved
35320 * Fires when a panel is removed.
35321 * @param {Roo.LayoutRegion} this
35322 * @param {Roo.ContentPanel} panel The panel
35324 "panelremoved" : true,
35326 * @event beforecollapse
35327 * Fires when this region before collapse.
35328 * @param {Roo.LayoutRegion} this
35330 "beforecollapse" : true,
35333 * Fires when this region is collapsed.
35334 * @param {Roo.LayoutRegion} this
35336 "collapsed" : true,
35339 * Fires when this region is expanded.
35340 * @param {Roo.LayoutRegion} this
35345 * Fires when this region is slid into view.
35346 * @param {Roo.LayoutRegion} this
35348 "slideshow" : true,
35351 * Fires when this region slides out of view.
35352 * @param {Roo.LayoutRegion} this
35354 "slidehide" : true,
35356 * @event panelactivated
35357 * Fires when a panel is activated.
35358 * @param {Roo.LayoutRegion} this
35359 * @param {Roo.ContentPanel} panel The activated panel
35361 "panelactivated" : true,
35364 * Fires when the user resizes this region.
35365 * @param {Roo.LayoutRegion} this
35366 * @param {Number} newSize The new size (width for east/west, height for north/south)
35370 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35371 this.panels = new Roo.util.MixedCollection();
35372 this.panels.getKey = this.getPanelId.createDelegate(this);
35374 this.activePanel = null;
35375 // ensure listeners are added...
35377 if (config.listeners || config.events) {
35378 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35379 listeners : config.listeners || {},
35380 events : config.events || {}
35384 if(skipConfig !== true){
35385 this.applyConfig(config);
35389 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35391 getPanelId : function(p){
35395 applyConfig : function(config){
35396 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35397 this.config = config;
35402 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35403 * the width, for horizontal (north, south) the height.
35404 * @param {Number} newSize The new width or height
35406 resizeTo : function(newSize){
35407 var el = this.el ? this.el :
35408 (this.activePanel ? this.activePanel.getEl() : null);
35410 switch(this.position){
35413 el.setWidth(newSize);
35414 this.fireEvent("resized", this, newSize);
35418 el.setHeight(newSize);
35419 this.fireEvent("resized", this, newSize);
35425 getBox : function(){
35426 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35429 getMargins : function(){
35430 return this.margins;
35433 updateBox : function(box){
35435 var el = this.activePanel.getEl();
35436 el.dom.style.left = box.x + "px";
35437 el.dom.style.top = box.y + "px";
35438 this.activePanel.setSize(box.width, box.height);
35442 * Returns the container element for this region.
35443 * @return {Roo.Element}
35445 getEl : function(){
35446 return this.activePanel;
35450 * Returns true if this region is currently visible.
35451 * @return {Boolean}
35453 isVisible : function(){
35454 return this.activePanel ? true : false;
35457 setActivePanel : function(panel){
35458 panel = this.getPanel(panel);
35459 if(this.activePanel && this.activePanel != panel){
35460 this.activePanel.setActiveState(false);
35461 this.activePanel.getEl().setLeftTop(-10000,-10000);
35463 this.activePanel = panel;
35464 panel.setActiveState(true);
35466 panel.setSize(this.box.width, this.box.height);
35468 this.fireEvent("panelactivated", this, panel);
35469 this.fireEvent("invalidated");
35473 * Show the specified panel.
35474 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35475 * @return {Roo.ContentPanel} The shown panel or null
35477 showPanel : function(panel){
35478 panel = this.getPanel(panel);
35480 this.setActivePanel(panel);
35486 * Get the active panel for this region.
35487 * @return {Roo.ContentPanel} The active panel or null
35489 getActivePanel : function(){
35490 return this.activePanel;
35494 * Add the passed ContentPanel(s)
35495 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35496 * @return {Roo.ContentPanel} The panel added (if only one was added)
35498 add : function(panel){
35499 if(arguments.length > 1){
35500 for(var i = 0, len = arguments.length; i < len; i++) {
35501 this.add(arguments[i]);
35505 if(this.hasPanel(panel)){
35506 this.showPanel(panel);
35509 var el = panel.getEl();
35510 if(el.dom.parentNode != this.mgr.el.dom){
35511 this.mgr.el.dom.appendChild(el.dom);
35513 if(panel.setRegion){
35514 panel.setRegion(this);
35516 this.panels.add(panel);
35517 el.setStyle("position", "absolute");
35518 if(!panel.background){
35519 this.setActivePanel(panel);
35520 if(this.config.initialSize && this.panels.getCount()==1){
35521 this.resizeTo(this.config.initialSize);
35524 this.fireEvent("paneladded", this, panel);
35529 * Returns true if the panel is in this region.
35530 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35531 * @return {Boolean}
35533 hasPanel : function(panel){
35534 if(typeof panel == "object"){ // must be panel obj
35535 panel = panel.getId();
35537 return this.getPanel(panel) ? true : false;
35541 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35542 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35543 * @param {Boolean} preservePanel Overrides the config preservePanel option
35544 * @return {Roo.ContentPanel} The panel that was removed
35546 remove : function(panel, preservePanel){
35547 panel = this.getPanel(panel);
35552 this.fireEvent("beforeremove", this, panel, e);
35553 if(e.cancel === true){
35556 var panelId = panel.getId();
35557 this.panels.removeKey(panelId);
35562 * Returns the panel specified or null if it's not in this region.
35563 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35564 * @return {Roo.ContentPanel}
35566 getPanel : function(id){
35567 if(typeof id == "object"){ // must be panel obj
35570 return this.panels.get(id);
35574 * Returns this regions position (north/south/east/west/center).
35577 getPosition: function(){
35578 return this.position;
35582 * Ext JS Library 1.1.1
35583 * Copyright(c) 2006-2007, Ext JS, LLC.
35585 * Originally Released Under LGPL - original licence link has changed is not relivant.
35588 * <script type="text/javascript">
35592 * @class Roo.bootstrap.layout.Region
35593 * @extends Roo.bootstrap.layout.Basic
35594 * This class represents a region in a layout manager.
35596 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35597 * @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})
35598 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35599 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35600 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35601 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35602 * @cfg {String} title The title for the region (overrides panel titles)
35603 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35604 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35605 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35606 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35607 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35608 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35609 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35610 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35611 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35612 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35614 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35615 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35616 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35617 * @cfg {Number} width For East/West panels
35618 * @cfg {Number} height For North/South panels
35619 * @cfg {Boolean} split To show the splitter
35620 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35622 * @cfg {string} cls Extra CSS classes to add to region
35624 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35625 * @cfg {string} region the region that it inhabits..
35628 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35629 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35631 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35632 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35633 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35635 Roo.bootstrap.layout.Region = function(config)
35637 this.applyConfig(config);
35639 var mgr = config.mgr;
35640 var pos = config.region;
35641 config.skipConfig = true;
35642 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35645 this.onRender(mgr.el);
35648 this.visible = true;
35649 this.collapsed = false;
35650 this.unrendered_panels = [];
35653 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35655 position: '', // set by wrapper (eg. north/south etc..)
35656 unrendered_panels : null, // unrendered panels.
35657 createBody : function(){
35658 /** This region's body element
35659 * @type Roo.Element */
35660 this.bodyEl = this.el.createChild({
35662 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35666 onRender: function(ctr, pos)
35668 var dh = Roo.DomHelper;
35669 /** This region's container element
35670 * @type Roo.Element */
35671 this.el = dh.append(ctr.dom, {
35673 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35675 /** This region's title element
35676 * @type Roo.Element */
35678 this.titleEl = dh.append(this.el.dom,
35681 unselectable: "on",
35682 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35684 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35685 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35688 this.titleEl.enableDisplayMode();
35689 /** This region's title text element
35690 * @type HTMLElement */
35691 this.titleTextEl = this.titleEl.dom.firstChild;
35692 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35694 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35695 this.closeBtn.enableDisplayMode();
35696 this.closeBtn.on("click", this.closeClicked, this);
35697 this.closeBtn.hide();
35699 this.createBody(this.config);
35700 if(this.config.hideWhenEmpty){
35702 this.on("paneladded", this.validateVisibility, this);
35703 this.on("panelremoved", this.validateVisibility, this);
35705 if(this.autoScroll){
35706 this.bodyEl.setStyle("overflow", "auto");
35708 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35710 //if(c.titlebar !== false){
35711 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35712 this.titleEl.hide();
35714 this.titleEl.show();
35715 if(this.config.title){
35716 this.titleTextEl.innerHTML = this.config.title;
35720 if(this.config.collapsed){
35721 this.collapse(true);
35723 if(this.config.hidden){
35727 if (this.unrendered_panels && this.unrendered_panels.length) {
35728 for (var i =0;i< this.unrendered_panels.length; i++) {
35729 this.add(this.unrendered_panels[i]);
35731 this.unrendered_panels = null;
35737 applyConfig : function(c)
35740 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35741 var dh = Roo.DomHelper;
35742 if(c.titlebar !== false){
35743 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35744 this.collapseBtn.on("click", this.collapse, this);
35745 this.collapseBtn.enableDisplayMode();
35747 if(c.showPin === true || this.showPin){
35748 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35749 this.stickBtn.enableDisplayMode();
35750 this.stickBtn.on("click", this.expand, this);
35751 this.stickBtn.hide();
35756 /** This region's collapsed element
35757 * @type Roo.Element */
35760 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35761 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35764 if(c.floatable !== false){
35765 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35766 this.collapsedEl.on("click", this.collapseClick, this);
35769 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35770 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35771 id: "message", unselectable: "on", style:{"float":"left"}});
35772 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35774 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35775 this.expandBtn.on("click", this.expand, this);
35779 if(this.collapseBtn){
35780 this.collapseBtn.setVisible(c.collapsible == true);
35783 this.cmargins = c.cmargins || this.cmargins ||
35784 (this.position == "west" || this.position == "east" ?
35785 {top: 0, left: 2, right:2, bottom: 0} :
35786 {top: 2, left: 0, right:0, bottom: 2});
35788 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35791 this.bottomTabs = c.tabPosition != "top";
35793 this.autoScroll = c.autoScroll || false;
35798 this.duration = c.duration || .30;
35799 this.slideDuration = c.slideDuration || .45;
35804 * Returns true if this region is currently visible.
35805 * @return {Boolean}
35807 isVisible : function(){
35808 return this.visible;
35812 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35813 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35815 //setCollapsedTitle : function(title){
35816 // title = title || " ";
35817 // if(this.collapsedTitleTextEl){
35818 // this.collapsedTitleTextEl.innerHTML = title;
35822 getBox : function(){
35824 // if(!this.collapsed){
35825 b = this.el.getBox(false, true);
35827 // b = this.collapsedEl.getBox(false, true);
35832 getMargins : function(){
35833 return this.margins;
35834 //return this.collapsed ? this.cmargins : this.margins;
35837 highlight : function(){
35838 this.el.addClass("x-layout-panel-dragover");
35841 unhighlight : function(){
35842 this.el.removeClass("x-layout-panel-dragover");
35845 updateBox : function(box)
35847 if (!this.bodyEl) {
35848 return; // not rendered yet..
35852 if(!this.collapsed){
35853 this.el.dom.style.left = box.x + "px";
35854 this.el.dom.style.top = box.y + "px";
35855 this.updateBody(box.width, box.height);
35857 this.collapsedEl.dom.style.left = box.x + "px";
35858 this.collapsedEl.dom.style.top = box.y + "px";
35859 this.collapsedEl.setSize(box.width, box.height);
35862 this.tabs.autoSizeTabs();
35866 updateBody : function(w, h)
35869 this.el.setWidth(w);
35870 w -= this.el.getBorderWidth("rl");
35871 if(this.config.adjustments){
35872 w += this.config.adjustments[0];
35875 if(h !== null && h > 0){
35876 this.el.setHeight(h);
35877 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35878 h -= this.el.getBorderWidth("tb");
35879 if(this.config.adjustments){
35880 h += this.config.adjustments[1];
35882 this.bodyEl.setHeight(h);
35884 h = this.tabs.syncHeight(h);
35887 if(this.panelSize){
35888 w = w !== null ? w : this.panelSize.width;
35889 h = h !== null ? h : this.panelSize.height;
35891 if(this.activePanel){
35892 var el = this.activePanel.getEl();
35893 w = w !== null ? w : el.getWidth();
35894 h = h !== null ? h : el.getHeight();
35895 this.panelSize = {width: w, height: h};
35896 this.activePanel.setSize(w, h);
35898 if(Roo.isIE && this.tabs){
35899 this.tabs.el.repaint();
35904 * Returns the container element for this region.
35905 * @return {Roo.Element}
35907 getEl : function(){
35912 * Hides this region.
35915 //if(!this.collapsed){
35916 this.el.dom.style.left = "-2000px";
35919 // this.collapsedEl.dom.style.left = "-2000px";
35920 // this.collapsedEl.hide();
35922 this.visible = false;
35923 this.fireEvent("visibilitychange", this, false);
35927 * Shows this region if it was previously hidden.
35930 //if(!this.collapsed){
35933 // this.collapsedEl.show();
35935 this.visible = true;
35936 this.fireEvent("visibilitychange", this, true);
35939 closeClicked : function(){
35940 if(this.activePanel){
35941 this.remove(this.activePanel);
35945 collapseClick : function(e){
35947 e.stopPropagation();
35950 e.stopPropagation();
35956 * Collapses this region.
35957 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35960 collapse : function(skipAnim, skipCheck = false){
35961 if(this.collapsed) {
35965 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35967 this.collapsed = true;
35969 this.split.el.hide();
35971 if(this.config.animate && skipAnim !== true){
35972 this.fireEvent("invalidated", this);
35973 this.animateCollapse();
35975 this.el.setLocation(-20000,-20000);
35977 this.collapsedEl.show();
35978 this.fireEvent("collapsed", this);
35979 this.fireEvent("invalidated", this);
35985 animateCollapse : function(){
35990 * Expands this region if it was previously collapsed.
35991 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35992 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35995 expand : function(e, skipAnim){
35997 e.stopPropagation();
35999 if(!this.collapsed || this.el.hasActiveFx()) {
36003 this.afterSlideIn();
36006 this.collapsed = false;
36007 if(this.config.animate && skipAnim !== true){
36008 this.animateExpand();
36012 this.split.el.show();
36014 this.collapsedEl.setLocation(-2000,-2000);
36015 this.collapsedEl.hide();
36016 this.fireEvent("invalidated", this);
36017 this.fireEvent("expanded", this);
36021 animateExpand : function(){
36025 initTabs : function()
36027 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36029 var ts = new Roo.bootstrap.panel.Tabs({
36030 el: this.bodyEl.dom,
36031 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36032 disableTooltips: this.config.disableTabTips,
36033 toolbar : this.config.toolbar
36036 if(this.config.hideTabs){
36037 ts.stripWrap.setDisplayed(false);
36040 ts.resizeTabs = this.config.resizeTabs === true;
36041 ts.minTabWidth = this.config.minTabWidth || 40;
36042 ts.maxTabWidth = this.config.maxTabWidth || 250;
36043 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36044 ts.monitorResize = false;
36045 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36046 ts.bodyEl.addClass('roo-layout-tabs-body');
36047 this.panels.each(this.initPanelAsTab, this);
36050 initPanelAsTab : function(panel){
36051 var ti = this.tabs.addTab(
36055 this.config.closeOnTab && panel.isClosable(),
36058 if(panel.tabTip !== undefined){
36059 ti.setTooltip(panel.tabTip);
36061 ti.on("activate", function(){
36062 this.setActivePanel(panel);
36065 if(this.config.closeOnTab){
36066 ti.on("beforeclose", function(t, e){
36068 this.remove(panel);
36072 panel.tabItem = ti;
36077 updatePanelTitle : function(panel, title)
36079 if(this.activePanel == panel){
36080 this.updateTitle(title);
36083 var ti = this.tabs.getTab(panel.getEl().id);
36085 if(panel.tabTip !== undefined){
36086 ti.setTooltip(panel.tabTip);
36091 updateTitle : function(title){
36092 if(this.titleTextEl && !this.config.title){
36093 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36097 setActivePanel : function(panel)
36099 panel = this.getPanel(panel);
36100 if(this.activePanel && this.activePanel != panel){
36101 if(this.activePanel.setActiveState(false) === false){
36105 this.activePanel = panel;
36106 panel.setActiveState(true);
36107 if(this.panelSize){
36108 panel.setSize(this.panelSize.width, this.panelSize.height);
36111 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36113 this.updateTitle(panel.getTitle());
36115 this.fireEvent("invalidated", this);
36117 this.fireEvent("panelactivated", this, panel);
36121 * Shows the specified panel.
36122 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36123 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36125 showPanel : function(panel)
36127 panel = this.getPanel(panel);
36130 var tab = this.tabs.getTab(panel.getEl().id);
36131 if(tab.isHidden()){
36132 this.tabs.unhideTab(tab.id);
36136 this.setActivePanel(panel);
36143 * Get the active panel for this region.
36144 * @return {Roo.ContentPanel} The active panel or null
36146 getActivePanel : function(){
36147 return this.activePanel;
36150 validateVisibility : function(){
36151 if(this.panels.getCount() < 1){
36152 this.updateTitle(" ");
36153 this.closeBtn.hide();
36156 if(!this.isVisible()){
36163 * Adds the passed ContentPanel(s) to this region.
36164 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36165 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36167 add : function(panel)
36169 if(arguments.length > 1){
36170 for(var i = 0, len = arguments.length; i < len; i++) {
36171 this.add(arguments[i]);
36176 // if we have not been rendered yet, then we can not really do much of this..
36177 if (!this.bodyEl) {
36178 this.unrendered_panels.push(panel);
36185 if(this.hasPanel(panel)){
36186 this.showPanel(panel);
36189 panel.setRegion(this);
36190 this.panels.add(panel);
36191 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36192 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36193 // and hide them... ???
36194 this.bodyEl.dom.appendChild(panel.getEl().dom);
36195 if(panel.background !== true){
36196 this.setActivePanel(panel);
36198 this.fireEvent("paneladded", this, panel);
36205 this.initPanelAsTab(panel);
36209 if(panel.background !== true){
36210 this.tabs.activate(panel.getEl().id);
36212 this.fireEvent("paneladded", this, panel);
36217 * Hides the tab for the specified panel.
36218 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36220 hidePanel : function(panel){
36221 if(this.tabs && (panel = this.getPanel(panel))){
36222 this.tabs.hideTab(panel.getEl().id);
36227 * Unhides the tab for a previously hidden panel.
36228 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36230 unhidePanel : function(panel){
36231 if(this.tabs && (panel = this.getPanel(panel))){
36232 this.tabs.unhideTab(panel.getEl().id);
36236 clearPanels : function(){
36237 while(this.panels.getCount() > 0){
36238 this.remove(this.panels.first());
36243 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36244 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36245 * @param {Boolean} preservePanel Overrides the config preservePanel option
36246 * @return {Roo.ContentPanel} The panel that was removed
36248 remove : function(panel, preservePanel)
36250 panel = this.getPanel(panel);
36255 this.fireEvent("beforeremove", this, panel, e);
36256 if(e.cancel === true){
36259 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36260 var panelId = panel.getId();
36261 this.panels.removeKey(panelId);
36263 document.body.appendChild(panel.getEl().dom);
36266 this.tabs.removeTab(panel.getEl().id);
36267 }else if (!preservePanel){
36268 this.bodyEl.dom.removeChild(panel.getEl().dom);
36270 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36271 var p = this.panels.first();
36272 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36273 tempEl.appendChild(p.getEl().dom);
36274 this.bodyEl.update("");
36275 this.bodyEl.dom.appendChild(p.getEl().dom);
36277 this.updateTitle(p.getTitle());
36279 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36280 this.setActivePanel(p);
36282 panel.setRegion(null);
36283 if(this.activePanel == panel){
36284 this.activePanel = null;
36286 if(this.config.autoDestroy !== false && preservePanel !== true){
36287 try{panel.destroy();}catch(e){}
36289 this.fireEvent("panelremoved", this, panel);
36294 * Returns the TabPanel component used by this region
36295 * @return {Roo.TabPanel}
36297 getTabs : function(){
36301 createTool : function(parentEl, className){
36302 var btn = Roo.DomHelper.append(parentEl, {
36304 cls: "x-layout-tools-button",
36307 cls: "roo-layout-tools-button-inner " + className,
36311 btn.addClassOnOver("roo-layout-tools-button-over");
36316 * Ext JS Library 1.1.1
36317 * Copyright(c) 2006-2007, Ext JS, LLC.
36319 * Originally Released Under LGPL - original licence link has changed is not relivant.
36322 * <script type="text/javascript">
36328 * @class Roo.SplitLayoutRegion
36329 * @extends Roo.LayoutRegion
36330 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36332 Roo.bootstrap.layout.Split = function(config){
36333 this.cursor = config.cursor;
36334 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36337 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36339 splitTip : "Drag to resize.",
36340 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36341 useSplitTips : false,
36343 applyConfig : function(config){
36344 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36347 onRender : function(ctr,pos) {
36349 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36350 if(!this.config.split){
36355 var splitEl = Roo.DomHelper.append(ctr.dom, {
36357 id: this.el.id + "-split",
36358 cls: "roo-layout-split roo-layout-split-"+this.position,
36361 /** The SplitBar for this region
36362 * @type Roo.SplitBar */
36363 // does not exist yet...
36364 Roo.log([this.position, this.orientation]);
36366 this.split = new Roo.bootstrap.SplitBar({
36367 dragElement : splitEl,
36368 resizingElement: this.el,
36369 orientation : this.orientation
36372 this.split.on("moved", this.onSplitMove, this);
36373 this.split.useShim = this.config.useShim === true;
36374 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36375 if(this.useSplitTips){
36376 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36378 //if(config.collapsible){
36379 // this.split.el.on("dblclick", this.collapse, this);
36382 if(typeof this.config.minSize != "undefined"){
36383 this.split.minSize = this.config.minSize;
36385 if(typeof this.config.maxSize != "undefined"){
36386 this.split.maxSize = this.config.maxSize;
36388 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36389 this.hideSplitter();
36394 getHMaxSize : function(){
36395 var cmax = this.config.maxSize || 10000;
36396 var center = this.mgr.getRegion("center");
36397 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36400 getVMaxSize : function(){
36401 var cmax = this.config.maxSize || 10000;
36402 var center = this.mgr.getRegion("center");
36403 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36406 onSplitMove : function(split, newSize){
36407 this.fireEvent("resized", this, newSize);
36411 * Returns the {@link Roo.SplitBar} for this region.
36412 * @return {Roo.SplitBar}
36414 getSplitBar : function(){
36419 this.hideSplitter();
36420 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36423 hideSplitter : function(){
36425 this.split.el.setLocation(-2000,-2000);
36426 this.split.el.hide();
36432 this.split.el.show();
36434 Roo.bootstrap.layout.Split.superclass.show.call(this);
36437 beforeSlide: function(){
36438 if(Roo.isGecko){// firefox overflow auto bug workaround
36439 this.bodyEl.clip();
36441 this.tabs.bodyEl.clip();
36443 if(this.activePanel){
36444 this.activePanel.getEl().clip();
36446 if(this.activePanel.beforeSlide){
36447 this.activePanel.beforeSlide();
36453 afterSlide : function(){
36454 if(Roo.isGecko){// firefox overflow auto bug workaround
36455 this.bodyEl.unclip();
36457 this.tabs.bodyEl.unclip();
36459 if(this.activePanel){
36460 this.activePanel.getEl().unclip();
36461 if(this.activePanel.afterSlide){
36462 this.activePanel.afterSlide();
36468 initAutoHide : function(){
36469 if(this.autoHide !== false){
36470 if(!this.autoHideHd){
36471 var st = new Roo.util.DelayedTask(this.slideIn, this);
36472 this.autoHideHd = {
36473 "mouseout": function(e){
36474 if(!e.within(this.el, true)){
36478 "mouseover" : function(e){
36484 this.el.on(this.autoHideHd);
36488 clearAutoHide : function(){
36489 if(this.autoHide !== false){
36490 this.el.un("mouseout", this.autoHideHd.mouseout);
36491 this.el.un("mouseover", this.autoHideHd.mouseover);
36495 clearMonitor : function(){
36496 Roo.get(document).un("click", this.slideInIf, this);
36499 // these names are backwards but not changed for compat
36500 slideOut : function(){
36501 if(this.isSlid || this.el.hasActiveFx()){
36504 this.isSlid = true;
36505 if(this.collapseBtn){
36506 this.collapseBtn.hide();
36508 this.closeBtnState = this.closeBtn.getStyle('display');
36509 this.closeBtn.hide();
36511 this.stickBtn.show();
36514 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36515 this.beforeSlide();
36516 this.el.setStyle("z-index", 10001);
36517 this.el.slideIn(this.getSlideAnchor(), {
36518 callback: function(){
36520 this.initAutoHide();
36521 Roo.get(document).on("click", this.slideInIf, this);
36522 this.fireEvent("slideshow", this);
36529 afterSlideIn : function(){
36530 this.clearAutoHide();
36531 this.isSlid = false;
36532 this.clearMonitor();
36533 this.el.setStyle("z-index", "");
36534 if(this.collapseBtn){
36535 this.collapseBtn.show();
36537 this.closeBtn.setStyle('display', this.closeBtnState);
36539 this.stickBtn.hide();
36541 this.fireEvent("slidehide", this);
36544 slideIn : function(cb){
36545 if(!this.isSlid || this.el.hasActiveFx()){
36549 this.isSlid = false;
36550 this.beforeSlide();
36551 this.el.slideOut(this.getSlideAnchor(), {
36552 callback: function(){
36553 this.el.setLeftTop(-10000, -10000);
36555 this.afterSlideIn();
36563 slideInIf : function(e){
36564 if(!e.within(this.el)){
36569 animateCollapse : function(){
36570 this.beforeSlide();
36571 this.el.setStyle("z-index", 20000);
36572 var anchor = this.getSlideAnchor();
36573 this.el.slideOut(anchor, {
36574 callback : function(){
36575 this.el.setStyle("z-index", "");
36576 this.collapsedEl.slideIn(anchor, {duration:.3});
36578 this.el.setLocation(-10000,-10000);
36580 this.fireEvent("collapsed", this);
36587 animateExpand : function(){
36588 this.beforeSlide();
36589 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36590 this.el.setStyle("z-index", 20000);
36591 this.collapsedEl.hide({
36594 this.el.slideIn(this.getSlideAnchor(), {
36595 callback : function(){
36596 this.el.setStyle("z-index", "");
36599 this.split.el.show();
36601 this.fireEvent("invalidated", this);
36602 this.fireEvent("expanded", this);
36630 getAnchor : function(){
36631 return this.anchors[this.position];
36634 getCollapseAnchor : function(){
36635 return this.canchors[this.position];
36638 getSlideAnchor : function(){
36639 return this.sanchors[this.position];
36642 getAlignAdj : function(){
36643 var cm = this.cmargins;
36644 switch(this.position){
36660 getExpandAdj : function(){
36661 var c = this.collapsedEl, cm = this.cmargins;
36662 switch(this.position){
36664 return [-(cm.right+c.getWidth()+cm.left), 0];
36667 return [cm.right+c.getWidth()+cm.left, 0];
36670 return [0, -(cm.top+cm.bottom+c.getHeight())];
36673 return [0, cm.top+cm.bottom+c.getHeight()];
36679 * Ext JS Library 1.1.1
36680 * Copyright(c) 2006-2007, Ext JS, LLC.
36682 * Originally Released Under LGPL - original licence link has changed is not relivant.
36685 * <script type="text/javascript">
36688 * These classes are private internal classes
36690 Roo.bootstrap.layout.Center = function(config){
36691 config.region = "center";
36692 Roo.bootstrap.layout.Region.call(this, config);
36693 this.visible = true;
36694 this.minWidth = config.minWidth || 20;
36695 this.minHeight = config.minHeight || 20;
36698 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36700 // center panel can't be hidden
36704 // center panel can't be hidden
36707 getMinWidth: function(){
36708 return this.minWidth;
36711 getMinHeight: function(){
36712 return this.minHeight;
36725 Roo.bootstrap.layout.North = function(config)
36727 config.region = 'north';
36728 config.cursor = 'n-resize';
36730 Roo.bootstrap.layout.Split.call(this, config);
36734 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36735 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36736 this.split.el.addClass("roo-layout-split-v");
36738 var size = config.initialSize || config.height;
36739 if(typeof size != "undefined"){
36740 this.el.setHeight(size);
36743 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36745 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36749 getBox : function(){
36750 if(this.collapsed){
36751 return this.collapsedEl.getBox();
36753 var box = this.el.getBox();
36755 box.height += this.split.el.getHeight();
36760 updateBox : function(box){
36761 if(this.split && !this.collapsed){
36762 box.height -= this.split.el.getHeight();
36763 this.split.el.setLeft(box.x);
36764 this.split.el.setTop(box.y+box.height);
36765 this.split.el.setWidth(box.width);
36767 if(this.collapsed){
36768 this.updateBody(box.width, null);
36770 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36778 Roo.bootstrap.layout.South = function(config){
36779 config.region = 'south';
36780 config.cursor = 's-resize';
36781 Roo.bootstrap.layout.Split.call(this, config);
36783 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36784 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36785 this.split.el.addClass("roo-layout-split-v");
36787 var size = config.initialSize || config.height;
36788 if(typeof size != "undefined"){
36789 this.el.setHeight(size);
36793 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36794 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36795 getBox : function(){
36796 if(this.collapsed){
36797 return this.collapsedEl.getBox();
36799 var box = this.el.getBox();
36801 var sh = this.split.el.getHeight();
36808 updateBox : function(box){
36809 if(this.split && !this.collapsed){
36810 var sh = this.split.el.getHeight();
36813 this.split.el.setLeft(box.x);
36814 this.split.el.setTop(box.y-sh);
36815 this.split.el.setWidth(box.width);
36817 if(this.collapsed){
36818 this.updateBody(box.width, null);
36820 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36824 Roo.bootstrap.layout.East = function(config){
36825 config.region = "east";
36826 config.cursor = "e-resize";
36827 Roo.bootstrap.layout.Split.call(this, config);
36829 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36830 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36831 this.split.el.addClass("roo-layout-split-h");
36833 var size = config.initialSize || config.width;
36834 if(typeof size != "undefined"){
36835 this.el.setWidth(size);
36838 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36839 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36840 getBox : function(){
36841 if(this.collapsed){
36842 return this.collapsedEl.getBox();
36844 var box = this.el.getBox();
36846 var sw = this.split.el.getWidth();
36853 updateBox : function(box){
36854 if(this.split && !this.collapsed){
36855 var sw = this.split.el.getWidth();
36857 this.split.el.setLeft(box.x);
36858 this.split.el.setTop(box.y);
36859 this.split.el.setHeight(box.height);
36862 if(this.collapsed){
36863 this.updateBody(null, box.height);
36865 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36869 Roo.bootstrap.layout.West = function(config){
36870 config.region = "west";
36871 config.cursor = "w-resize";
36873 Roo.bootstrap.layout.Split.call(this, config);
36875 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36876 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36877 this.split.el.addClass("roo-layout-split-h");
36881 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36882 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36884 onRender: function(ctr, pos)
36886 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36887 var size = this.config.initialSize || this.config.width;
36888 if(typeof size != "undefined"){
36889 this.el.setWidth(size);
36893 getBox : function(){
36894 if(this.collapsed){
36895 return this.collapsedEl.getBox();
36897 var box = this.el.getBox();
36899 box.width += this.split.el.getWidth();
36904 updateBox : function(box){
36905 if(this.split && !this.collapsed){
36906 var sw = this.split.el.getWidth();
36908 this.split.el.setLeft(box.x+box.width);
36909 this.split.el.setTop(box.y);
36910 this.split.el.setHeight(box.height);
36912 if(this.collapsed){
36913 this.updateBody(null, box.height);
36915 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36918 Roo.namespace("Roo.bootstrap.panel");/*
36920 * Ext JS Library 1.1.1
36921 * Copyright(c) 2006-2007, Ext JS, LLC.
36923 * Originally Released Under LGPL - original licence link has changed is not relivant.
36926 * <script type="text/javascript">
36929 * @class Roo.ContentPanel
36930 * @extends Roo.util.Observable
36931 * A basic ContentPanel element.
36932 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36933 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36934 * @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
36935 * @cfg {Boolean} closable True if the panel can be closed/removed
36936 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36937 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36938 * @cfg {Toolbar} toolbar A toolbar for this panel
36939 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36940 * @cfg {String} title The title for this panel
36941 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36942 * @cfg {String} url Calls {@link #setUrl} with this value
36943 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36944 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36945 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36946 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36947 * @cfg {Boolean} badges render the badges
36950 * Create a new ContentPanel.
36951 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36952 * @param {String/Object} config A string to set only the title or a config object
36953 * @param {String} content (optional) Set the HTML content for this panel
36954 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36956 Roo.bootstrap.panel.Content = function( config){
36958 this.tpl = config.tpl || false;
36960 var el = config.el;
36961 var content = config.content;
36963 if(config.autoCreate){ // xtype is available if this is called from factory
36966 this.el = Roo.get(el);
36967 if(!this.el && config && config.autoCreate){
36968 if(typeof config.autoCreate == "object"){
36969 if(!config.autoCreate.id){
36970 config.autoCreate.id = config.id||el;
36972 this.el = Roo.DomHelper.append(document.body,
36973 config.autoCreate, true);
36975 var elcfg = { tag: "div",
36976 cls: "roo-layout-inactive-content",
36980 elcfg.html = config.html;
36984 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36987 this.closable = false;
36988 this.loaded = false;
36989 this.active = false;
36992 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36994 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36996 this.wrapEl = this.el; //this.el.wrap();
36998 if (config.toolbar.items) {
36999 ti = config.toolbar.items ;
37000 delete config.toolbar.items ;
37004 this.toolbar.render(this.wrapEl, 'before');
37005 for(var i =0;i < ti.length;i++) {
37006 // Roo.log(['add child', items[i]]);
37007 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37009 this.toolbar.items = nitems;
37010 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37011 delete config.toolbar;
37015 // xtype created footer. - not sure if will work as we normally have to render first..
37016 if (this.footer && !this.footer.el && this.footer.xtype) {
37017 if (!this.wrapEl) {
37018 this.wrapEl = this.el.wrap();
37021 this.footer.container = this.wrapEl.createChild();
37023 this.footer = Roo.factory(this.footer, Roo);
37028 if(typeof config == "string"){
37029 this.title = config;
37031 Roo.apply(this, config);
37035 this.resizeEl = Roo.get(this.resizeEl, true);
37037 this.resizeEl = this.el;
37039 // handle view.xtype
37047 * Fires when this panel is activated.
37048 * @param {Roo.ContentPanel} this
37052 * @event deactivate
37053 * Fires when this panel is activated.
37054 * @param {Roo.ContentPanel} this
37056 "deactivate" : true,
37060 * Fires when this panel is resized if fitToFrame is true.
37061 * @param {Roo.ContentPanel} this
37062 * @param {Number} width The width after any component adjustments
37063 * @param {Number} height The height after any component adjustments
37069 * Fires when this tab is created
37070 * @param {Roo.ContentPanel} this
37081 if(this.autoScroll){
37082 this.resizeEl.setStyle("overflow", "auto");
37084 // fix randome scrolling
37085 //this.el.on('scroll', function() {
37086 // Roo.log('fix random scolling');
37087 // this.scrollTo('top',0);
37090 content = content || this.content;
37092 this.setContent(content);
37094 if(config && config.url){
37095 this.setUrl(this.url, this.params, this.loadOnce);
37100 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37102 if (this.view && typeof(this.view.xtype) != 'undefined') {
37103 this.view.el = this.el.appendChild(document.createElement("div"));
37104 this.view = Roo.factory(this.view);
37105 this.view.render && this.view.render(false, '');
37109 this.fireEvent('render', this);
37112 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37116 setRegion : function(region){
37117 this.region = region;
37118 this.setActiveClass(region && !this.background);
37122 setActiveClass: function(state)
37125 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37126 this.el.setStyle('position','relative');
37128 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37129 this.el.setStyle('position', 'absolute');
37134 * Returns the toolbar for this Panel if one was configured.
37135 * @return {Roo.Toolbar}
37137 getToolbar : function(){
37138 return this.toolbar;
37141 setActiveState : function(active)
37143 this.active = active;
37144 this.setActiveClass(active);
37146 if(this.fireEvent("deactivate", this) === false){
37151 this.fireEvent("activate", this);
37155 * Updates this panel's element
37156 * @param {String} content The new content
37157 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37159 setContent : function(content, loadScripts){
37160 this.el.update(content, loadScripts);
37163 ignoreResize : function(w, h){
37164 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37167 this.lastSize = {width: w, height: h};
37172 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37173 * @return {Roo.UpdateManager} The UpdateManager
37175 getUpdateManager : function(){
37176 return this.el.getUpdateManager();
37179 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37180 * @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:
37183 url: "your-url.php",
37184 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37185 callback: yourFunction,
37186 scope: yourObject, //(optional scope)
37189 text: "Loading...",
37194 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37195 * 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.
37196 * @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}
37197 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37198 * @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.
37199 * @return {Roo.ContentPanel} this
37202 var um = this.el.getUpdateManager();
37203 um.update.apply(um, arguments);
37209 * 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.
37210 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37211 * @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)
37212 * @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)
37213 * @return {Roo.UpdateManager} The UpdateManager
37215 setUrl : function(url, params, loadOnce){
37216 if(this.refreshDelegate){
37217 this.removeListener("activate", this.refreshDelegate);
37219 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37220 this.on("activate", this.refreshDelegate);
37221 return this.el.getUpdateManager();
37224 _handleRefresh : function(url, params, loadOnce){
37225 if(!loadOnce || !this.loaded){
37226 var updater = this.el.getUpdateManager();
37227 updater.update(url, params, this._setLoaded.createDelegate(this));
37231 _setLoaded : function(){
37232 this.loaded = true;
37236 * Returns this panel's id
37239 getId : function(){
37244 * Returns this panel's element - used by regiosn to add.
37245 * @return {Roo.Element}
37247 getEl : function(){
37248 return this.wrapEl || this.el;
37253 adjustForComponents : function(width, height)
37255 //Roo.log('adjustForComponents ');
37256 if(this.resizeEl != this.el){
37257 width -= this.el.getFrameWidth('lr');
37258 height -= this.el.getFrameWidth('tb');
37261 var te = this.toolbar.getEl();
37262 te.setWidth(width);
37263 height -= te.getHeight();
37266 var te = this.footer.getEl();
37267 te.setWidth(width);
37268 height -= te.getHeight();
37272 if(this.adjustments){
37273 width += this.adjustments[0];
37274 height += this.adjustments[1];
37276 return {"width": width, "height": height};
37279 setSize : function(width, height){
37280 if(this.fitToFrame && !this.ignoreResize(width, height)){
37281 if(this.fitContainer && this.resizeEl != this.el){
37282 this.el.setSize(width, height);
37284 var size = this.adjustForComponents(width, height);
37285 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37286 this.fireEvent('resize', this, size.width, size.height);
37291 * Returns this panel's title
37294 getTitle : function(){
37296 if (typeof(this.title) != 'object') {
37301 for (var k in this.title) {
37302 if (!this.title.hasOwnProperty(k)) {
37306 if (k.indexOf('-') >= 0) {
37307 var s = k.split('-');
37308 for (var i = 0; i<s.length; i++) {
37309 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37312 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37319 * Set this panel's title
37320 * @param {String} title
37322 setTitle : function(title){
37323 this.title = title;
37325 this.region.updatePanelTitle(this, title);
37330 * Returns true is this panel was configured to be closable
37331 * @return {Boolean}
37333 isClosable : function(){
37334 return this.closable;
37337 beforeSlide : function(){
37339 this.resizeEl.clip();
37342 afterSlide : function(){
37344 this.resizeEl.unclip();
37348 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37349 * Will fail silently if the {@link #setUrl} method has not been called.
37350 * This does not activate the panel, just updates its content.
37352 refresh : function(){
37353 if(this.refreshDelegate){
37354 this.loaded = false;
37355 this.refreshDelegate();
37360 * Destroys this panel
37362 destroy : function(){
37363 this.el.removeAllListeners();
37364 var tempEl = document.createElement("span");
37365 tempEl.appendChild(this.el.dom);
37366 tempEl.innerHTML = "";
37372 * form - if the content panel contains a form - this is a reference to it.
37373 * @type {Roo.form.Form}
37377 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37378 * This contains a reference to it.
37384 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37394 * @param {Object} cfg Xtype definition of item to add.
37398 getChildContainer: function () {
37399 return this.getEl();
37404 var ret = new Roo.factory(cfg);
37409 if (cfg.xtype.match(/^Form$/)) {
37412 //if (this.footer) {
37413 // el = this.footer.container.insertSibling(false, 'before');
37415 el = this.el.createChild();
37418 this.form = new Roo.form.Form(cfg);
37421 if ( this.form.allItems.length) {
37422 this.form.render(el.dom);
37426 // should only have one of theses..
37427 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37428 // views.. should not be just added - used named prop 'view''
37430 cfg.el = this.el.appendChild(document.createElement("div"));
37433 var ret = new Roo.factory(cfg);
37435 ret.render && ret.render(false, ''); // render blank..
37445 * @class Roo.bootstrap.panel.Grid
37446 * @extends Roo.bootstrap.panel.Content
37448 * Create a new GridPanel.
37449 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37450 * @param {Object} config A the config object
37456 Roo.bootstrap.panel.Grid = function(config)
37460 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37461 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37463 config.el = this.wrapper;
37464 //this.el = this.wrapper;
37466 if (config.container) {
37467 // ctor'ed from a Border/panel.grid
37470 this.wrapper.setStyle("overflow", "hidden");
37471 this.wrapper.addClass('roo-grid-container');
37476 if(config.toolbar){
37477 var tool_el = this.wrapper.createChild();
37478 this.toolbar = Roo.factory(config.toolbar);
37480 if (config.toolbar.items) {
37481 ti = config.toolbar.items ;
37482 delete config.toolbar.items ;
37486 this.toolbar.render(tool_el);
37487 for(var i =0;i < ti.length;i++) {
37488 // Roo.log(['add child', items[i]]);
37489 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37491 this.toolbar.items = nitems;
37493 delete config.toolbar;
37496 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37497 config.grid.scrollBody = true;;
37498 config.grid.monitorWindowResize = false; // turn off autosizing
37499 config.grid.autoHeight = false;
37500 config.grid.autoWidth = false;
37502 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37504 if (config.background) {
37505 // render grid on panel activation (if panel background)
37506 this.on('activate', function(gp) {
37507 if (!gp.grid.rendered) {
37508 gp.grid.render(this.wrapper);
37509 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37514 this.grid.render(this.wrapper);
37515 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37518 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37519 // ??? needed ??? config.el = this.wrapper;
37524 // xtype created footer. - not sure if will work as we normally have to render first..
37525 if (this.footer && !this.footer.el && this.footer.xtype) {
37527 var ctr = this.grid.getView().getFooterPanel(true);
37528 this.footer.dataSource = this.grid.dataSource;
37529 this.footer = Roo.factory(this.footer, Roo);
37530 this.footer.render(ctr);
37540 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37541 getId : function(){
37542 return this.grid.id;
37546 * Returns the grid for this panel
37547 * @return {Roo.bootstrap.Table}
37549 getGrid : function(){
37553 setSize : function(width, height){
37554 if(!this.ignoreResize(width, height)){
37555 var grid = this.grid;
37556 var size = this.adjustForComponents(width, height);
37557 var gridel = grid.getGridEl();
37558 gridel.setSize(size.width, size.height);
37560 var thd = grid.getGridEl().select('thead',true).first();
37561 var tbd = grid.getGridEl().select('tbody', true).first();
37563 tbd.setSize(width, height - thd.getHeight());
37572 beforeSlide : function(){
37573 this.grid.getView().scroller.clip();
37576 afterSlide : function(){
37577 this.grid.getView().scroller.unclip();
37580 destroy : function(){
37581 this.grid.destroy();
37583 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37588 * @class Roo.bootstrap.panel.Nest
37589 * @extends Roo.bootstrap.panel.Content
37591 * Create a new Panel, that can contain a layout.Border.
37594 * @param {Roo.BorderLayout} layout The layout for this panel
37595 * @param {String/Object} config A string to set only the title or a config object
37597 Roo.bootstrap.panel.Nest = function(config)
37599 // construct with only one argument..
37600 /* FIXME - implement nicer consturctors
37601 if (layout.layout) {
37603 layout = config.layout;
37604 delete config.layout;
37606 if (layout.xtype && !layout.getEl) {
37607 // then layout needs constructing..
37608 layout = Roo.factory(layout, Roo);
37612 config.el = config.layout.getEl();
37614 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37616 config.layout.monitorWindowResize = false; // turn off autosizing
37617 this.layout = config.layout;
37618 this.layout.getEl().addClass("roo-layout-nested-layout");
37625 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37627 setSize : function(width, height){
37628 if(!this.ignoreResize(width, height)){
37629 var size = this.adjustForComponents(width, height);
37630 var el = this.layout.getEl();
37631 if (size.height < 1) {
37632 el.setWidth(size.width);
37634 el.setSize(size.width, size.height);
37636 var touch = el.dom.offsetWidth;
37637 this.layout.layout();
37638 // ie requires a double layout on the first pass
37639 if(Roo.isIE && !this.initialized){
37640 this.initialized = true;
37641 this.layout.layout();
37646 // activate all subpanels if not currently active..
37648 setActiveState : function(active){
37649 this.active = active;
37650 this.setActiveClass(active);
37653 this.fireEvent("deactivate", this);
37657 this.fireEvent("activate", this);
37658 // not sure if this should happen before or after..
37659 if (!this.layout) {
37660 return; // should not happen..
37663 for (var r in this.layout.regions) {
37664 reg = this.layout.getRegion(r);
37665 if (reg.getActivePanel()) {
37666 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37667 reg.setActivePanel(reg.getActivePanel());
37670 if (!reg.panels.length) {
37673 reg.showPanel(reg.getPanel(0));
37682 * Returns the nested BorderLayout for this panel
37683 * @return {Roo.BorderLayout}
37685 getLayout : function(){
37686 return this.layout;
37690 * Adds a xtype elements to the layout of the nested panel
37694 xtype : 'ContentPanel',
37701 xtype : 'NestedLayoutPanel',
37707 items : [ ... list of content panels or nested layout panels.. ]
37711 * @param {Object} cfg Xtype definition of item to add.
37713 addxtype : function(cfg) {
37714 return this.layout.addxtype(cfg);
37719 * Ext JS Library 1.1.1
37720 * Copyright(c) 2006-2007, Ext JS, LLC.
37722 * Originally Released Under LGPL - original licence link has changed is not relivant.
37725 * <script type="text/javascript">
37728 * @class Roo.TabPanel
37729 * @extends Roo.util.Observable
37730 * A lightweight tab container.
37734 // basic tabs 1, built from existing content
37735 var tabs = new Roo.TabPanel("tabs1");
37736 tabs.addTab("script", "View Script");
37737 tabs.addTab("markup", "View Markup");
37738 tabs.activate("script");
37740 // more advanced tabs, built from javascript
37741 var jtabs = new Roo.TabPanel("jtabs");
37742 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37744 // set up the UpdateManager
37745 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37746 var updater = tab2.getUpdateManager();
37747 updater.setDefaultUrl("ajax1.htm");
37748 tab2.on('activate', updater.refresh, updater, true);
37750 // Use setUrl for Ajax loading
37751 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37752 tab3.setUrl("ajax2.htm", null, true);
37755 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37758 jtabs.activate("jtabs-1");
37761 * Create a new TabPanel.
37762 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37763 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37765 Roo.bootstrap.panel.Tabs = function(config){
37767 * The container element for this TabPanel.
37768 * @type Roo.Element
37770 this.el = Roo.get(config.el);
37773 if(typeof config == "boolean"){
37774 this.tabPosition = config ? "bottom" : "top";
37776 Roo.apply(this, config);
37780 if(this.tabPosition == "bottom"){
37781 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37782 this.el.addClass("roo-tabs-bottom");
37784 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37785 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37786 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37788 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37790 if(this.tabPosition != "bottom"){
37791 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37792 * @type Roo.Element
37794 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37795 this.el.addClass("roo-tabs-top");
37799 this.bodyEl.setStyle("position", "relative");
37801 this.active = null;
37802 this.activateDelegate = this.activate.createDelegate(this);
37807 * Fires when the active tab changes
37808 * @param {Roo.TabPanel} this
37809 * @param {Roo.TabPanelItem} activePanel The new active tab
37813 * @event beforetabchange
37814 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37815 * @param {Roo.TabPanel} this
37816 * @param {Object} e Set cancel to true on this object to cancel the tab change
37817 * @param {Roo.TabPanelItem} tab The tab being changed to
37819 "beforetabchange" : true
37822 Roo.EventManager.onWindowResize(this.onResize, this);
37823 this.cpad = this.el.getPadding("lr");
37824 this.hiddenCount = 0;
37827 // toolbar on the tabbar support...
37828 if (this.toolbar) {
37829 alert("no toolbar support yet");
37830 this.toolbar = false;
37832 var tcfg = this.toolbar;
37833 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37834 this.toolbar = new Roo.Toolbar(tcfg);
37835 if (Roo.isSafari) {
37836 var tbl = tcfg.container.child('table', true);
37837 tbl.setAttribute('width', '100%');
37845 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37848 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37850 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37852 tabPosition : "top",
37854 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37856 currentTabWidth : 0,
37858 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37862 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37866 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37868 preferredTabWidth : 175,
37870 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37872 resizeTabs : false,
37874 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37876 monitorResize : true,
37878 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37883 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37884 * @param {String} id The id of the div to use <b>or create</b>
37885 * @param {String} text The text for the tab
37886 * @param {String} content (optional) Content to put in the TabPanelItem body
37887 * @param {Boolean} closable (optional) True to create a close icon on the tab
37888 * @return {Roo.TabPanelItem} The created TabPanelItem
37890 addTab : function(id, text, content, closable, tpl)
37892 var item = new Roo.bootstrap.panel.TabItem({
37896 closable : closable,
37899 this.addTabItem(item);
37901 item.setContent(content);
37907 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37908 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37909 * @return {Roo.TabPanelItem}
37911 getTab : function(id){
37912 return this.items[id];
37916 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37917 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37919 hideTab : function(id){
37920 var t = this.items[id];
37923 this.hiddenCount++;
37924 this.autoSizeTabs();
37929 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37930 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37932 unhideTab : function(id){
37933 var t = this.items[id];
37935 t.setHidden(false);
37936 this.hiddenCount--;
37937 this.autoSizeTabs();
37942 * Adds an existing {@link Roo.TabPanelItem}.
37943 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37945 addTabItem : function(item){
37946 this.items[item.id] = item;
37947 this.items.push(item);
37948 // if(this.resizeTabs){
37949 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37950 // this.autoSizeTabs();
37952 // item.autoSize();
37957 * Removes a {@link Roo.TabPanelItem}.
37958 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37960 removeTab : function(id){
37961 var items = this.items;
37962 var tab = items[id];
37963 if(!tab) { return; }
37964 var index = items.indexOf(tab);
37965 if(this.active == tab && items.length > 1){
37966 var newTab = this.getNextAvailable(index);
37971 this.stripEl.dom.removeChild(tab.pnode.dom);
37972 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37973 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37975 items.splice(index, 1);
37976 delete this.items[tab.id];
37977 tab.fireEvent("close", tab);
37978 tab.purgeListeners();
37979 this.autoSizeTabs();
37982 getNextAvailable : function(start){
37983 var items = this.items;
37985 // look for a next tab that will slide over to
37986 // replace the one being removed
37987 while(index < items.length){
37988 var item = items[++index];
37989 if(item && !item.isHidden()){
37993 // if one isn't found select the previous tab (on the left)
37996 var item = items[--index];
37997 if(item && !item.isHidden()){
38005 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38006 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38008 disableTab : function(id){
38009 var tab = this.items[id];
38010 if(tab && this.active != tab){
38016 * Enables a {@link Roo.TabPanelItem} that is disabled.
38017 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38019 enableTab : function(id){
38020 var tab = this.items[id];
38025 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38026 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38027 * @return {Roo.TabPanelItem} The TabPanelItem.
38029 activate : function(id){
38030 var tab = this.items[id];
38034 if(tab == this.active || tab.disabled){
38038 this.fireEvent("beforetabchange", this, e, tab);
38039 if(e.cancel !== true && !tab.disabled){
38041 this.active.hide();
38043 this.active = this.items[id];
38044 this.active.show();
38045 this.fireEvent("tabchange", this, this.active);
38051 * Gets the active {@link Roo.TabPanelItem}.
38052 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38054 getActiveTab : function(){
38055 return this.active;
38059 * Updates the tab body element to fit the height of the container element
38060 * for overflow scrolling
38061 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38063 syncHeight : function(targetHeight){
38064 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38065 var bm = this.bodyEl.getMargins();
38066 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38067 this.bodyEl.setHeight(newHeight);
38071 onResize : function(){
38072 if(this.monitorResize){
38073 this.autoSizeTabs();
38078 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38080 beginUpdate : function(){
38081 this.updating = true;
38085 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38087 endUpdate : function(){
38088 this.updating = false;
38089 this.autoSizeTabs();
38093 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38095 autoSizeTabs : function(){
38096 var count = this.items.length;
38097 var vcount = count - this.hiddenCount;
38098 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38101 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38102 var availWidth = Math.floor(w / vcount);
38103 var b = this.stripBody;
38104 if(b.getWidth() > w){
38105 var tabs = this.items;
38106 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38107 if(availWidth < this.minTabWidth){
38108 /*if(!this.sleft){ // incomplete scrolling code
38109 this.createScrollButtons();
38112 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38115 if(this.currentTabWidth < this.preferredTabWidth){
38116 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38122 * Returns the number of tabs in this TabPanel.
38125 getCount : function(){
38126 return this.items.length;
38130 * Resizes all the tabs to the passed width
38131 * @param {Number} The new width
38133 setTabWidth : function(width){
38134 this.currentTabWidth = width;
38135 for(var i = 0, len = this.items.length; i < len; i++) {
38136 if(!this.items[i].isHidden()) {
38137 this.items[i].setWidth(width);
38143 * Destroys this TabPanel
38144 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38146 destroy : function(removeEl){
38147 Roo.EventManager.removeResizeListener(this.onResize, this);
38148 for(var i = 0, len = this.items.length; i < len; i++){
38149 this.items[i].purgeListeners();
38151 if(removeEl === true){
38152 this.el.update("");
38157 createStrip : function(container)
38159 var strip = document.createElement("nav");
38160 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38161 container.appendChild(strip);
38165 createStripList : function(strip)
38167 // div wrapper for retard IE
38168 // returns the "tr" element.
38169 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38170 //'<div class="x-tabs-strip-wrap">'+
38171 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38172 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38173 return strip.firstChild; //.firstChild.firstChild.firstChild;
38175 createBody : function(container)
38177 var body = document.createElement("div");
38178 Roo.id(body, "tab-body");
38179 //Roo.fly(body).addClass("x-tabs-body");
38180 Roo.fly(body).addClass("tab-content");
38181 container.appendChild(body);
38184 createItemBody :function(bodyEl, id){
38185 var body = Roo.getDom(id);
38187 body = document.createElement("div");
38190 //Roo.fly(body).addClass("x-tabs-item-body");
38191 Roo.fly(body).addClass("tab-pane");
38192 bodyEl.insertBefore(body, bodyEl.firstChild);
38196 createStripElements : function(stripEl, text, closable, tpl)
38198 var td = document.createElement("li"); // was td..
38201 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38204 stripEl.appendChild(td);
38206 td.className = "x-tabs-closable";
38207 if(!this.closeTpl){
38208 this.closeTpl = new Roo.Template(
38209 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38210 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38211 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38214 var el = this.closeTpl.overwrite(td, {"text": text});
38215 var close = el.getElementsByTagName("div")[0];
38216 var inner = el.getElementsByTagName("em")[0];
38217 return {"el": el, "close": close, "inner": inner};
38220 // not sure what this is..
38221 // if(!this.tabTpl){
38222 //this.tabTpl = new Roo.Template(
38223 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38224 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38226 // this.tabTpl = new Roo.Template(
38227 // '<a href="#">' +
38228 // '<span unselectable="on"' +
38229 // (this.disableTooltips ? '' : ' title="{text}"') +
38230 // ' >{text}</span></a>'
38236 var template = tpl || this.tabTpl || false;
38240 template = new Roo.Template(
38242 '<span unselectable="on"' +
38243 (this.disableTooltips ? '' : ' title="{text}"') +
38244 ' >{text}</span></a>'
38248 switch (typeof(template)) {
38252 template = new Roo.Template(template);
38258 var el = template.overwrite(td, {"text": text});
38260 var inner = el.getElementsByTagName("span")[0];
38262 return {"el": el, "inner": inner};
38270 * @class Roo.TabPanelItem
38271 * @extends Roo.util.Observable
38272 * Represents an individual item (tab plus body) in a TabPanel.
38273 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38274 * @param {String} id The id of this TabPanelItem
38275 * @param {String} text The text for the tab of this TabPanelItem
38276 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38278 Roo.bootstrap.panel.TabItem = function(config){
38280 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38281 * @type Roo.TabPanel
38283 this.tabPanel = config.panel;
38285 * The id for this TabPanelItem
38288 this.id = config.id;
38290 this.disabled = false;
38292 this.text = config.text;
38294 this.loaded = false;
38295 this.closable = config.closable;
38298 * The body element for this TabPanelItem.
38299 * @type Roo.Element
38301 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38302 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38303 this.bodyEl.setStyle("display", "block");
38304 this.bodyEl.setStyle("zoom", "1");
38305 //this.hideAction();
38307 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38309 this.el = Roo.get(els.el);
38310 this.inner = Roo.get(els.inner, true);
38311 this.textEl = Roo.get(this.el.dom.firstChild, true);
38312 this.pnode = Roo.get(els.el.parentNode, true);
38313 // this.el.on("mousedown", this.onTabMouseDown, this);
38314 this.el.on("click", this.onTabClick, this);
38316 if(config.closable){
38317 var c = Roo.get(els.close, true);
38318 c.dom.title = this.closeText;
38319 c.addClassOnOver("close-over");
38320 c.on("click", this.closeClick, this);
38326 * Fires when this tab becomes the active tab.
38327 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38328 * @param {Roo.TabPanelItem} this
38332 * @event beforeclose
38333 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38334 * @param {Roo.TabPanelItem} this
38335 * @param {Object} e Set cancel to true on this object to cancel the close.
38337 "beforeclose": true,
38340 * Fires when this tab is closed.
38341 * @param {Roo.TabPanelItem} this
38345 * @event deactivate
38346 * Fires when this tab is no longer the active tab.
38347 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38348 * @param {Roo.TabPanelItem} this
38350 "deactivate" : true
38352 this.hidden = false;
38354 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38357 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38359 purgeListeners : function(){
38360 Roo.util.Observable.prototype.purgeListeners.call(this);
38361 this.el.removeAllListeners();
38364 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38367 this.pnode.addClass("active");
38370 this.tabPanel.stripWrap.repaint();
38372 this.fireEvent("activate", this.tabPanel, this);
38376 * Returns true if this tab is the active tab.
38377 * @return {Boolean}
38379 isActive : function(){
38380 return this.tabPanel.getActiveTab() == this;
38384 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38387 this.pnode.removeClass("active");
38389 this.fireEvent("deactivate", this.tabPanel, this);
38392 hideAction : function(){
38393 this.bodyEl.hide();
38394 this.bodyEl.setStyle("position", "absolute");
38395 this.bodyEl.setLeft("-20000px");
38396 this.bodyEl.setTop("-20000px");
38399 showAction : function(){
38400 this.bodyEl.setStyle("position", "relative");
38401 this.bodyEl.setTop("");
38402 this.bodyEl.setLeft("");
38403 this.bodyEl.show();
38407 * Set the tooltip for the tab.
38408 * @param {String} tooltip The tab's tooltip
38410 setTooltip : function(text){
38411 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38412 this.textEl.dom.qtip = text;
38413 this.textEl.dom.removeAttribute('title');
38415 this.textEl.dom.title = text;
38419 onTabClick : function(e){
38420 e.preventDefault();
38421 this.tabPanel.activate(this.id);
38424 onTabMouseDown : function(e){
38425 e.preventDefault();
38426 this.tabPanel.activate(this.id);
38429 getWidth : function(){
38430 return this.inner.getWidth();
38433 setWidth : function(width){
38434 var iwidth = width - this.pnode.getPadding("lr");
38435 this.inner.setWidth(iwidth);
38436 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38437 this.pnode.setWidth(width);
38441 * Show or hide the tab
38442 * @param {Boolean} hidden True to hide or false to show.
38444 setHidden : function(hidden){
38445 this.hidden = hidden;
38446 this.pnode.setStyle("display", hidden ? "none" : "");
38450 * Returns true if this tab is "hidden"
38451 * @return {Boolean}
38453 isHidden : function(){
38454 return this.hidden;
38458 * Returns the text for this tab
38461 getText : function(){
38465 autoSize : function(){
38466 //this.el.beginMeasure();
38467 this.textEl.setWidth(1);
38469 * #2804 [new] Tabs in Roojs
38470 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38472 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38473 //this.el.endMeasure();
38477 * Sets the text for the tab (Note: this also sets the tooltip text)
38478 * @param {String} text The tab's text and tooltip
38480 setText : function(text){
38482 this.textEl.update(text);
38483 this.setTooltip(text);
38484 //if(!this.tabPanel.resizeTabs){
38485 // this.autoSize();
38489 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38491 activate : function(){
38492 this.tabPanel.activate(this.id);
38496 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38498 disable : function(){
38499 if(this.tabPanel.active != this){
38500 this.disabled = true;
38501 this.pnode.addClass("disabled");
38506 * Enables this TabPanelItem if it was previously disabled.
38508 enable : function(){
38509 this.disabled = false;
38510 this.pnode.removeClass("disabled");
38514 * Sets the content for this TabPanelItem.
38515 * @param {String} content The content
38516 * @param {Boolean} loadScripts true to look for and load scripts
38518 setContent : function(content, loadScripts){
38519 this.bodyEl.update(content, loadScripts);
38523 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38524 * @return {Roo.UpdateManager} The UpdateManager
38526 getUpdateManager : function(){
38527 return this.bodyEl.getUpdateManager();
38531 * Set a URL to be used to load the content for this TabPanelItem.
38532 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38533 * @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)
38534 * @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)
38535 * @return {Roo.UpdateManager} The UpdateManager
38537 setUrl : function(url, params, loadOnce){
38538 if(this.refreshDelegate){
38539 this.un('activate', this.refreshDelegate);
38541 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38542 this.on("activate", this.refreshDelegate);
38543 return this.bodyEl.getUpdateManager();
38547 _handleRefresh : function(url, params, loadOnce){
38548 if(!loadOnce || !this.loaded){
38549 var updater = this.bodyEl.getUpdateManager();
38550 updater.update(url, params, this._setLoaded.createDelegate(this));
38555 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38556 * Will fail silently if the setUrl method has not been called.
38557 * This does not activate the panel, just updates its content.
38559 refresh : function(){
38560 if(this.refreshDelegate){
38561 this.loaded = false;
38562 this.refreshDelegate();
38567 _setLoaded : function(){
38568 this.loaded = true;
38572 closeClick : function(e){
38575 this.fireEvent("beforeclose", this, o);
38576 if(o.cancel !== true){
38577 this.tabPanel.removeTab(this.id);
38581 * The text displayed in the tooltip for the close icon.
38584 closeText : "Close this tab"
38587 * This script refer to:
38588 * Title: International Telephone Input
38589 * Author: Jack O'Connor
38590 * Code version: v12.1.12
38591 * Availability: https://github.com/jackocnr/intl-tel-input.git
38594 Roo.bootstrap.PhoneInputData = function() {
38597 "Afghanistan (افغانستان)",
38602 "Albania (Shqipëri)",
38607 "Algeria (الجزائر)",
38632 "Antigua and Barbuda",
38642 "Armenia (Հայաստան)",
38658 "Austria (Österreich)",
38663 "Azerbaijan (Azərbaycan)",
38673 "Bahrain (البحرين)",
38678 "Bangladesh (বাংলাদেশ)",
38688 "Belarus (Беларусь)",
38693 "Belgium (België)",
38723 "Bosnia and Herzegovina (Босна и Херцеговина)",
38738 "British Indian Ocean Territory",
38743 "British Virgin Islands",
38753 "Bulgaria (България)",
38763 "Burundi (Uburundi)",
38768 "Cambodia (កម្ពុជា)",
38773 "Cameroon (Cameroun)",
38782 ["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"]
38785 "Cape Verde (Kabu Verdi)",
38790 "Caribbean Netherlands",
38801 "Central African Republic (République centrafricaine)",
38821 "Christmas Island",
38827 "Cocos (Keeling) Islands",
38838 "Comoros (جزر القمر)",
38843 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38848 "Congo (Republic) (Congo-Brazzaville)",
38868 "Croatia (Hrvatska)",
38889 "Czech Republic (Česká republika)",
38894 "Denmark (Danmark)",
38909 "Dominican Republic (República Dominicana)",
38913 ["809", "829", "849"]
38931 "Equatorial Guinea (Guinea Ecuatorial)",
38951 "Falkland Islands (Islas Malvinas)",
38956 "Faroe Islands (Føroyar)",
38977 "French Guiana (Guyane française)",
38982 "French Polynesia (Polynésie française)",
38997 "Georgia (საქართველო)",
39002 "Germany (Deutschland)",
39022 "Greenland (Kalaallit Nunaat)",
39059 "Guinea-Bissau (Guiné Bissau)",
39084 "Hungary (Magyarország)",
39089 "Iceland (Ísland)",
39109 "Iraq (العراق)",
39125 "Israel (ישראל)",
39152 "Jordan (الأردن)",
39157 "Kazakhstan (Казахстан)",
39178 "Kuwait (الكويت)",
39183 "Kyrgyzstan (Кыргызстан)",
39193 "Latvia (Latvija)",
39198 "Lebanon (لبنان)",
39213 "Libya (ليبيا)",
39223 "Lithuania (Lietuva)",
39238 "Macedonia (FYROM) (Македонија)",
39243 "Madagascar (Madagasikara)",
39273 "Marshall Islands",
39283 "Mauritania (موريتانيا)",
39288 "Mauritius (Moris)",
39309 "Moldova (Republica Moldova)",
39319 "Mongolia (Монгол)",
39324 "Montenegro (Crna Gora)",
39334 "Morocco (المغرب)",
39340 "Mozambique (Moçambique)",
39345 "Myanmar (Burma) (မြန်မာ)",
39350 "Namibia (Namibië)",
39365 "Netherlands (Nederland)",
39370 "New Caledonia (Nouvelle-Calédonie)",
39405 "North Korea (조선 민주주의 인민 공화국)",
39410 "Northern Mariana Islands",
39426 "Pakistan (پاکستان)",
39436 "Palestine (فلسطين)",
39446 "Papua New Guinea",
39488 "Réunion (La Réunion)",
39494 "Romania (România)",
39510 "Saint Barthélemy",
39521 "Saint Kitts and Nevis",
39531 "Saint Martin (Saint-Martin (partie française))",
39537 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39542 "Saint Vincent and the Grenadines",
39557 "São Tomé and Príncipe (São Tomé e Príncipe)",
39562 "Saudi Arabia (المملكة العربية السعودية)",
39567 "Senegal (Sénégal)",
39597 "Slovakia (Slovensko)",
39602 "Slovenia (Slovenija)",
39612 "Somalia (Soomaaliya)",
39622 "South Korea (대한민국)",
39627 "South Sudan (جنوب السودان)",
39637 "Sri Lanka (ශ්රී ලංකාව)",
39642 "Sudan (السودان)",
39652 "Svalbard and Jan Mayen",
39663 "Sweden (Sverige)",
39668 "Switzerland (Schweiz)",
39673 "Syria (سوريا)",
39718 "Trinidad and Tobago",
39723 "Tunisia (تونس)",
39728 "Turkey (Türkiye)",
39738 "Turks and Caicos Islands",
39748 "U.S. Virgin Islands",
39758 "Ukraine (Україна)",
39763 "United Arab Emirates (الإمارات العربية المتحدة)",
39785 "Uzbekistan (Oʻzbekiston)",
39795 "Vatican City (Città del Vaticano)",
39806 "Vietnam (Việt Nam)",
39811 "Wallis and Futuna (Wallis-et-Futuna)",
39816 "Western Sahara (الصحراء الغربية)",
39822 "Yemen (اليمن)",
39846 * This script refer to:
39847 * Title: International Telephone Input
39848 * Author: Jack O'Connor
39849 * Code version: v12.1.12
39850 * Availability: https://github.com/jackocnr/intl-tel-input.git
39854 * @class Roo.bootstrap.PhoneInput
39855 * @extends Roo.bootstrap.TriggerField
39856 * An input with International dial-code selection
39858 * @cfg {String} defaultDialCode default '+852'
39859 * @cfg {Array} preferedCountries default []
39862 * Create a new PhoneInput.
39863 * @param {Object} config Configuration options
39866 Roo.bootstrap.PhoneInput = function(config) {
39867 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39870 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39872 listWidth: undefined,
39874 selectedClass: 'active',
39876 invalidClass : "has-warning",
39878 validClass: 'has-success',
39880 allowed: '0123456789',
39885 * @cfg {String} defaultDialCode The default dial code when initializing the input
39887 defaultDialCode: '+852',
39890 * @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
39892 preferedCountries: false,
39894 getAutoCreate : function()
39896 var data = Roo.bootstrap.PhoneInputData();
39897 var align = this.labelAlign || this.parentLabelAlign();
39900 this.allCountries = [];
39901 this.dialCodeMapping = [];
39903 for (var i = 0; i < data.length; i++) {
39905 this.allCountries[i] = {
39909 priority: c[3] || 0,
39910 areaCodes: c[4] || null
39912 this.dialCodeMapping[c[2]] = {
39915 priority: c[3] || 0,
39916 areaCodes: c[4] || null
39928 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39929 maxlength: this.max_length,
39930 cls : 'form-control tel-input',
39931 autocomplete: 'new-password'
39934 var hiddenInput = {
39937 cls: 'hidden-tel-input'
39941 hiddenInput.name = this.name;
39944 if (this.disabled) {
39945 input.disabled = true;
39948 var flag_container = {
39965 cls: this.hasFeedback ? 'has-feedback' : '',
39971 cls: 'dial-code-holder',
39978 cls: 'roo-select2-container input-group',
39985 if (this.fieldLabel.length) {
39988 tooltip: 'This field is required'
39994 cls: 'control-label',
40000 html: this.fieldLabel
40003 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40009 if(this.indicatorpos == 'right') {
40010 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40017 if(align == 'left') {
40025 if(this.labelWidth > 12){
40026 label.style = "width: " + this.labelWidth + 'px';
40028 if(this.labelWidth < 13 && this.labelmd == 0){
40029 this.labelmd = this.labelWidth;
40031 if(this.labellg > 0){
40032 label.cls += ' col-lg-' + this.labellg;
40033 input.cls += ' col-lg-' + (12 - this.labellg);
40035 if(this.labelmd > 0){
40036 label.cls += ' col-md-' + this.labelmd;
40037 container.cls += ' col-md-' + (12 - this.labelmd);
40039 if(this.labelsm > 0){
40040 label.cls += ' col-sm-' + this.labelsm;
40041 container.cls += ' col-sm-' + (12 - this.labelsm);
40043 if(this.labelxs > 0){
40044 label.cls += ' col-xs-' + this.labelxs;
40045 container.cls += ' col-xs-' + (12 - this.labelxs);
40055 var settings = this;
40057 ['xs','sm','md','lg'].map(function(size){
40058 if (settings[size]) {
40059 cfg.cls += ' col-' + size + '-' + settings[size];
40063 this.store = new Roo.data.Store({
40064 proxy : new Roo.data.MemoryProxy({}),
40065 reader : new Roo.data.JsonReader({
40076 'name' : 'dialCode',
40080 'name' : 'priority',
40084 'name' : 'areaCodes',
40091 if(!this.preferedCountries) {
40092 this.preferedCountries = [
40099 var p = this.preferedCountries.reverse();
40102 for (var i = 0; i < p.length; i++) {
40103 for (var j = 0; j < this.allCountries.length; j++) {
40104 if(this.allCountries[j].iso2 == p[i]) {
40105 var t = this.allCountries[j];
40106 this.allCountries.splice(j,1);
40107 this.allCountries.unshift(t);
40113 this.store.proxy.data = {
40115 data: this.allCountries
40121 initEvents : function()
40124 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40126 this.indicator = this.indicatorEl();
40127 this.flag = this.flagEl();
40128 this.dialCodeHolder = this.dialCodeHolderEl();
40130 this.trigger = this.el.select('div.flag-box',true).first();
40131 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40136 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40137 _this.list.setWidth(lw);
40140 this.list.on('mouseover', this.onViewOver, this);
40141 this.list.on('mousemove', this.onViewMove, this);
40142 this.inputEl().on("keyup", this.onKeyUp, this);
40143 this.inputEl().on("keypress", this.onKeyPress, this);
40145 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40147 this.view = new Roo.View(this.list, this.tpl, {
40148 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40151 this.view.on('click', this.onViewClick, this);
40152 this.setValue(this.defaultDialCode);
40155 onTriggerClick : function(e)
40157 Roo.log('trigger click');
40162 if(this.isExpanded()){
40164 this.hasFocus = false;
40166 this.store.load({});
40167 this.hasFocus = true;
40172 isExpanded : function()
40174 return this.list.isVisible();
40177 collapse : function()
40179 if(!this.isExpanded()){
40183 Roo.get(document).un('mousedown', this.collapseIf, this);
40184 Roo.get(document).un('mousewheel', this.collapseIf, this);
40185 this.fireEvent('collapse', this);
40189 expand : function()
40193 if(this.isExpanded() || !this.hasFocus){
40197 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40198 this.list.setWidth(lw);
40201 this.restrictHeight();
40203 Roo.get(document).on('mousedown', this.collapseIf, this);
40204 Roo.get(document).on('mousewheel', this.collapseIf, this);
40206 this.fireEvent('expand', this);
40209 restrictHeight : function()
40211 this.list.alignTo(this.inputEl(), this.listAlign);
40212 this.list.alignTo(this.inputEl(), this.listAlign);
40215 onViewOver : function(e, t)
40217 if(this.inKeyMode){
40220 var item = this.view.findItemFromChild(t);
40223 var index = this.view.indexOf(item);
40224 this.select(index, false);
40229 onViewClick : function(view, doFocus, el, e)
40231 var index = this.view.getSelectedIndexes()[0];
40233 var r = this.store.getAt(index);
40236 this.onSelect(r, index);
40238 if(doFocus !== false && !this.blockFocus){
40239 this.inputEl().focus();
40243 onViewMove : function(e, t)
40245 this.inKeyMode = false;
40248 select : function(index, scrollIntoView)
40250 this.selectedIndex = index;
40251 this.view.select(index);
40252 if(scrollIntoView !== false){
40253 var el = this.view.getNode(index);
40255 this.list.scrollChildIntoView(el, false);
40260 createList : function()
40262 this.list = Roo.get(document.body).createChild({
40264 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40265 style: 'display:none'
40268 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40271 collapseIf : function(e)
40273 var in_combo = e.within(this.el);
40274 var in_list = e.within(this.list);
40275 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40277 if (in_combo || in_list || is_list) {
40283 onSelect : function(record, index)
40285 if(this.fireEvent('beforeselect', this, record, index) !== false){
40287 this.setFlagClass(record.data.iso2);
40288 this.setDialCode(record.data.dialCode);
40289 this.hasFocus = false;
40291 this.fireEvent('select', this, record, index);
40295 flagEl : function()
40297 var flag = this.el.select('div.flag',true).first();
40304 dialCodeHolderEl : function()
40306 var d = this.el.select('input.dial-code-holder',true).first();
40313 setDialCode : function(v)
40315 this.dialCodeHolder.dom.value = '+'+v;
40318 setFlagClass : function(n)
40320 this.flag.dom.className = 'flag '+n;
40323 getValue : function()
40325 var v = this.inputEl().getValue();
40326 if(this.dialCodeHolder) {
40327 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40332 setValue : function(v)
40334 var d = this.getDialCode(v);
40336 //invalid dial code
40337 if(v.length == 0 || !d || d.length == 0) {
40339 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40340 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40346 this.setFlagClass(this.dialCodeMapping[d].iso2);
40347 this.setDialCode(d);
40348 this.inputEl().dom.value = v.replace('+'+d,'');
40349 this.hiddenEl().dom.value = this.getValue();
40354 getDialCode : function(v)
40358 if (v.length == 0) {
40359 return this.dialCodeHolder.dom.value;
40363 if (v.charAt(0) != "+") {
40366 var numericChars = "";
40367 for (var i = 1; i < v.length; i++) {
40368 var c = v.charAt(i);
40371 if (this.dialCodeMapping[numericChars]) {
40372 dialCode = v.substr(1, i);
40374 if (numericChars.length == 4) {
40384 this.setValue(this.defaultDialCode);
40388 hiddenEl : function()
40390 return this.el.select('input.hidden-tel-input',true).first();
40393 // after setting val
40394 onKeyUp : function(e){
40395 this.setValue(this.getValue());
40398 onKeyPress : function(e){
40399 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40406 * @class Roo.bootstrap.MoneyField
40407 * @extends Roo.bootstrap.ComboBox
40408 * Bootstrap MoneyField class
40411 * Create a new MoneyField.
40412 * @param {Object} config Configuration options
40415 Roo.bootstrap.MoneyField = function(config) {
40417 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40421 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40424 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40426 allowDecimals : true,
40428 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40430 decimalSeparator : ".",
40432 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40434 decimalPrecision : 0,
40436 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40438 allowNegative : true,
40440 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40444 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40446 minValue : Number.NEGATIVE_INFINITY,
40448 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40450 maxValue : Number.MAX_VALUE,
40452 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40454 minText : "The minimum value for this field is {0}",
40456 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40458 maxText : "The maximum value for this field is {0}",
40460 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40461 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40463 nanText : "{0} is not a valid number",
40465 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40469 * @cfg {String} defaults currency of the MoneyField
40470 * value should be in lkey
40472 defaultCurrency : false,
40474 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40476 thousandsDelimiter : false,
40478 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40489 getAutoCreate : function()
40491 var align = this.labelAlign || this.parentLabelAlign();
40503 cls : 'form-control roo-money-amount-input',
40504 autocomplete: 'new-password'
40507 var hiddenInput = {
40511 cls: 'hidden-number-input'
40514 if(this.max_length) {
40515 input.maxlength = this.max_length;
40519 hiddenInput.name = this.name;
40522 if (this.disabled) {
40523 input.disabled = true;
40526 var clg = 12 - this.inputlg;
40527 var cmd = 12 - this.inputmd;
40528 var csm = 12 - this.inputsm;
40529 var cxs = 12 - this.inputxs;
40533 cls : 'row roo-money-field',
40537 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40541 cls: 'roo-select2-container input-group',
40545 cls : 'form-control roo-money-currency-input',
40546 autocomplete: 'new-password',
40548 name : this.currencyName
40552 cls : 'input-group-addon',
40566 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40570 cls: this.hasFeedback ? 'has-feedback' : '',
40581 if (this.fieldLabel.length) {
40584 tooltip: 'This field is required'
40590 cls: 'control-label',
40596 html: this.fieldLabel
40599 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40605 if(this.indicatorpos == 'right') {
40606 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40613 if(align == 'left') {
40621 if(this.labelWidth > 12){
40622 label.style = "width: " + this.labelWidth + 'px';
40624 if(this.labelWidth < 13 && this.labelmd == 0){
40625 this.labelmd = this.labelWidth;
40627 if(this.labellg > 0){
40628 label.cls += ' col-lg-' + this.labellg;
40629 input.cls += ' col-lg-' + (12 - this.labellg);
40631 if(this.labelmd > 0){
40632 label.cls += ' col-md-' + this.labelmd;
40633 container.cls += ' col-md-' + (12 - this.labelmd);
40635 if(this.labelsm > 0){
40636 label.cls += ' col-sm-' + this.labelsm;
40637 container.cls += ' col-sm-' + (12 - this.labelsm);
40639 if(this.labelxs > 0){
40640 label.cls += ' col-xs-' + this.labelxs;
40641 container.cls += ' col-xs-' + (12 - this.labelxs);
40652 var settings = this;
40654 ['xs','sm','md','lg'].map(function(size){
40655 if (settings[size]) {
40656 cfg.cls += ' col-' + size + '-' + settings[size];
40663 initEvents : function()
40665 this.indicator = this.indicatorEl();
40667 this.initCurrencyEvent();
40669 this.initNumberEvent();
40672 initCurrencyEvent : function()
40675 throw "can not find store for combo";
40678 this.store = Roo.factory(this.store, Roo.data);
40679 this.store.parent = this;
40683 this.triggerEl = this.el.select('.input-group-addon', true).first();
40685 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40690 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40691 _this.list.setWidth(lw);
40694 this.list.on('mouseover', this.onViewOver, this);
40695 this.list.on('mousemove', this.onViewMove, this);
40696 this.list.on('scroll', this.onViewScroll, this);
40699 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40702 this.view = new Roo.View(this.list, this.tpl, {
40703 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40706 this.view.on('click', this.onViewClick, this);
40708 this.store.on('beforeload', this.onBeforeLoad, this);
40709 this.store.on('load', this.onLoad, this);
40710 this.store.on('loadexception', this.onLoadException, this);
40712 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40713 "up" : function(e){
40714 this.inKeyMode = true;
40718 "down" : function(e){
40719 if(!this.isExpanded()){
40720 this.onTriggerClick();
40722 this.inKeyMode = true;
40727 "enter" : function(e){
40730 if(this.fireEvent("specialkey", this, e)){
40731 this.onViewClick(false);
40737 "esc" : function(e){
40741 "tab" : function(e){
40744 if(this.fireEvent("specialkey", this, e)){
40745 this.onViewClick(false);
40753 doRelay : function(foo, bar, hname){
40754 if(hname == 'down' || this.scope.isExpanded()){
40755 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40763 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40767 initNumberEvent : function(e)
40769 this.inputEl().on("keydown" , this.fireKey, this);
40770 this.inputEl().on("focus", this.onFocus, this);
40771 this.inputEl().on("blur", this.onBlur, this);
40773 this.inputEl().relayEvent('keyup', this);
40775 if(this.indicator){
40776 this.indicator.addClass('invisible');
40779 this.originalValue = this.getValue();
40781 if(this.validationEvent == 'keyup'){
40782 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40783 this.inputEl().on('keyup', this.filterValidation, this);
40785 else if(this.validationEvent !== false){
40786 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40789 if(this.selectOnFocus){
40790 this.on("focus", this.preFocus, this);
40793 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40794 this.inputEl().on("keypress", this.filterKeys, this);
40796 this.inputEl().relayEvent('keypress', this);
40799 var allowed = "0123456789";
40801 if(this.allowDecimals){
40802 allowed += this.decimalSeparator;
40805 if(this.allowNegative){
40809 if(this.thousandsDelimiter) {
40813 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40815 var keyPress = function(e){
40817 var k = e.getKey();
40819 var c = e.getCharCode();
40822 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40823 allowed.indexOf(String.fromCharCode(c)) === -1
40829 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40833 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40838 this.inputEl().on("keypress", keyPress, this);
40842 onTriggerClick : function(e)
40849 this.loadNext = false;
40851 if(this.isExpanded()){
40856 this.hasFocus = true;
40858 if(this.triggerAction == 'all') {
40859 this.doQuery(this.allQuery, true);
40863 this.doQuery(this.getRawValue());
40866 getCurrency : function()
40868 var v = this.currencyEl().getValue();
40873 restrictHeight : function()
40875 this.list.alignTo(this.currencyEl(), this.listAlign);
40876 this.list.alignTo(this.currencyEl(), this.listAlign);
40879 onViewClick : function(view, doFocus, el, e)
40881 var index = this.view.getSelectedIndexes()[0];
40883 var r = this.store.getAt(index);
40886 this.onSelect(r, index);
40890 onSelect : function(record, index){
40892 if(this.fireEvent('beforeselect', this, record, index) !== false){
40894 this.setFromCurrencyData(index > -1 ? record.data : false);
40898 this.fireEvent('select', this, record, index);
40902 setFromCurrencyData : function(o)
40906 this.lastCurrency = o;
40908 if (this.currencyField) {
40909 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40911 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40914 this.lastSelectionText = currency;
40916 //setting default currency
40917 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40918 this.setCurrency(this.defaultCurrency);
40922 this.setCurrency(currency);
40925 setFromData : function(o)
40929 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40931 this.setFromCurrencyData(c);
40936 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40938 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40941 this.setValue(value);
40945 setCurrency : function(v)
40947 this.currencyValue = v;
40950 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40955 setValue : function(v)
40957 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40963 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40965 this.inputEl().dom.value = (v == '') ? '' :
40966 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40968 if(!this.allowZero && v === '0') {
40969 this.hiddenEl().dom.value = '';
40970 this.inputEl().dom.value = '';
40977 getRawValue : function()
40979 var v = this.inputEl().getValue();
40984 getValue : function()
40986 return this.fixPrecision(this.parseValue(this.getRawValue()));
40989 parseValue : function(value)
40991 if(this.thousandsDelimiter) {
40993 r = new RegExp(",", "g");
40994 value = value.replace(r, "");
40997 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40998 return isNaN(value) ? '' : value;
41002 fixPrecision : function(value)
41004 if(this.thousandsDelimiter) {
41006 r = new RegExp(",", "g");
41007 value = value.replace(r, "");
41010 var nan = isNaN(value);
41012 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41013 return nan ? '' : value;
41015 return parseFloat(value).toFixed(this.decimalPrecision);
41018 decimalPrecisionFcn : function(v)
41020 return Math.floor(v);
41023 validateValue : function(value)
41025 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41029 var num = this.parseValue(value);
41032 this.markInvalid(String.format(this.nanText, value));
41036 if(num < this.minValue){
41037 this.markInvalid(String.format(this.minText, this.minValue));
41041 if(num > this.maxValue){
41042 this.markInvalid(String.format(this.maxText, this.maxValue));
41049 validate : function()
41051 if(this.disabled || this.allowBlank){
41056 var currency = this.getCurrency();
41058 if(this.validateValue(this.getRawValue()) && currency.length){
41063 this.markInvalid();
41067 getName: function()
41072 beforeBlur : function()
41078 var v = this.parseValue(this.getRawValue());
41085 onBlur : function()
41089 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41090 //this.el.removeClass(this.focusClass);
41093 this.hasFocus = false;
41095 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41099 var v = this.getValue();
41101 if(String(v) !== String(this.startValue)){
41102 this.fireEvent('change', this, v, this.startValue);
41105 this.fireEvent("blur", this);
41108 inputEl : function()
41110 return this.el.select('.roo-money-amount-input', true).first();
41113 currencyEl : function()
41115 return this.el.select('.roo-money-currency-input', true).first();
41118 hiddenEl : function()
41120 return this.el.select('input.hidden-number-input',true).first();